cobra_commander 0.7.0 → 0.8.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: 7155830962a165a9d34da676e053580b6a375543c0823cf89ce72db41b8fdc7e
4
- data.tar.gz: db6fd674597394e4c5ee1f8e42b42d9e24305de678a9043f17ed026238f6edcc
3
+ metadata.gz: 6f1de2b5ceb4fbde7dcd12fd71606c55cdd733e6928e4c6a866dfc1f3ec65af3
4
+ data.tar.gz: 5a9537dabd13b974df919be911755debf92761b6bdf99b370c59cec2c8fc09af
5
5
  SHA512:
6
- metadata.gz: ee123f0d3b655a028e76e991fd3c7c0d411f9ece583965f9f4547a6356cce527049b41e90d665b18c433d5c8a293e43726c9a5b3fa822fb0a433d08b23df8f6e
7
- data.tar.gz: 3f9514a4c74710643361ac3d3950e3f3d3b72e864b44c6f5addfc35997ae99a779bf07d5382cb38fa424a9d859de741d7836cd93e57624e55d044d8c0a0cfc0a
6
+ metadata.gz: 58f066894063d46d3b1c25baf2b085f3212db9ac716eee5c8ef6c2465c82233f06fae79e30c04205d95b75551799cd3f1c67901a60a516bf079e2cb154baa2b2
7
+ data.tar.gz: 99babd4c1ae0b5e218ed913bf175cbd38d4efe4bc19d11ea108d8f914c9f3381a7f43bb6c8a4d64171bec648e5c5e8441c39c30f5c3a83f11e0c9f1aa4e0948b
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Version 0.8 - 2020-07-21
6
+
7
+ * Standardize Cobra CLI options
8
+ * More powerful filters to all cobra commands (--js, --ruby)
9
+ * More powerful filters to cobra ls and exec (--dependencies, --dependents)
10
+ * Graphs for components, not just umbrella
11
+
5
12
  ## Version 0.7 - 2020-07-08
6
13
 
7
14
  * Introduces CobraCommander::Umbrella with optimizations for dependency resolution
data/README.md CHANGED
@@ -24,19 +24,110 @@ Or install it yourself as:
24
24
 
25
25
  $ gem install cobra_commander
26
26
 
27
- ## Usage
27
+ ## Usage (cobra help)
28
28
 
