cobra_commander 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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