cobra_commander 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7b56002a2501fd994a0d806833c2d4f10f3d8090b5f9983534c1fd2b36bf8e6
4
- data.tar.gz: 6b0dca74d6a0f9a91ee7a0d7704bfbc82710cbfa89e696184e87de1f623beb40
3
+ metadata.gz: 7155830962a165a9d34da676e053580b6a375543c0823cf89ce72db41b8fdc7e
4
+ data.tar.gz: db6fd674597394e4c5ee1f8e42b42d9e24305de678a9043f17ed026238f6edcc
5
5
  SHA512:
6
- metadata.gz: a321af237699e859f729bc241889bb95b83832f7d8e4d5191e09fc627b5101240dfa8ef8f96034ab7c0f3dd42f5b4518976c65953fc088b7e69410040a0ad422
7
- data.tar.gz: 7a447a00e0fb9d4b389d1f9296115f8b2e232cd72b5d3519d3868b0b69211ad92ce0fb0bb07921ba596e87796f657f56826a905ced034cee1eb708783170672e
6
+ metadata.gz: ee123f0d3b655a028e76e991fd3c7c0d411f9ece583965f9f4547a6356cce527049b41e90d665b18c433d5c8a293e43726c9a5b3fa822fb0a433d08b23df8f6e
7
+ data.tar.gz: 3f9514a4c74710643361ac3d3950e3f3d3b72e864b44c6f5addfc35997ae99a779bf07d5382cb38fa424a9d859de741d7836cd93e57624e55d044d8c0a0cfc0a
@@ -0,0 +1,11 @@
1
+ # EditorConfig is awesome: https://EditorConfig.org
2
+ # top-most EditorConfig file
3
+ root = true
4
+
5
+ # Unix-style newlines with a newline ending every file
6
+ [*]
7
+ end_of_line = lf
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+ indent_style = space
11
+ indent_size = 2
@@ -4,10 +4,9 @@ before_install:
4
4
  - "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
5
5
  - gem install bundler:"$BUNDLER_VERSION"
6
6
  - sudo apt-get -qq install graphviz
7
- - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0
7
+ - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0
8
8
  - export PATH="$HOME/.yarn/bin:$PATH"
9
9
  rvm:
10
10
  - 2.5.1
11
11
  env:
12
- - BUNDLER_VERSION=1.15.4
13
- - BUNDLER_VERSION=1.16.3
12
+ - BUNDLER_VERSION=1.17.3
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Version 0.7 - 2020-07-08
6
+
7
+ * Introduces CobraCommander::Umbrella with optimizations for dependency resolution
8
+ * Deprecates cobra cache and cache usages
9
+
5
10
  ## Version 0.6.1 - 2019-07-05
6
11
 