29
29
  ```bash
30
30
  Commands:
31
- cobra cache APP_PATH CACHE_PATH # Caches a representation of the component structure of the app
32
- cobra changes APP_PATH [--results=RESULTS] [--branch=BRANCH] [--cache=nil] # Prints list of changed files
33
- cobra dependencies_of [component] [--app=pwd] [--format=FORMAT] [--cache=nil] # Outputs a list of components that [component] depends on within [app] context
34
- cobra dependents_of [component] [--app=pwd] [--format=FORMAT] [--cache=nil] # Outputs count of components in [app] dependent on [component]
35
- cobra do [command] [--app=pwd] [--cache=nil] # Executes the command in the context of each component in [app]
36
- cobra graph APP_PATH [--format=FORMAT] [--cache=nil] # Outputs graph
37
- cobra help [COMMAND] # Describe available commands or one specific command
38
- cobra ls [app_path] [--app=pwd] [--format=FORMAT] [--cache=nil] # Prints tree of components for an app
39
- cobra version # Prints version
31
+ cobra changes [--results=RESULTS] [--branch=BRANCH] # Prints list of changed files
32
+ cobra exec [component] <command> # Executes the command in the context of a given component or set of components. If no component is given executes the command in all components.
33
+ cobra graph [component] # Outputs a graph of a given component or umbrella
34
+ cobra help [COMMAND] # Describe available commands or one specific command
35
+ cobra ls [component] # Lists the components in the context of a given component or umbrella
36
+ cobra tree [component] # Prints the dependency tree of a given component or umbrella
37
+ cobra version # Prints version
38
+
39
+ Options:
40
+ -a, [--app=APP]
41
+ # Default: /Users/chjunior/workspace/power/cobra_commander
42
+ [--js], [--no-js] # Consider only the JS dependency graph
43
+ [--ruby], [--no-ruby] # Consider only the Ruby dependency graph
44
+ ```
45
+
46
+ ### cobra changes
47
+
48
+ ```sh
49
+ ➜ nitro git:(nova/remove-cobra-commander) be cobra help changes
50
+ Usage:
51
+ cobra changes [--results=RESULTS] [--branch=BRANCH]
52
+
53
+ Options:
54
+ -r, [--results=RESULTS] # Accepts test, full, name or json
55
+ # Default: test
56
+ -b, [--branch=BRANCH] # Specified target to calculate against
57
+ # Default: master
58
+ -a, [--app=APP]
59
+ # Default: /Users/chjunior/workspace/power/nitro
60
+ [--js], [--no-js] # Consider only the JS dependency graph
61
+ [--ruby], [--no-ruby] # Consider only the Ruby dependency graph
62
+
63
+ Prints list of changed files
64
+ ```
65
+
66
+ ### cobra exec
67
+
68
+ ```sh
69
+ Usage:
70
+ cobra exec [component] <command>
71
+
72
+ Options:
73
+ [--dependencies], [--no-dependencies] # Run the command on each dependency of a given component
74
+ [--dependents], [--no-dependents] # Run the command on each dependency of a given component
75
+ -a, [--app=APP]
76
+ # Default: /Users/chjunior/workspace/power/cobra_commander
77
+ [--js], [--no-js] # Consider only the JS dependency graph
78
+ [--ruby], [--no-ruby] # Consider only the Ruby dependency graph
79
+
80
+ Executes the command in the context of a given component or set of components. If no component is given executes the command in all components.
81
+ ```
82
+
83
+ ### cobra graph
84
+
85
+ ```sh
86
+ Usage:
87
+ cobra graph [component]
88
+
89
+ Options:
90
+ -o, [--output=OUTPUT] # Output file, accepts .png or .dot
91
+ # Default: /Users/chjunior/workspace/power/cobra_commander/output.png
92
+ -a, [--app=APP]
93
+ # Default: /Users/chjunior/workspace/power/cobra_commander
94
+ [--js], [--no-js] # Consider only the JS dependency graph
95
+ [--ruby], [--no-ruby] # Consider only the Ruby dependency graph
96
+
97
+ Outputs a graph of a given component or umbrella
98
+ ```
99
+
100
+ ### cobra ls
101
+
102
+ ```sh
103
+ Usage:
104
+ cobra ls [component]
105
+
106
+ Options:
107
+ -d, [--dependencies], [--no-dependencies] # Run the command on each dependency of a given component
108
+ -D, [--dependents], [--no-dependents] # Run the command on each dependency of a given component
109
+ -t, [--total], [--no-total] # Prints the total count of components
110
+ -a, [--app=APP]
111
+ # Default: /Users/chjunior/workspace/power/cobra_commander
112
+ [--js], [--no-js] # Consider only the JS dependency graph
113
+ [--ruby], [--no-ruby] # Consider only the Ruby dependency graph
114
+
115
+ Lists the components in the context of a given component or umbrella
116
+ ```
117
+
118
+ ### cobra tree
119
+
120
+ ```sh
121
+ Usage:
122
+ cobra tree [component]
123
+
124
+ Options:
125
+ -a, [--app=APP]
126
+ # Default: /Users/chjunior/workspace/power/cobra_commander
127
+ [--js], [--no-js] # Consider only the JS dependency graph
128
+ [--ruby], [--no-ruby] # Consider only the Ruby dependency graph
129
+
130
+ Prints the dependency tree of a given component or umbrella
40
131
  ```
41
132
 
42
133
  ## Development
@@ -34,6 +34,7 @@ DESCRIPTION
34
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
35
  spec.require_paths = ["lib"]
36
36
 
37
+ spec.add_dependency "bundler", "~> 1.17"
37
38
  spec.add_dependency "thor", ["< 2.0", ">= 0.18.1"]
38
39
  spec.add_dependency "ruby-graphviz", "~> 1.2.3"
39
40
 
data/exe/cobra CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "cobra_commander"
4
+ require "cobra_commander/cli"
5
5
 
6
6
  CobraCommander::CLI.start
@@ -3,16 +3,7 @@
3
3
  require "cobra_commander/dependencies"
4
4
  require "cobra_commander/component"
5
5
  require "cobra_commander/umbrella"
6
-
7
- require "cobra_commander/cli"
8
- require "cobra_commander/cached_component_tree"
9
- require "cobra_commander/calculated_component_tree"
10
6
  require "cobra_commander/version"
