cobra_commander 0.7.0 → 0.8.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +101 -10
- data/cobra_commander.gemspec +1 -0
- data/exe/cobra +1 -1
- data/lib/cobra_commander.rb +0 -9
- data/lib/cobra_commander/cli.rb +63 -58
- data/lib/cobra_commander/executor.rb +3 -7
- data/lib/cobra_commander/output.rb +3 -72
- data/lib/cobra_commander/output/ascii_tree.rb +55 -0
- data/lib/cobra_commander/output/flat_list.rb +16 -0
- data/lib/cobra_commander/output/graph_viz.rb +27 -0
- data/lib/cobra_commander/version.rb +1 -1
- metadata +19 -6
- data/lib/cobra_commander/cached_component_tree.rb +0 -24
- data/lib/cobra_commander/calculated_component_tree.rb +0 -141
- data/lib/cobra_commander/component_tree.rb +0 -69
- data/lib/cobra_commander/graph.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f1de2b5ceb4fbde7dcd12fd71606c55cdd733e6928e4c6a866dfc1f3ec65af3
|
4
|
+
data.tar.gz: 5a9537dabd13b974df919be911755debf92761b6bdf99b370c59cec2c8fc09af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58f066894063d46d3b1c25baf2b085f3212db9ac716eee5c8ef6c2465c82233f06fae79e30c04205d95b75551799cd3f1c67901a60a516bf079e2cb154baa2b2
|
7
|
+
data.tar.gz: 99babd4c1ae0b5e218ed913bf175cbd38d4efe4bc19d11ea108d8f914c9f3381a7f43bb6c8a4d64171bec648e5c5e8441c39c30f5c3a83f11e0c9f1aa4e0948b
|
data/CHANGELOG.md
CHANGED
@@ -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
|
32
|
-
cobra
|
33
|
-
cobra
|
34
|
-
cobra
|
35
|
-
cobra
|
36
|
-
cobra
|
37
|
-
cobra
|
38
|
-
|
39
|
-
|
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
|
data/cobra_commander.gemspec
CHANGED
@@ -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
data/lib/cobra_commander.rb
CHANGED
@@ -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
|
data/lib/cobra_commander/cli.rb
CHANGED
@@ -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
|
-
|
10
|
-
|
11
|
-
|
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 "
|
23
|
-
|
24
|
-
|
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 "
|
34
|
-
method_option :
|
35
|
-
|
36
|
-
method_option :
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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 "
|
44
|
-
|
45
|
-
method_option :
|
46
|
-
method_option :
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
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 "
|
55
|
-
def
|
56
|
-
|
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
|
60
|
-
method_option :
|
61
|
-
|
62
|
-
def graph(
|
63
|
-
|
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
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
84
|
+
def components_filtered(component_name)
|
85
|
+
return umbrella.components unless component_name
|
86
|
+
component = find_component(component_name)
|
83
87
|
|
84
|
-
|
85
|
-
|
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
|
-
|
6
|
-
def
|
7
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
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
|
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.
|
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-
|
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
|