7
12
  * Better supports yarn workspaces globbing by delegating to yarn to calculate the list of components rather than re-implementing in Ruby. PR [#34](https://github.com/powerhome/cobra_commander/pull/34)
@@ -11,10 +11,12 @@ Gem::Specification.new do |spec|
11
11
  spec.authors = [
12
12
  "Ben Langfeld",
13
13
  "Garett Arrowood",
14
+ "Carlos Palhares",
14
15
  ]
15
16
  spec.email = [
16
17
  "blangfeld@powerhrg.com",
17
18
  "garett.arrowood@powerhrg.com",
19
+ "carlos.palhares@powerhrg.com",
18
20
  ]
19
21
  spec.summary = "Tools for working with Component Based Rails Apps"
20
22
  spec.description = <<~DESCRIPTION
@@ -35,7 +37,7 @@ DESCRIPTION
35
37
  spec.add_dependency "thor", ["< 2.0", ">= 0.18.1"]
36
38
  spec.add_dependency "ruby-graphviz", "~> 1.2.3"
37
39
 
38
- spec.add_development_dependency "bundler", "~> 1.13"
40
+ spec.add_development_dependency "bundler", "~> 1.17"
39
41
  spec.add_development_dependency "rake", "~> 10.0"
40
42
  spec.add_development_dependency "rspec", "~> 3.5"
41
43
  spec.add_development_dependency "guard-rspec"
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "cobra_commander/dependencies"
4
+ require "cobra_commander/component"
5
+ require "cobra_commander/umbrella"
6
+
3
7
  require "cobra_commander/cli"
4
8
  require "cobra_commander/cached_component_tree"
5
9
  require "cobra_commander/calculated_component_tree"
@@ -16,6 +20,13 @@ require "cobra_commander/executor"
16
20
  module CobraCommander
17
21
  UMBRELLA_APP_NAME = "App"
18
22
 
23
+ def self.umbrella(root_path, yarn: false, bundler: false, name: UMBRELLA_APP_NAME)
24
+ umbrella = Umbrella.new(name, root_path)
25
+ umbrella.add_source(:yarn, Dependencies::YarnWorkspace.new(root_path)) unless bundler
26
+ umbrella.add_source(:bundler, Dependencies::Bundler.new(root_path)) unless yarn
27
+ umbrella
28
+ end
29
+
19
30
  def self.umbrella_tree(path)
20
31
  CalculatedComponentTree.new(UMBRELLA_APP_NAME, path)
21
32
  end
@@ -3,28 +3,33 @@
3
3
  module CobraCommander
4
4
  # Calculates directly & transitively affected components
5
5
  class Affected
6
- attr_reader :directly, :transitively
7
-
8
- def initialize(tree, changes, path)
9
- @tree = tree
6
+ def initialize(umbrella, changes)
7
+ @umbrella = umbrella
10
8
  @changes = changes
11
- @path = path
12
9
  run!
13
10
  end
14
11
 
15
12
  def names
16
- @names ||= paths.map { |path| File.basename(path) }
13
+ @names ||= all_affected.map(&:name)
17
14
  end
18
15
 
19
16
  def scripts
20
17
  @scripts ||= paths.map { |path| File.join(path, "test.sh") }
21
18
  end
22
19
 
20
+ def directly
21
+ @directly.map(&method(:affected_component))
22
+ end
23
+
24
+ def transitively
25
+ @transitively.map(&method(:affected_component))
26
+ end
27
+
23
28
  def json_representation # rubocop:disable Metrics/MethodLength
24
29
  {
25
30
  changed_files: @changes,
26
- directly_affected_components: @directly,
27
- transitively_affected_components: @transitively,
31
+ directly_affected_components: directly,
32
+ transitively_affected_components: transitively,
28
33
  test_scripts: scripts,
29
34
  component_names: names,
30
35
  languages: {
@@ -39,46 +44,48 @@ module CobraCommander
39
44
  def run!
40
45
  @transitively = Set.new
41
46
  @directly = Set.new
42
- find_dependencies(@tree)
43
- @transitively.delete(name: UMBRELLA_APP_NAME, path: @path, type: @tree[:type])
44
- @transitively = @transitively.to_a.sort_by { |h| h[:name] }
45
- @directly = @directly.to_a.sort_by { |h| h[:name] }
47
+ @umbrella.components.each(&method(:add_if_changed))
48
+ @transitively = @transitively.sort_by(&:name)
49
+ @directly = @directly.sort_by(&:name)
46
50
  end
47
51
 
48
- def find_dependencies(parent_component)
49
- parent_component[:dependencies].each do |component|
50
- add_if_changed(component)
51
- find_dependencies(component)
52
- end
52
+ def add_if_changed(component)
53
+ return if component.root_paths.uniq.none? { |path| @changes.any?(Regexp.new(path)) }
54
+
55
+ @directly << component
56
+ @transitively.merge(component.deep_dependents)
53
57
  end
54
58
 
55
- def add_if_changed(component)
56
- @changes.each do |change|
57
- if change.start_with?(component[:path])
58
- @directly << component.reject { |k| k == :dependencies || k == :ancestry }
59
- @transitively.merge component[:ancestry]
60
- end
61
- end
59
+ def affected_component(component)
60
+ {
61
+ name: component.name,
62
+ path: component.root_paths,
63
+ type: component.sources.keys.map(&:to_s).map(&:capitalize).join(" & "),
64
+ }
62
65
  end
63
66
 
64
67
  def all_affected
65
- @all_affected ||= (@directly + @transitively).uniq.sort_by { |h| h[:path] }
68
+ @all_affected ||= (@directly | @transitively).sort_by(&:name)
66
69
  end
67
70
 
68
71
  def paths
69
- @paths ||= all_affected.map { |component| component[:path] }
72
+ @paths ||= all_affected.map(&:root_paths).flatten.uniq
70
73
  end
71
74
 
72
- def types
73
- @types ||= all_affected.map { |component| component[:type] }
75
+ def all_affected_sources
76
+ all_affected
77
+ .map(&:sources)
78
+ .map(&:keys)
79
+ .flatten
80
+ .uniq
74
81
  end
75
82
 
76
83
  def contains_ruby?
77
- types.uniq.join.include?("Ruby")
84
+ all_affected_sources.include?(:bundler)
78
85
  end
79
86
 
80
87
  def contains_js?
81
- types.uniq.join.include?("JS")
88
+ all_affected_sources.include?(:yarn)
82
89
  end
83
90
  end
84
91
  end
@@ -132,8 +132,8 @@ module CobraCommander
132
132
 
133
133
  def yarn_workspaces
134
134
  @yarn_workspaces ||= begin
135
- output, = Open3.capture2("yarn workspaces info --silent", chdir: @root_path)
136
- JSON.parse(output)
135
+ output, = Open3.capture2("yarn --json workspaces info", chdir: @root_path)
136
+ JSON.parse(JSON.parse(output)["data"])
137
137
  end
138
138
  end
139
139
  end
@@ -8,12 +8,12 @@ module CobraCommander
8
8
  class Change
9
9
  InvalidSelectionError = Class.new(StandardError)
10
10
 
11
- def initialize(tree, results, branch)
12
- @root_dir = Dir.chdir(tree.path) { `git rev-parse --show-toplevel`.chomp }
11
+ def initialize(umbrella, results, branch)
12
+ @root_dir = Dir.chdir(umbrella.path) { `git rev-parse --show-toplevel`.chomp }
13
13
  @results = results
14
14
  @branch = branch
15
- @tree = tree.to_h
16
- @affected = Affected.new(@tree, changes, tree.path)
15
+ @umbrella = umbrella
16
+ @affected = Affected.new(@umbrella, changes)
17
17
  end
18
18
 
19
19
  def run!
@@ -85,8 +85,8 @@ module CobraCommander
85
85
  end
86
86
  end
87
87
 
88
- def display(component)
89
- "#{component[:name]} - #{component[:type]}"
88
+ def display(name:, type:, **)
89
+ "#{name} - #{type}"
90
90
  end
91
91
 
92
92
  def blank_line
@@ -6,17 +6,16 @@ require "fileutils"
6
6
  module CobraCommander
7
7
  # Implements the tool's CLI
8
8
  class CLI < Thor
9
- CACHE_DESCRIPTION = "Path to a cache file to use (default: nil). If specified, this file will be used to store " \
10
- "the component tree for the app to speed up subsequent invocations. Must be rotated any time the component " \
11
- "dependency structure changes."
9
+ CACHE_DESCRIPTION = "[DEPRECATED] Path to a cache file to use (default: nil). If specified, this file will " \
10
+ "be used to store the component tree for the app to speed up subsequent invocations. Must be rotated any time " \
11
+ "the component dependency structure changes."
12
12
  COMMON_OPTIONS = "[--app=pwd] [--format=FORMAT] [--cache=nil]"
13
13
 
14
14
  desc "do [command] [--app=pwd] [--cache=nil]", "Executes the command in the context of each component in [app]"
15
15
  method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
16
16
  method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
17
17
  def do(command)
18
- tree = maybe_cached_tree(options.app, options.cache)
19
- executor = Executor.new(tree)
18
+ executor = Executor.new(umbrella(options.app).components)
20
19
  executor.exec(command)
21
20
  end
22
21
 
@@ -24,10 +23,9 @@ module CobraCommander
24
23
  method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
25
24
  method_option :format, default: "tree", aliases: "-f", desc: "Format (list or tree, default: list)"
26
25
  method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
27
- def ls(app_path = nil)
28
- tree = maybe_cached_tree(app_path || options.app, options.cache)
26
+ def ls(app_path = Dir.pwd)
29
27
  Output.print(
30
- tree,
28
+ umbrella(app_path).root,
31
29
  options.format
32
30
  )
33
31
  end
@@ -37,8 +35,8 @@ module CobraCommander
37
35
  method_option :format, default: "count", aliases: "-f", desc: "count or list"
38
36
  method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
39
37
  def dependents_of(component)
40
- tree = maybe_cached_tree(options.app, options.cache)
41
- dependents = tree.dependents_of(component)
38
+ dependents = umbrella(options.app).dependents_of(component)
39
+ return unless dependents
42
40
  puts "list" == options.format ? dependents.map(&:name) : dependents.size
43
41
  end
44
42
 
@@ -47,9 +45,8 @@ module CobraCommander
47
45
  method_option :format, default: "list", aliases: "-f", desc: "Format (list or tree, default: list)"
48
46
  method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
49
47
  def dependencies_of(component)
50
- tree = maybe_cached_tree(options.app, options.cache)
51
48
  Output.print(
52
- tree.subtree(component),
49
+ umbrella(options.app).find(component),
53
50
  options.format
54
51
  )
55
52
  end
@@ -59,12 +56,11 @@ module CobraCommander
59
56
  puts CobraCommander::VERSION
60
57
  end
61
58
 
62
- desc "graph APP_PATH [--format=FORMAT] [--cache=nil]", "Outputs graph"
59
+ desc "graph APP_PATH [--format=FORMAT] [--cache=nil] [--component]", "Outputs graph"
63
60
  method_option :format, default: "png", aliases: "-f", desc: "Accepts png or dot"
64
61
  method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
65
62
  def graph(app_path)
66
- tree = maybe_cached_tree(app_path, options.cache)
67
- Graph.new(tree, options.format).generate!
63
+ Graph.new(umbrella(app_path).root, options.format).generate!
68
64
  end
69
65
 
70
66
  desc "changes APP_PATH [--results=RESULTS] [--branch=BRANCH] [--cache=nil]", "Prints list of changed files"
@@ -72,34 +68,21 @@ module CobraCommander
72
68
  method_option :branch, default: "master", aliases: "-b", desc: "Specified target to calculate against"
73
69
  method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
74
70
  def changes(app_path)
75
- tree = maybe_cached_tree(app_path, options.cache)
76
- Change.new(tree, options.results, options.branch).run!
71
+ Change.new(umbrella(app_path), options.results, options.branch).run!
77
72
  end
78
73
 
79
- desc "cache APP_PATH CACHE_PATH", "Caches a representation of the component structure of the app"
74
+ desc "cache APP_PATH CACHE_PATH", "[DEPRECATED] Caches a representation of the component structure of the app"
80
75
  def cache(app_path, cache_path)
81
76
  tree = CobraCommander.umbrella_tree(app_path)
82
- write_tree_cache(tree, cache_path)
77
+ FileUtils.mkdir_p(File.dirname(cache_path))
78
+ File.write(cache_path, tree.to_json)
83
79
  puts "Created cache of component tree at #{cache_path}"
84
80
  end
85
81
 
86
82
  private
87
83
 
88
- def maybe_cached_tree(app_path, cache_path)
89
- return CobraCommander.umbrella_tree(app_path) unless cache_path
90
-
91
- if File.exist?(cache_path)
92
- CobraCommander.tree_from_cache(cache_path)
93
- else
94
- tree = CobraCommander.umbrella_tree(app_path)
95
- write_tree_cache(tree, cache_path)
96
- tree
97
- end
98
- end
99
-
100
- def write_tree_cache(tree, cache_path)
101
- FileUtils.mkdir_p(File.dirname(cache_path))
102
- File.write(cache_path, tree.to_json)
84
+ def umbrella(path)
85
+ CobraCommander.umbrella(path)
103
86
  end
104
87
  end
105
88
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ # Represents a component withing an Umbrella
5
+ class Component
6
+ attr_reader :name, :dependencies, :sources
7
+
8
+ def initialize(umbrella, name)
9
+ @umbrella = umbrella
10
+ @name = name
11
+ @dependency_names = []
12
+ @sources = {}
13
+ end
14
+
15
+ def add_source(key, path, dependency_names)
16
+ @sources[key] = path
17
+ @dependency_names |= dependency_names
18
+ end
19
+
20
+ def root_paths
21
+ @sources.values.map(&File.method(:dirname)).uniq
22
+ end
23
+
24
+ def inspect
25
+ "#<CobraCommander::Component:#{object_id} #{name} dependencies=#{dependencies.map(&:name)} packages=#{sources}>"
26
+ end
27
+
28
+ def deep_dependents
29
+ @deep_dependents ||= @umbrella.components.find_all do |dep|
30
+ dep.deep_dependencies.include?(self)
31
+ end
32
+ end
33
+
34
+ def deep_dependencies
35
+ @deep_dependencies ||= dependencies.reduce(dependencies) do |deps, dep|
36
+ deps | dep.deep_dependencies
37
+ end
38
+ end
39
+
40
+ def dependents
41
+ @dependents ||= @umbrella.components.find_all do |dep|
42
+ dep.dependencies.include?(self)
43
+ end
44
+ end
45
+
46
+ def dependencies
47
+ @dependencies ||= @dependency_names.sort
48
+ .map(&@umbrella.method(:find))
49
+ .compact
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dependencies/yarn_workspace"
4
+ require_relative "dependencies/bundler"
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ module Dependencies
5
+ # Calculates ruby bundler dependencies
6
+ class Bundler
7
+ def initialize(root)
8
+ @definition = ::Bundler::Definition.build(
9
+ Pathname.new(File.join(root, "Gemfile")).realpath,
10
+ Pathname.new(File.join(root, "Gemfile.lock")).realpath,
11
+ false
12
+ )
13
+ end
14
+
15
+ def path
16
+ @definition.lockfile
17
+ end
18
+
19
+ def dependencies
20
+ @definition.dependencies.map(&:name)
21
+ end
22
+
23
+ def components
24
+ components_source.specs.map do |spec|
25
+ { path: spec.loaded_from, name: spec.name, dependencies: spec.dependencies.map(&:name) }
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def components_source
32
+ @components_source ||= @definition.send(:sources).path_sources.find do |source|
33
+ source.path.to_s.eql?("components")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module CobraCommander
6
+ module Dependencies
7
+ module Yarn
8
+ # Represents an Yarn package.json file
9
+ class Package
10
+ attr_reader :path
11
+
12
+ def initialize(path)
13
+ @path = Pathname.new(File.join(path, "package.json")).realpath
14
+ end
15
+
16
+ def project_tag
17
+ name.match(%r{^@[\w-]+\/}).to_s
18
+ end
19
+
20
+ def name
21
+ json["name"]
22
+ end
23
+
24
+ def dependencies
25
+ json.fetch("dependencies", {})
26
+ .merge(json.fetch("devDependencies", {}))
27
+ end
28
+
29
+ private
30
+
31
+ def json
32
+ @json ||= JSON.parse(File.read(@path))
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ module Dependencies
5
+ module Yarn
6
+ # Yarn package repository to load and cache package.json files
7
+ class PackageRepo
8
+ def initialize
9
+ @specs ||= {}
10
+ end
11
+
12
+ def specs
13
+ @specs.values
14
+ end
15
+
16
+ def load_linked_specs(package)
17
+ package.dependencies.values.each do |spec|
18
+ next unless spec =~ /link:(.+)/
19
+ load_spec(File.join(package.path, "..", Regexp.last_match(1)))
20
+ end
21
+ end
22
+
23
+ def load_spec(path)
24
+ @specs[path] ||= Package.new(path).tap do |package|
25
+ load_linked_specs(package)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ require_relative "yarn/package"
6
+ require_relative "yarn/package_repo"
7
+
8
+ module CobraCommander
9
+ module Dependencies
10
+ # Yarn workspace components source for an umbrella
11
+ class YarnWorkspace
12
+ attr_reader :packages
13
+
14
+ def initialize(root_path)
15
+ @repo = Yarn::PackageRepo.new
16
+ @root_package = Yarn::Package.new(root_path)
17
+ @repo.load_linked_specs(@root_package)
18
+ load_workspace_packages
19
+ end
20
+
21
+ def path
22
+ @root_package.path
23
+ end
24
+
25
+ def dependencies
26
+ (workspace_spec.keys | @root_package.dependencies.keys).map(&method(:untag))
27
+ end
28
+
29
+ def components
30
+ @repo.specs.map do |spec|
31
+ { path: spec.path, name: untag(spec.name), dependencies: spec.dependencies.keys.map(&method(:untag)) }
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def load_workspace_packages
38
+ workspace_spec.map do |_name, spec|
39
+ @repo.load_spec File.expand_path(File.join(@root_package.path, "..", spec["location"]))
40
+ end
41
+ end
42
+
43
+ def workspace_spec
44
+ @workspace_spec ||= begin
45
+ output, = Open3.capture2("yarn workspaces --json info", chdir: File.dirname(@root_package.path))
46
+ JSON.parse(JSON.parse(output)["data"])
47
+ end
48
+ end
49
+
50
+ def untag(name)
51
+ name.gsub(@root_package.project_tag, "")
52
+ end
53
+ end
54
+ end
55
+ end
@@ -3,28 +3,18 @@
3
3
  module CobraCommander
4
4
  # Execute commands on all components of a ComponentTree
5
5
  class Executor
6
- def initialize(tree)
7
- @tree = tree
6
+ def initialize(components)
7
+ @components = components
8
8
  end
9
9
 
10
10
  def exec(command, printer = $stdout)
11
- @tree.flatten.each do |component|
12
- printer.puts "===> #{component.name} (#{component.path})"
13
- output, = run_in_component(component, command)
14
- printer.puts output
11
+ @components.each do |component|
12
+ component.root_paths.each do |path|
13
+ printer.puts "===> #{component.name} (#{path})"
14
+ output, = Open3.capture2e(command, chdir: path, unsetenv_others: true)
15
+ printer.puts output
16
+ end
15
17
  end
16
18
  end
17
-
18
- private
19
-
20
- def run_in_component(component, command)
21
- Dir.chdir(component.path) do
22
- Open3.capture2e(env_vars(component), command)
23
- end
24
- end
25
-
26
- def env_vars(component)
27
- { "CURRENT_COMPONENT" => component.name, "CURRENT_COMPONENT_PATH" => component.path }
28
- end
29
19
  end
30
20
  end
@@ -5,31 +5,23 @@ require "graphviz"
5
5
  module CobraCommander
6
6
  # Generates graphs of components
7
7
  class Graph
8
- def initialize(tree, format)
8
+ def initialize(node, format)
9
9
  @format = format
10
- @tree = tree.to_h
10
+ @node = node
11
11
  end
12
12
 
13
13
  def generate!
14
14
  return unless valid_format?
15
15
 
16
16
  g = GraphViz.new(:G, type: :digraph, concentrate: true)
17
+ ([@node] + @node.deep_dependencies).each do |component|
18
+ g.add_nodes component.name
19
+ g.add_edges component.name, component.dependencies.map(&:name)
20
+ end
17
21
 
18
- app_node = g.add_nodes(@tree[:name])
19
- map_nodes(g, app_node, @tree)
20
22
  output(g)
21
23
  end
22
24
 
23
- private
24
-
25
- def map_nodes(g, parent_node, tree)
26
- tree[:dependencies].each do |dep|
27
- node = g.find_node(dep[:name]) || g.add_nodes(dep[:name])
28
- g.add_edges(parent_node, node)
29
- map_nodes(g, node, dep)
30
- end
31
- end
32
-
33
25
  def output(g)
34
26
  graph = "graph.#{@format}"
35
27
  g.output(@format => graph)
@@ -3,8 +3,8 @@
3
3
  module CobraCommander
4
4
  # Module for pretty printing dependency trees
5
5
  module Output
6
- def self.print(tree, format)
7
- output = format == "list" ? Output::FlatList.new(tree) : Output::Tree.new(tree)
6
+ def self.print(node, format)
7
+ output = format == "list" ? Output::FlatList.new(node) : Output::Tree.new(node)
8
8
  puts output.to_s
9
9
  end
10
10
 
@@ -15,7 +15,7 @@ module CobraCommander
15
15
  end
16
16
 
17
17
  def to_s
18
- @tree.flatten.map(&:name)
18
+ @tree.deep_dependencies.map(&:name).sort
19
19
  end
20
20
  end
21
21
 
@@ -28,22 +28,22 @@ module CobraCommander
28
28
  TEE = "├── "
29
29
  CORNER = "└── "
30
30
 
31
- def initialize(tree)
32
- @tree = tree
31
+ def initialize(node)
32
+ @node = node
33
33
  end
34
34
 
35
35
  def to_s
36
36
  StringIO.new.tap do |io|
37
- io.puts @tree.name
38
- list_dependencies(io, @tree)
37
+ io.puts @node.name
38
+ list_dependencies(io, @node)
39
39
  end.string
40
40
  end
41
41
 
42
42
  private
43
43
 
44
- def list_dependencies(io, deps, outdents = [])
45
- deps.dependencies.each do |dep|
46
- decide_on_line(io, deps, dep, outdents)
44
+ def list_dependencies(io, node, outdents = [])
45
+ node.dependencies.each do |dep|
46
+ decide_on_line(io, node, dep, outdents)
47
47
  end
48
48
  nil
49
49
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ # An umbrella application
5
+ class Umbrella
6
+ attr_reader :name, :path
7
+
8
+ def initialize(name, path)
9
+ @root_component = Component.new(self, name)
10
+ @path = path
11
+ @components = {}
12
+ end
13
+
14
+ def find(name)
15
+ @components[name]
16
+ end
17
+
18
+ def root
19
+ @root_component
20
+ end
21
+
22
+ def resolve(component_root_path)
23
+ return root if root.root_paths.include?(component_root_path)
24
+ components.find do |component|
25
+ component.root_paths.include?(component_root_path)
26
+ end
27
+ end
28
+
29
+ def add_source(key, source)
30
+ @root_component.add_source key, source.path, source.dependencies
31
+ source.components.each do |path:, name:, dependencies:|
32
+ @components[name] ||= Component.new(self, name)
33
+ @components[name].add_source key, path, dependencies
34
+ end
35
+ end
36
+
37
+ def components
38
+ @components.values
39
+ end
40
+
41
+ def dependents_of(component)
42
+ find(component)&.deep_dependents
43
+ &.sort_by(&:name)
44
+ end
45
+
46
+ def dependencies_of(name)
47
+ find(name)&.deep_dependencies
48
+ &.sort_by(&:name)
49
+ end
50
+ end
51
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CobraCommander
4
- VERSION = "0.6.1"
4
+ VERSION = "0.7.0"
5
5
  end
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": [
3
+ "config:base"
4
+ ],
5
+ "ignorePaths": [
6
+ "spec/fixtures"
7
+ ]
8
+ }
metadata CHANGED
@@ -1,36 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cobra_commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
8
8
  - Garett Arrowood
9
+ - Carlos Palhares
9
10
  autorequire:
10
11
  bindir: exe
11
12
  cert_chain: []
12
- date: 2019-07-05 00:00:00.000000000 Z
13
+ date: 2020-07-08 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: thor
16
17
  requirement: !ruby/object:Gem::Requirement
17
18
  requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 0.18.1
21
19
  - - "<"
22
20
  - !ruby/object:Gem::Version
23
21
  version: '2.0'
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.18.1
24
25
  type: :runtime
25
26
  prerelease: false
26
27
  version_requirements: !ruby/object:Gem::Requirement
27
28
  requirements:
28
- - - ">="
29
- - !ruby/object:Gem::Version
30
- version: 0.18.1
31
29
  - - "<"
32
30
  - !ruby/object:Gem::Version
33
31
  version: '2.0'
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.18.1
34
35
  - !ruby/object:Gem::Dependency
35
36
  name: ruby-graphviz
36
37
  requirement: !ruby/object:Gem::Requirement
@@ -51,14 +52,14 @@ dependencies:
51
52
  requirements:
52
53
  - - "~>"
53
54
  - !ruby/object:Gem::Version
54
- version: '1.13'
55
+ version: '1.17'
55
56
  type: :development
56
57
  prerelease: false
57
58
  version_requirements: !ruby/object:Gem::Requirement
58
59
  requirements:
59
60
  - - "~>"
60
61
  - !ruby/object:Gem::Version
61
- version: '1.13'
62
+ version: '1.17'
62
63
  - !ruby/object:Gem::Dependency
63
64
  name: rake
64
65
  requirement: !ruby/object:Gem::Requirement
@@ -150,11 +151,13 @@ description: |
150
151
  email:
151
152
  - blangfeld@powerhrg.com
152
153
  - garett.arrowood@powerhrg.com
154
+ - carlos.palhares@powerhrg.com
153
155
  executables:
154
156
  - cobra
155
157
  extensions: []
156
158
  extra_rdoc_files: []
157
159
  files:
160
+ - ".editorconfig"
158
161
  - ".gitignore"
159
162
  - ".rspec"
160
163
  - ".rubocop.yml"
@@ -177,11 +180,19 @@ files:
177
180
  - lib/cobra_commander/calculated_component_tree.rb
178
181
  - lib/cobra_commander/change.rb
179
182
  - lib/cobra_commander/cli.rb
183
+ - lib/cobra_commander/component.rb
180
184
  - lib/cobra_commander/component_tree.rb
185
+ - lib/cobra_commander/dependencies.rb
186
+ - lib/cobra_commander/dependencies/bundler.rb
187
+ - lib/cobra_commander/dependencies/yarn/package.rb
188
+ - lib/cobra_commander/dependencies/yarn/package_repo.rb
189
+ - lib/cobra_commander/dependencies/yarn_workspace.rb
181
190
  - lib/cobra_commander/executor.rb
182
191
  - lib/cobra_commander/graph.rb
183
192
  - lib/cobra_commander/output.rb
193
+ - lib/cobra_commander/umbrella.rb
184
194
  - lib/cobra_commander/version.rb
195
+ - renovate.json
185
196
  homepage: http://tech.powerhrg.com/cobra_commander/
186
197
  licenses:
187
198
  - MIT
@@ -201,7 +212,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
212
  - !ruby/object:Gem::Version
202
213
  version: '0'
203
214
  requirements: []
204
- rubygems_version: 3.0.4
215
+ rubyforge_project:
216
+ rubygems_version: 2.7.3
205
217
  signing_key:
206
218
  specification_version: 4
207
219
  summary: Tools for working with Component Based Rails Apps