11
- require "cobra_commander/graph"
12
- require "cobra_commander/change"
13
- require "cobra_commander/affected"
14
- require "cobra_commander/output"
15
- require "cobra_commander/executor"
16
7
 
17
8
  # Tools for working with Component Based Rails Apps (see http://shageman.github.io/cbra.info/).
18
9
  # Includes tools for graphing the components of an app and their relationships, as well as selectively
@@ -3,86 +3,91 @@
3
3
  require "thor"
4
4
  require "fileutils"
5
5
 
6
+ require "cobra_commander"
7
+ require "cobra_commander/affected"
8
+ require "cobra_commander/change"
9
+ require "cobra_commander/executor"
10
+ require "cobra_commander/output"
11
+
6
12
  module CobraCommander
7
13
  # Implements the tool's CLI
8
14
  class CLI < Thor
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
- COMMON_OPTIONS = "[--app=pwd] [--format=FORMAT] [--cache=nil]"
13
-
14
- desc "do [command] [--app=pwd] [--cache=nil]", "Executes the command in the context of each component in [app]"
15
- method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
16
- method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
17
- def do(command)
18
- executor = Executor.new(umbrella(options.app).components)
19
- executor.exec(command)
20
- end
15
+ class_option :app, default: Dir.pwd, aliases: "-a", type: :string
16
+ class_option :js, default: false, type: :boolean, desc: "Consider only the JS dependency graph"
17
+ class_option :ruby, default: false, type: :boolean, desc: "Consider only the Ruby dependency graph"
21
18
 
22
- desc "ls [app_path] #{COMMON_OPTIONS}", "Prints tree of components for an app"
23
- method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
24
- method_option :format, default: "tree", aliases: "-f", desc: "Format (list or tree, default: list)"
25
- method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
26
- def ls(app_path = Dir.pwd)
27
- Output.print(
28
- umbrella(app_path).root,
29
- options.format
30
- )
19
+ desc "version", "Prints version"
20
+ def version
21
+ puts CobraCommander::VERSION
31
22
  end
32
23
 
33
- desc "dependents_of [component] #{COMMON_OPTIONS}", "Outputs count of components in [app] dependent on [component]"
34
- method_option :app, default: Dir.pwd, aliases: "-a", desc: "Path to the root app where the component is mounted"
35
- method_option :format, default: "count", aliases: "-f", desc: "count or list"
36
- method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
37
- def dependents_of(component)
38
- dependents = umbrella(options.app).dependents_of(component)
39
- return unless dependents
40
- puts "list" == options.format ? dependents.map(&:name) : dependents.size
24
+ desc "ls [component]", "Lists the components in the context of a given component or umbrella"
25
+ method_option :dependencies, type: :boolean, aliases: "-d",
26
+ desc: "Run the command on each dependency of a given component"
27
+ method_option :dependents, type: :boolean, aliases: "-D",
28
+ desc: "Run the command on each dependency of a given component"
29
+ method_option :total, type: :boolean, aliases: "-t", desc: "Prints the total count of components"
30
+ def ls(component = nil)
31
+ components = components_filtered(component)
32
+ puts options.total ? components.size : CobraCommander::Output::FlatList.new(components).to_s
41
33
  end
42
34
 
