cobra_commander 0.4.0 → 0.5.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: fb2f9f51193bd4ec022e4f12eae0b43f02d0f4f7adac086ac8c38f2bd7f3bf56
4
- data.tar.gz: bb44e1c12845cab98efdcb8cf1da499f67fa335bd781bf14f4c07d11ed3c9eb5
3
+ metadata.gz: 0a05f3e1ab4ccff9ca040dcf4d694aebc28df0f50160067cf73eaea89f4bad59
4
+ data.tar.gz: d0132e226b7527e72ebb84e3844116dd3a0c2ed8ecc1394d9df5f7fad5f777d8
5
5
  SHA512:
6
- metadata.gz: fd838a67943d9e2ae8cc7038b81ccacaaea719f8cc08cea7704df5147ffc1609ef2a91622549d423954003465d1ec310b0d09bf10ec8f3cf915850e0dbdbe39c
7
- data.tar.gz: a4dec3544d6a5fbcd258b759b9ba143955acf054b4ff44af1fb25d0f3391f37741e9c2d9e31221fd192ec08c843f39b62833a6741de6313e17453e37ca82722b
6
+ metadata.gz: 1cb90f61448f1bc69088418fc86dafbc25fce09d069507e22fc22f0a2d9a63eccf95ce3edeb13b6ee2657e19303489c2b59087944ddc07597c7d0e201c36a307
7
+ data.tar.gz: '023961e1f0abe297e0d0cacc169b3da23fac68f833627d01d480f06d0822019877cd1ac78bb20bf7f951b2bbdce04a0c0ddbb7b3b444a90669f18c967a43775c'
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Version 0.5.0 - 2018-09-20
6
+
7
+ * Renames `dependencies_of` to `dependents_of`. PR [#25](https://github.com/powerhome/cobra_commander/pull/25)
8
+ * Add `dependencies_of` command list the direct and indirect dependencies of one component. PR [#25](https://github.com/powerhome/cobra_commander/pull/25)
9
+ * Add `do` command allow executing a command in the context of each component. PR [#26](https://github.com/powerhome/cobra_commander/pull/26)
10
+
5
11
  ## Version 0.4.0 - 2018-09-06
6
12
 
7
13
  * Add `dependencies_of` command to permit listing or counting the dependencies of a particular component. PR [#24](https://github.com/powerhome/cobra_commander/pull/24)
data/README.md CHANGED
@@ -28,11 +28,15 @@ Or install it yourself as:
28
28
 
29
29
  ```bash
30
30
  Commands:
31
- cobra changes APP_PATH [--results=RESULTS] [--branch=BRANCH] # Prints list of changed files
32
- cobra graph APP_PATH [--format=FORMAT] # Outputs graph
33
- cobra help [COMMAND] # Describe available commands or one specific command
34
- cobra ls APP_PATH # Prints tree of components for an app
35
- cobra version # Prints version
31
+ cobra changes APP_PATH [--results=RESULTS] [--branch=BRANCH] # Prints list of changed files
32
+ cobra graph APP_PATH [--format=FORMAT] # Outputs graph
33
+ cobra help [COMMAND] # Describe available commands or one specific command
34
+ cobra ls APP_PATH [--format=list|tree] # Prints tree of components for an app
35
+ cobra version # Prints version
36
+ cobra do [command] # Executes the command in the context of each component
37
+ cobra dependencies_of [component] [--app=/path/to/app] [--format=list|tree] # Lists the dependencies of the component in the app context
38
+ cobra dependents_of [component] [--app=/path/to/app] [--format=list|count] # Lists or counts the components that depend directly or indirectly on the given component
39
+ cobra version # Prints version
36
40
  ```
37
41
 
38
42
  ## Development
@@ -3,14 +3,19 @@
3
3
  require "cobra_commander/cli"
4
4
  require "cobra_commander/component_tree"
5
5
  require "cobra_commander/version"
6
- require "cobra_commander/formatted_output"
7
6
  require "cobra_commander/graph"
8
7
  require "cobra_commander/change"
9
8
  require "cobra_commander/affected"
9
+ require "cobra_commander/output"
10
+ require "cobra_commander/executor"
10
11
 
11
12
  # Tools for working with Component Based Rails Apps (see http://shageman.github.io/cbra.info/).
12
13
  # Includes tools for graphing the components of an app and their relationships, as well as selectively
13
14
  # testing components based on changes made.
14
15
  module CobraCommander
15
16
  UMBRELLA_APP_NAME = "App"
17
+
18
+ def self.umbrella_tree(path)
19
+ ComponentTree.new(UMBRELLA_APP_NAME, path)
20
+ end
16
21
  end
@@ -13,7 +13,7 @@ module CobraCommander
13
13
  @root_dir = Dir.chdir(path) { `git rev-parse --show-toplevel`.chomp }
14
14
  @results = results
15
15
  @branch = branch
16
- @tree = ComponentTree.new(path).to_h
16
+ @tree = CobraCommander.umbrella_tree(path).to_h
17
17
  @affected = Affected.new(@tree, changes, path)
18
18
  end
19
19
 
@@ -5,16 +5,40 @@ require "thor"
5
5
  module CobraCommander
6
6
  # Implements the tool's CLI
7
7
  class CLI < Thor
8
- desc "ls APP_PATH", "Prints tree of components for an app"
9
- def ls(app_path)
10
- puts FormattedOutput.new(app_path).run!
8
+ desc "do [command]", "Executes the command in the context of each component in [app]"
9
+ method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
10
+ def do(command)
11
+ tree = CobraCommander.umbrella_tree(options.app)
12
+ executor = Executor.new(tree)
13
+ executor.exec(command)
11
14
  end
12
15
 
13
- desc "dependencies_of APP_PATH", "Outputs count of components in APP_PATH dependent on COMPONENT"
14
- method_option :component, required: true, aliases: "-c", desc: "Name of component. Ex: my_component"
16
+ desc "ls [app_path]", "Prints tree of components for an app"
17
+ method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
18
+ method_option :format, default: "tree", aliases: "-f", desc: "Format (list or tree, default: list)"
19
+ def ls(app_path = nil)
20
+ Output.print(
21
+ CobraCommander.umbrella_tree(app_path || options.app),
22
+ options.format
23
+ )
24
+ end
25
+
26
+ desc "dependents_of [component]", "Outputs count of components in [app] dependent on [component]"
27
+ method_option :app, default: Dir.pwd, aliases: "-a", desc: "Path to the root app where the component is mounted"
15
28
  method_option :format, default: "count", aliases: "-f", desc: "count or list"
16
- def dependencies_of(app_path)
17
- puts FormattedOutput.new(app_path).dependencies_of!(@options[:component], @options[:format])
29
+ def dependents_of(component)
30
+ dependents = CobraCommander.umbrella_tree(options.app).dependents_of(component)
31
+ puts "list" == options.format ? dependents.map(&:name) : dependents.size
32
+ end
33
+
34
+ desc "dependencies_of [component]", "Outputs a list of components that [component] depends on within [app] context"
35
+ method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
36
+ method_option :format, default: "list", aliases: "-f", desc: "Format (list or tree, default: list)"
37
+ def dependencies_of(component)
38
+ Output.print(
39
+ CobraCommander.umbrella_tree(options.app).subtree(component),
40
+ options.format
41
+ )
18
42
  end
19
43
 
20
44
  desc "version", "Prints version"
@@ -25,14 +49,14 @@ module CobraCommander
25
49
  desc "graph APP_PATH [--format=FORMAT]", "Outputs graph"
26
50
  method_option :format, default: "png", aliases: "-f", desc: "Accepts png or dot"
27
51
  def graph(app_path)
28
- Graph.new(app_path, @options[:format]).generate!
52
+ Graph.new(app_path, options.format).generate!
29
53
  end
30
54
 
31
55
  desc "changes APP_PATH [--results=RESULTS] [--branch=BRANCH]", "Prints list of changed files"
32
56
  method_option :results, default: "test", aliases: "-r", desc: "Accepts test, full, name or json"
33
57
  method_option :branch, default: "master", aliases: "-b", desc: "Specified target to calculate against"
34
58
  def changes(app_path)
35
- Change.new(app_path, @options[:results], @options[:branch]).run!
59
+ Change.new(app_path, options.results, options.branch).run!
36
60
  end
37
61
  end
38
62
  end
@@ -1,139 +1,167 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bundler"
3
4
  require "json"
4
5
 
5
6
  module CobraCommander
6
- # Representation of the tree of components and their dependencies
7
+ # Represents a dependency tree in a given context
7
8
  class ComponentTree
8
- def initialize(path)
9
- @root_path = path
9
+ attr_reader :name, :path
10
+
11
+ def initialize(name, path, ancestry = Set.new)
12
+ @name = name
13
+ @path = path
14
+ @ancestry = ancestry
15
+ @ruby = Ruby.new(path)
16
+ @js = Js.new(path)
17
+ @type = type_of_component
18
+ end
19
+
20
+ def flatten
21
+ _flatten(self)
22
+ end
23
+
24
+ def subtree(name)
25
+ _subtree(name, self)
26
+ end
27
+
28
+ def depends_on?(component_name)
29
+ dependencies.any? do |component|
30
+ component.name == component_name || component.depends_on?(component_name)
31
+ end
32
+ end
33
+
34
+ def dependents_of(component_name)
35
+ depends = depends_on?(component_name) ? self : nil
36
+ dependents_below = dependencies.map do |component|
37
+ component.dependents_of(component_name)
38
+ end
39
+ [depends, dependents_below].flatten.compact.uniq(&:name)
10
40
  end
11
41
 
12
42
  def to_h
13
- Tree.new(UMBRELLA_APP_NAME, @root_path).to_h
43
+ {
44
+ name: @name,
45
+ path: path,
46
+ type: @type,
47
+ ancestry: @ancestry,
48
+ dependencies: dependencies.map(&:to_h),
49
+ }
14
50
  end
15
51
 
16
- # Generates component tree
17
- class Tree
18
- def initialize(name, path, ancestry = Set.new)
19
- @name = name
20
- @root_path = path
21
- @ancestry = ancestry
22
- @ruby = Ruby.new(path)
23
- @js = Js.new(path)
24
- @type = type_of_component
52
+ def dependencies
53
+ @deps ||= begin
54
+ deps = @ruby.dependencies + @js.dependencies
55
+ deps.sort_by { |dep| dep[:name] }
56
+ .map(&method(:dep_representation))
25
57
  end
58
+ end
26
59
 
27
- def to_h
28
- {
29
- name: @name,
30
- path: @root_path,
31
- type: @type,
32
- ancestry: @ancestry,
33
- dependencies: dependencies.map(&method(:dep_representation)),
34
- }
60
+ private
61
+
62
+ def _flatten(component)
63
+ component.dependencies.map do |dep|
64
+ [dep] + _flatten(dep)
65
+ end.flatten.uniq(&:name)
66
+ end
67
+
68
+ def _subtree(name, tree)
69
+ return tree if tree.name == name
70
+ tree.dependencies.each do |component|
71
+ return _subtree(name, component)
35
72
  end
73
+ end
36
74
 
37
- private
75
+ def type_of_component
76
+ return "Ruby & JS" if @ruby.gem? && @js.node?
77
+ return "Ruby" if @ruby.gem?
78
+ return "JS" if @js.node?
79
+ end
38
80
 
39
- def type_of_component
40
- return "Ruby & JS" if @ruby.gem? && @js.node?
41
- return "Ruby" if @ruby.gem?
42
- return "JS" if @js.node?
81
+ def dep_representation(dep)
82
+ full_path = File.expand_path(File.join(path, dep[:path]))
83
+ ancestry = @ancestry + [{ name: @name, path: path, type: @type }]
84
+ ComponentTree.new(dep[:name], full_path, ancestry)
85
+ end
86
+
87
+ # Calculates ruby dependencies
88
+ class Ruby
89
+ def initialize(root_path)
90
+ @root_path = root_path
43
91
  end
44
92
 
45
93
  def dependencies
46
94
  @deps ||= begin
47
- deps = @ruby.dependencies + @js.dependencies
48
- deps.sort_by { |dep| dep[:name] }
95
+ return [] unless gem?
96
+ gems = bundler_definition.dependencies.select { |dep| path?(dep.source) }
97
+ format(gems)
49
98
  end
50
99
  end
51
100
 
52
- def dep_representation(dep)
53
- full_path = File.expand_path(File.join(@root_path, dep[:path]))
54
- ancestry = @ancestry + [{ name: @name, path: @root_path, type: @type }]
55
- self.class.new(dep[:name], full_path, ancestry).to_h
101
+ def path?(source)
102
+ return if source.nil?
103
+ source_has_path = source.respond_to?(:path?) ? source.path? : source.is_a_path?
104
+ source_has_path && source.path.to_s != "."
56
105
  end
57
106
 
58
- # Calculates ruby dependencies
59
- class Ruby
60
- def initialize(root_path)
61
- @root_path = root_path
62
- end
63
-
64
- def dependencies
65
- @deps ||= begin
66
- return [] unless gem?
67
- gems = bundler_definition.dependencies.select { |dep| path?(dep.source) }
68
- format(gems)
69
- end
70
- end
71
-
72
- def path?(source)
73
- return if source.nil?
74
- source_has_path = source.respond_to?(:path?) ? source.path? : source.is_a_path?
75
- source_has_path && source.path.to_s != "."
76
- end
77
-
78
- def format(deps)
79
- deps.map do |dep|
80
- path = File.join(dep.source.path, dep.name)
81
- { name: dep.name, path: path }
82
- end
107
+ def format(deps)
108
+ deps.map do |dep|
109
+ path = File.join(dep.source.path, dep.name)
110
+ { name: dep.name, path: path }
83
111
  end
112
+ end
84
113
 
85
- def gem?
86
- @gem ||= File.exist?(gemfile_path)
87
- end
114
+ def gem?
115
+ @gem ||= File.exist?(gemfile_path)
116
+ end
88
117
 
89
- def bundler_definition
90
- ::Bundler::Definition.build(gemfile_path, gemfile_lock_path, nil)
91
- end
118
+ def bundler_definition
119
+ ::Bundler::Definition.build(gemfile_path, gemfile_lock_path, nil)
120
+ end
92
121
 
93
- def gemfile_path
94
- File.join(@root_path, "Gemfile")
95
- end
122
+ def gemfile_path
123
+ File.join(@root_path, "Gemfile")
124
+ end
96
125
 
97
- def gemfile_lock_path
98
- File.join(@root_path, "Gemfile.lock")
99
- end
126
+ def gemfile_lock_path
127
+ File.join(@root_path, "Gemfile.lock")
100
128
  end
129
+ end
101
130
 
102
- # Calculates js dependencies
103
- class Js
104
- def initialize(root_path)
105
- @root_path = root_path
106
- end
131
+ # Calculates js dependencies
132
+ class Js
133
+ def initialize(root_path)
134
+ @root_path = root_path
135
+ end
107
136
 
108
- def dependencies
109
- @deps ||= begin
110
- return [] unless node?
111
- json = JSON.parse(File.read(package_json_path))
112
- format combined_deps(json)
113
- end
137
+ def dependencies
138
+ @deps ||= begin
139
+ return [] unless node?
140
+ json = JSON.parse(File.read(package_json_path))
141
+ format combined_deps(json)
114
142
  end
143
+ end
115
144
 
116
- def format(deps)
117
- return [] if deps.nil?
118
- linked_deps = deps.select { |_, v| v.start_with? "link:" }
119
- linked_deps.map do |_, v|
120
- relational_path = v.split("link:")[1]
121
- dep_name = relational_path.split("/")[-1]
122
- { name: dep_name, path: relational_path }
123
- end
145
+ def format(deps)
146
+ return [] if deps.nil?
147
+ linked_deps = deps.select { |_, v| v.start_with? "link:" }
148
+ linked_deps.map do |_, v|
149
+ relational_path = v.split("link:")[1]
150
+ dep_name = relational_path.split("/")[-1]
151
+ { name: dep_name, path: relational_path }
124
152
  end
153
+ end
125
154
 
126
- def node?
127
- @node ||= File.exist?(package_json_path)
128
- end
155
+ def node?
156
+ @node ||= File.exist?(package_json_path)
157
+ end
129
158
 
130
- def package_json_path
131
- File.join(@root_path, "package.json")
132
- end
159
+ def package_json_path
160
+ File.join(@root_path, "package.json")
161
+ end
133
162
 
134
- def combined_deps(json)
135
- Hash(json["dependencies"]).merge(Hash(json["devDependencies"]))
136
- end
163
+ def combined_deps(json)
164
+ Hash(json["dependencies"]).merge(Hash(json["devDependencies"]))
137
165
  end
138
166
  end
139
167
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ # Execute commands on all components of a ComponentTree
5
+ class Executor
6
+ def initialize(tree)
7
+ @tree = tree
8
+ end
9
+
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
15
+ end
16
+ 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
+ end
30
+ end
@@ -8,7 +8,7 @@ module CobraCommander
8
8
  class Graph
9
9
  def initialize(app_path, format)
10
10
  @format = format
11
- @tree = ComponentTree.new(app_path).to_h
11
+ @tree = CobraCommander.umbrella_tree(app_path).to_h
12
12
  end
13
13
 
14
14
  def generate!
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ # Module for pretty printing dependency trees
5
+ module Output
6
+ def self.print(tree, format)
7
+ output = format == "list" ? Output::FlatList.new(tree) : Output::Tree.new(tree)
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.flatten.map(&:name)
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(tree)
32
+ @tree = tree
33
+ end
34
+
35
+ def to_s
36
+ StringIO.new.tap do |io|
37
+ io.puts @tree.name
38
+ list_dependencies(io, @tree)
39
+ end.string
40
+ end
41
+
42
+ private
43
+
44
+ def list_dependencies(io, deps, outdents = [])
45
+ deps.dependencies.each do |dep|
46
+ decide_on_line(io, deps, 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CobraCommander
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.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.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-09-06 00:00:00.000000000 Z
12
+ date: 2018-09-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -170,8 +170,9 @@ files:
170
170
  - lib/cobra_commander/change.rb
171
171
  - lib/cobra_commander/cli.rb
172
172
  - lib/cobra_commander/component_tree.rb
173
- - lib/cobra_commander/formatted_output.rb
173
+ - lib/cobra_commander/executor.rb
174
174
  - lib/cobra_commander/graph.rb
175
+ - lib/cobra_commander/output.rb
175
176
  - lib/cobra_commander/version.rb
176
177
  homepage: http://tech.powerhrg.com/cobra_commander/
177
178
  licenses:
@@ -193,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
194
  version: '0'
194
195
  requirements: []
195
196
  rubyforge_project:
196
- rubygems_version: 2.7.3
197
+ rubygems_version: 2.7.7
197
198
  signing_key:
198
199
  specification_version: 4
199
200
  summary: Tools for working with Component Based Rails Apps
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "cobra_commander/component_tree"
4
-
5
- module CobraCommander
6
- # Formats CLI ls output
7
- class FormattedOutput
8
- attr_accessor :tree
9
-
10
- SPACE = " "
11
- BAR = "│   "
12
- TEE = "├── "
13
- CORNER = "└── "
14
-
15
- def initialize(app_path)
16
- @tree = ComponentTree.new(app_path).to_h
17
- end
18
-
19
- def run!
20
- puts @tree[:name]
21
- list_dependencies(@tree)
22
- nil
23
- end
24
-
25
- def dependencies_of!(component_name, format)
26
- @component_name = component_name
27
-
28
- results = @tree[:dependencies].map do |component|
29
- if @component_name == component[:name]
30
- @tree[:name]
31
- elsif dependency?(component)
32
- component[:name]
33
- end
34
- end.compact
35
-
36
- "list" == format ? results : results.size
37
- end
38
-
39
- private
40
-
41
- def dependency?(deps)
42
- deps[:dependencies].each do |dep|
43
- return true if @component_name == dep[:name] || dependency?(dep)
44
- end
45
- false
46
- end
47
-
48
- def list_dependencies(deps, outdents = [])
49
- deps[:dependencies].each do |dep|
50
- decide_on_line(deps, dep, outdents)
51
- end
52
- end
53
-
54
- def decide_on_line(parent, dep, outdents)
55
- if parent[:dependencies].last != dep
56
- add_tee(outdents, dep)
57
- else
58
- add_corner(outdents, dep)
59
- end
60
- end
61
-
62
- def add_tee(outdents, dep)
63
- puts line(outdents, TEE, dep[:name])
64
- list_dependencies(dep, (outdents + [BAR]))
65
- end
66
-
67
- def add_corner(outdents, dep)
68
- puts line(outdents, CORNER, dep[:name])
69
- list_dependencies(dep, (outdents + [SPACE]))
70
- end
71
-
72
- def line(outdents, sym, name)
73
- (outdents + [sym] + [name]).join
74
- end
75
- end
76
- end