43
- desc "dependencies_of [component] #{COMMON_OPTIONS}", "Outputs a list of components that [component] depends on"
44
- method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
45
- method_option :format, default: "list", aliases: "-f", desc: "Format (list or tree, default: list)"
46
- method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
47
- def dependencies_of(component)
48
- Output.print(
49
- umbrella(options.app).find(component),
50
- options.format
35
+ desc "exec [component] <command>", "Executes the command in the context of a given component or set of components. " \
36
+ "If no component is given executes the command in all components."
37
+ method_option :dependencies, type: :boolean, desc: "Run the command on each dependency of a given component"
38
+ method_option :dependents, type: :boolean, desc: "Run the command on each dependency of a given component"
39
+ def exec(command_or_component, command = nil)
40
+ CobraCommander::Executor.exec(
41
+ components_filtered(command && command_or_component),
42
+ command ? command : command_or_component
51
43
  )
52
44
  end
53
45
 
54
- desc "version", "Prints version"
55
- def version
56
- puts CobraCommander::VERSION
46
+ desc "tree [component]", "Prints the dependency tree of a given component or umbrella"
47
+ def tree(component = nil)
48
+ component = find_component(component)
49
+ puts CobraCommander::Output::AsciiTree.new(component).to_s
57
50
  end
58
51
 
59
- desc "graph APP_PATH [--format=FORMAT] [--cache=nil] [--component]", "Outputs graph"
60
- method_option :format, default: "png", aliases: "-f", desc: "Accepts png or dot"
61
- method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
62
- def graph(app_path)
63
- Graph.new(umbrella(app_path).root, options.format).generate!
52
+ desc "graph [component]", "Outputs a graph of a given component or umbrella"
53
+ method_option :output, default: File.join(Dir.pwd, "output.png"), aliases: "-o",
54
+ desc: "Output file, accepts .png or .dot"
55
+ def graph(component = nil)
56
+ CobraCommander::Output::GraphViz.generate(
57
+ find_component(component),
58
+ options.output
59
+ )
60
+ puts "Graph generated at #{options.output}"
61
+ rescue ArgumentError => error
62
+ error error.message
64
63
  end
65
64
 
66
- desc "changes APP_PATH [--results=RESULTS] [--branch=BRANCH] [--cache=nil]", "Prints list of changed files"
65
+ desc "changes [--results=RESULTS] [--branch=BRANCH]", "Prints list of changed files"
67
66
  method_option :results, default: "test", aliases: "-r", desc: "Accepts test, full, name or json"
68
67
  method_option :branch, default: "master", aliases: "-b", desc: "Specified target to calculate against"
69
- method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
70
- def changes(app_path)
71
- Change.new(umbrella(app_path), options.results, options.branch).run!
68
+ def changes
69
+ Change.new(umbrella, options.results, options.branch).run!
70
+ end
71
+
72
+ private
73
+
74
+ def umbrella
75
+ @umbrella ||= CobraCommander.umbrella(options.app, yarn: options.js, bundler: options.ruby)
72
76
  end
73
77
 
74
- desc "cache APP_PATH CACHE_PATH", "[DEPRECATED] Caches a representation of the component structure of the app"
75
- def cache(app_path, cache_path)
76
- tree = CobraCommander.umbrella_tree(app_path)
77
- FileUtils.mkdir_p(File.dirname(cache_path))
78
- File.write(cache_path, tree.to_json)
79
- puts "Created cache of component tree at #{cache_path}"
78
+ def find_component(name)
79
+ return umbrella.root unless name
80
+
81
+ umbrella.find(name) || error("Component #{name} not found, try one of `cobra ls`") || exit(1)
80
82
  end
81
83
 
82
- private
84
+ def components_filtered(component_name)
85
+ return umbrella.components unless component_name
86
+ component = find_component(component_name)
83
87
 
84
- def umbrella(path)
85
- CobraCommander.umbrella(path)
88
+ return component.deep_dependencies if options.dependencies
89
+ return component.deep_dependents if options.dependents
90
+ [component]
86
91
  end
87
92
  end
88
93
  end
@@ -2,13 +2,9 @@
2
2
 
3
3
  module CobraCommander
4
4
  # Execute commands on all components of a ComponentTree
5
- class Executor
6
- def initialize(components)
7
- @components = components
8
- end
9
-
10
- def exec(command, printer = $stdout)
11
- @components.each do |component|
5
+ module Executor
6
+ def self.exec(components, command, printer = $stdout)
7
+ components.each do |component|
12
8
  component.root_paths.each do |path|
13
9
  printer.puts "===> #{component.name} (#{path})"
14
10
  output, = Open3.capture2e(command, chdir: path, unsetenv_others: true)
@@ -1,74 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CobraCommander
4
- # Module for pretty printing dependency trees
5
- module Output
6
- def self.print(node, format)
7
- output = format == "list" ? Output::FlatList.new(node) : Output::Tree.new(node)
8
- puts output.to_s
9
- end
10
-
11
- # Flattens a tree and prints unique items
12
- class FlatList
13
- def initialize(tree)
14
- @tree = tree
15
- end
16
-
17
- def to_s
18
- @tree.deep_dependencies.map(&:name).sort
19
- end
20
- end
21
-
22
- # Prints the tree in a nice tree form
23
- class Tree
24
- attr_accessor :tree
25
-
26
- SPACE = " "
27
- BAR = "│   "
28
- TEE = "├── "
29
- CORNER = "└── "
30
-
31
- def initialize(node)
32
- @node = node
33
- end
34
-
35
- def to_s
36
- StringIO.new.tap do |io|
37
- io.puts @node.name
38
- list_dependencies(io, @node)
39
- end.string
40
- end
41
-
42
- private
43
-
44
- def list_dependencies(io, node, outdents = [])
45
- node.dependencies.each do |dep|
46
- decide_on_line(io, node, dep, outdents)
47
- end
48
- nil
49
- end
50
-
51
- def decide_on_line(io, parent, dep, outdents)
52
- if parent.dependencies.last != dep
53
- add_tee(io, outdents, dep)
54
- else
55
- add_corner(io, outdents, dep)
56
- end
57
- end
58
-
59
- def add_tee(io, outdents, dep)
60
- io.puts line(outdents, TEE, dep.name)
61
- list_dependencies(io, dep, (outdents + [BAR]))
62
- end
63
-
64
- def add_corner(io, outdents, dep)
65
- io.puts line(outdents, CORNER, dep.name)
66
- list_dependencies(io, dep, (outdents + [SPACE]))
67
- end
68
-
69
- def line(outdents, sym, name)
70
- (outdents + [sym] + [name]).join
71
- end
72
- end
73
- end
74
- end
3
+ require_relative "output/flat_list"
4
+ require_relative "output/ascii_tree"
5
+ require_relative "output/graph_viz"
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ module Output
5
+ # Prints the tree in a nice tree form
6
+ class AsciiTree
7
+ SPACE = " "
8
+ BAR = "│   "
9
+ TEE = "├── "
10
+ CORNER = "└── "
11
+
12
+ def initialize(component)
13
+ @component = component
14
+ end
15
+
16
+ def to_s
17
+ StringIO.new.tap do |io|
18
+ io.puts @component.name
19
+ list_dependencies(io, @component)
20
+ end.string
21
+ end
22
+
23
+ private
24
+
25
+ def list_dependencies(io, component, outdents = [])
26
+ component.dependencies.each do |dep|
27
+ decide_on_line(io, component, dep, outdents)
28
+ end
29
+ nil
30
+ end
31
+
32
+ def decide_on_line(io, parent, dep, outdents)
33
+ if parent.dependencies.last != dep
34
+ add_tee(io, outdents, dep)
35
+ else
36
+ add_corner(io, outdents, dep)
37
+ end
38
+ end
39
+
40
+ def add_tee(io, outdents, dep)
41
+ io.puts line(outdents, TEE, dep.name)
42
+ list_dependencies(io, dep, (outdents + [BAR]))
43
+ end
44
+
45
+ def add_corner(io, outdents, dep)
46
+ io.puts line(outdents, CORNER, dep.name)
47
+ list_dependencies(io, dep, (outdents + [SPACE]))
48
+ end
49
+
50
+ def line(outdents, sym, name)
51
+ (outdents + [sym] + [name]).join
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ module Output
5
+ # Prints a list of components' names sorted alphabetically
6
+ class FlatList
7
+ def initialize(components)
8
+ @components = components
9
+ end
10
+
11
+ def to_s
12
+ @components.map(&:name).sort
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphviz"
4
+
5
+ module CobraCommander
6
+ module Output
7
+ # Generates graphs of components
8
+ module GraphViz
9
+ def self.generate(component, output)
10
+ g = ::GraphViz.new(:G, type: :digraph, concentrate: true)
11
+ ([component] + component.deep_dependencies).each do |comp|
12
+ g.add_nodes comp.name
13
+ g.add_edges comp.name, comp.dependencies.map(&:name)
14
+ end
15
+
16
+ g.output(extract_format(output) => output)
17
+ end
18
+
19
+ private_class_method def self.extract_format(output)
20
+ format = output[-3..-1]
21
+ return format if format == "png" || format == "dot"
22
+
23
+ raise ArgumentError, "output format must be 'png' or 'dot'"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CobraCommander
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cobra_commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
@@ -10,8 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2020-07-08 00:00:00.000000000 Z
13
+ date: 2020-07-27 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.17'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.17'
15
29
  - !ruby/object:Gem::Dependency
16
30
  name: thor
17
31
  requirement: !ruby/object:Gem::Requirement
@@ -176,20 +190,19 @@ files:
176
190
  - exe/cobra
177
191
  - lib/cobra_commander.rb
178
192
  - lib/cobra_commander/affected.rb
179
- - lib/cobra_commander/cached_component_tree.rb
180
- - lib/cobra_commander/calculated_component_tree.rb
181
193
  - lib/cobra_commander/change.rb
182
194
  - lib/cobra_commander/cli.rb
183
195
  - lib/cobra_commander/component.rb
184
- - lib/cobra_commander/component_tree.rb
185
196
  - lib/cobra_commander/dependencies.rb
186
197
  - lib/cobra_commander/dependencies/bundler.rb
187
198
  - lib/cobra_commander/dependencies/yarn/package.rb
188
199
  - lib/cobra_commander/dependencies/yarn/package_repo.rb
189
200
  - lib/cobra_commander/dependencies/yarn_workspace.rb
190
201
  - lib/cobra_commander/executor.rb
191
- - lib/cobra_commander/graph.rb
192
202
  - lib/cobra_commander/output.rb
203
+ - lib/cobra_commander/output/ascii_tree.rb
204
+ - lib/cobra_commander/output/flat_list.rb
205
+ - lib/cobra_commander/output/graph_viz.rb
193
206
  - lib/cobra_commander/umbrella.rb
194
207
  - lib/cobra_commander/version.rb
195
208
  - renovate.json
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "cobra_commander/component_tree"
4
-
5
- module CobraCommander
6
- # Represents a dependency tree in a given context, built from a cache
7
- class CachedComponentTree < ComponentTree
8
- attr_reader :dependencies
9
-
10
- def self.from_cache_file(cache_file)
11
- cache = JSON.parse(File.read(cache_file), symbolize_names: true)
12
- new(cache)
13
- end
14
-
15
- def initialize(cache)
16
- super(cache[:name], cache[:path])
17
- @type = cache[:type]
18
- @ancestry = Set.new(cache[:ancestry])
19
- @dependencies = (cache[:dependencies] || []).map do |dep|
20
- CachedComponentTree.new(dep)
21
- end
22
- end
23
- end
24
- end
@@ -1,141 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "cobra_commander/component_tree"
4
- require "open3"
5
-
6
- module CobraCommander
7
- # Represents a dependency tree in a given context
8
- class CalculatedComponentTree < ComponentTree
9
- attr_reader :name, :path
10
-
11
- def initialize(name, path, ancestry = Set.new)
12
- super(name, path)
13
- @ancestry = ancestry
14
- @ruby = Ruby.new(path)
15
- @js = Js.new(path)
16
- @type = type_of_component
17
- end
18
-
19
- def dependencies
20
- @deps ||= begin
21
- deps = @ruby.dependencies + @js.dependencies
22
- deps.sort_by { |dep| dep[:name] }
23
- .map(&method(:dep_representation))
24
- end
25
- end
26
-
27
- private
28
-
29
- def type_of_component
30
- return "Ruby & JS" if @ruby.gem? && @js.node?
31
- return "Ruby" if @ruby.gem?
32
- return "JS" if @js.node?
33
- end
34
-
35
- def dep_representation(dep)
36
- full_path = File.expand_path(File.join(path, dep[:path]))
37
- ancestry = @ancestry + [{ name: @name, path: path, type: @type }]
38
- CalculatedComponentTree.new(dep[:name], full_path, ancestry)
39
- end
40
-
41
- # Calculates ruby dependencies
42
- class Ruby
43
- def initialize(root_path)
44
- @root_path = root_path
45
- end
46
-
47
- def dependencies
48
- @deps ||= begin
49
- return [] unless gem?
50
- gems = bundler_definition.dependencies.select { |dep| path?(dep.source) }
51
- format(gems)
52
- end
53
- end
54
-
55
- def path?(source)
56
- return if source.nil?
57
- source_has_path = source.respond_to?(:path?) ? source.path? : source.is_a_path?
58
- source_has_path && source.path.to_s != "."
59
- end
60
-
61
- def format(deps)
62
- deps.map do |dep|
63
- path = File.join(dep.source.path, dep.name)
64
- { name: dep.name, path: path }
65
- end
66
- end
67
-
68
- def gem?
69
- @gem ||= File.exist?(gemfile_path)
70
- end
71
-
72
- def bundler_definition
73
- ::Bundler::Definition.build(gemfile_path, gemfile_lock_path, nil)
74
- end
75
-
76
- def gemfile_path
77
- File.join(@root_path, "Gemfile")
78
- end
79
-
80
- def gemfile_lock_path
81
- File.join(@root_path, "Gemfile.lock")
82
- end
83
- end
84
-
85
- # Calculates js dependencies
86
- class Js
87
- def initialize(root_path)
88
- @root_path = root_path
89
- end
90
-
91
- def dependencies
92
- @deps ||= begin
93
- return [] unless node?
94
- json = JSON.parse(File.read(package_json_path))
95
- combined_deps(json)
96
- end
97
- end
98
-
99
- def format_dependencies(deps)
100
- return [] if deps.nil?
101
- linked_deps = deps.select { |_, v| v.start_with? "link:" }
102
- linked_deps.map do |_, v|
103
- relational_path = v.split("link:")[1]
104
- dep_name = relational_path.split("/")[-1]
105
- { name: dep_name, path: relational_path }
106
- end
107
- end
108
-
109
- def node?
110
- @node ||= File.exist?(package_json_path)
111
- end
112
-
113
- def package_json_path
114
- File.join(@root_path, "package.json")
115
- end
116
-
117
- def combined_deps(json)
118
- worskpace_dependencies = build_workspaces(json["workspaces"])
119
- dependencies = format_dependencies Hash(json["dependencies"]).merge(Hash(json["devDependencies"]))
120
- (dependencies + worskpace_dependencies).uniq
121
- end
122
-
123
- def build_workspaces(workspaces)
124
- return [] if workspaces.nil?
125
-
126
- yarn_workspaces.map do |_, component|
127
- { name: component["location"].split("/")[-1], path: component["location"] }
128
- end
129
- end
130
-
131
- private
132
-
133
- def yarn_workspaces
134
- @yarn_workspaces ||= begin
135
- output, = Open3.capture2("yarn --json workspaces info", chdir: @root_path)
136
- JSON.parse(JSON.parse(output)["data"])
137
- end
138
- end
139
- end
140
- end
141
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler"
4
- require "json"
5
-
6
- module CobraCommander
7
- # Represents a dependency tree in a given context
8
- class ComponentTree
9
- attr_reader :name, :path
10
-
11
- def initialize(name, path)
12
- @name = name
13
- @path = path
14
- end
15
-
16
- def flatten
17
- _flatten(self)
18
- end
19
-
20
- def subtree(name)
21
- _subtree(name, self)
22
- end
23
-
24
- def depends_on?(component_name)
25
- dependencies.any? do |component|
26
- component.name == component_name || component.depends_on?(component_name)
27
- end
28
- end
29
-
30
- def dependents_of(component_name)
31
- depends = depends_on?(component_name) ? self : nil
32
- dependents_below = dependencies.map do |component|
33
- component.dependents_of(component_name)
34
- end
35
- [depends, dependents_below].flatten.compact.uniq(&:name)
36
- end
37
-
38
- def to_h(json_compatible: false)
39
- {
40
- name: @name,
41
- path: path,
42
- type: @type,
43
- ancestry: json_compatible ? @ancestry.to_a : @ancestry,
44
- dependencies: dependencies.map { |dep| dep.to_h(json_compatible: json_compatible) },
45
- }
46
- end
47
-
48
- def to_json
49
- JSON.dump(to_h(json_compatible: true))
50
- end
51
-
52
- private
53
-
54
- def _flatten(component)
55
- component.dependencies.map do |dep|
56
- [dep] + _flatten(dep)
57
- end.flatten.uniq(&:name)
58
- end
59
-
60
- def _subtree(name, tree)
61
- return tree if tree.name == name
62
- tree.dependencies.each do |component|
63
- presence = _subtree(name, component)
64
- return presence if presence
65
- end
66
- nil
67
- end
68
- end
69
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "graphviz"
4
-
5
- module CobraCommander
6
- # Generates graphs of components
7
- class Graph
8
- def initialize(node, format)
9
- @format = format
10
- @node = node
11
- end
12
-
13
- def generate!
14
- return unless valid_format?
15
-
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
21
-
22
- output(g)
23
- end
24
-
25
- def output(g)
26
- graph = "graph.#{@format}"
27
- g.output(@format => graph)
28
- puts "Graph generated at #{`pwd`.chomp}/#{graph}"
29
- end
30
-
31
- def valid_format?
32
- return true if @format == "png" || @format == "dot"
33
- puts "FORMAT must be 'png' or 'dot'"
34
- false
35
- end
36
- end
37
- end