cobra_commander 0.7.0 → 0.9.2

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.
@@ -3,7 +3,7 @@
3
3
  module CobraCommander
4
4
  # Represents a component withing an Umbrella
5
5
  class Component
6
- attr_reader :name, :dependencies, :sources
6
+ attr_reader :name, :sources
7
7
 
8
8
  def initialize(umbrella, name)
9
9
  @umbrella = umbrella
@@ -1,23 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bundler"
4
+ require "bundler/lockfile_parser"
5
+ require "pathname"
6
+
3
7
  module CobraCommander
4
8
  module Dependencies
5
9
  # Calculates ruby bundler dependencies
6
10
  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
11
+ attr_reader :path
14
12
 
15
- def path
16
- @definition.lockfile
13
+ def initialize(root)
14
+ @root = root
15
+ @path = Pathname.new(File.join(root, "Gemfile.lock")).realpath
17
16
  end
18
17
 
19
18
  def dependencies
20
- @definition.dependencies.map(&:name)
19
+ lockfile.dependencies.values.map(&:name)
21
20
  end
22
21
 
23
22
  def components
@@ -28,9 +27,14 @@ module CobraCommander
28
27
 
29
28
  private
30
29
 
30
+ def lockfile
31
+ @lockfile ||= ::Bundler::LockfileParser.new(::Bundler.read_file(path))
32
+ end
33
+
31
34
  def components_source
32
- @components_source ||= @definition.send(:sources).path_sources.find do |source|
33
- source.path.to_s.eql?("components")
35
+ @components_source ||= begin
36
+ source = @lockfile.sources.find { |s| s.path.to_s.eql?("components") }
37
+ ::Bundler::Source::Path.new(source.options.merge("root_path" => @root))
34
38
  end
35
39
  end
36
40
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "pathname"
4
5
 
5
6
  module CobraCommander
6
7
  module Dependencies
@@ -10,11 +11,11 @@ module CobraCommander
10
11
  attr_reader :path
11
12
 
12
13
  def initialize(path)
13
- @path = Pathname.new(File.join(path, "package.json")).realpath
14
+ @path = ::Pathname.new(File.join(path, "package.json")).realpath
14
15
  end
15
16
 
16
17
  def project_tag
17
- name.match(%r{^@[\w-]+\/}).to_s
18
+ name.match(%r{^@[\w-]+/}).to_s
18
19
  end
19
20
 
20
21
  def name
@@ -6,7 +6,7 @@ module CobraCommander
6
6
  # Yarn package repository to load and cache package.json files
7
7
  class PackageRepo
8
8
  def initialize
9
- @specs ||= {}
9
+ @specs = {}
10
10
  end
11
11
 
12
12
  def specs
@@ -14,8 +14,9 @@ module CobraCommander
14
14
  end
15
15
 
16
16
  def load_linked_specs(package)
17
- package.dependencies.values.each do |spec|
17
+ package.dependencies.each_value do |spec|
18
18
  next unless spec =~ /link:(.+)/
19
+
19
20
  load_spec(File.join(package.path, "..", Regexp.last_match(1)))
20
21
  end
21
22
  end
@@ -1,20 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "executor/component_exec"
4
+ require_relative "executor/multi_exec"
5
+
3
6
  module CobraCommander
4
7
  # 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|
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
17
- end
8
+ module Executor
9
+ def self.exec(components, command, output = $stdout, status_output = $stderr)
10
+ components = Array(components)
11
+ exec = if components.size == 1
12
+ ComponentExec.new(components.first)
13
+ else
14
+ MultiExec.new(components)
15
+ end
16
+ exec.run(command, output: output, spin_output: status_output)
18
17
  end
19
18
  end
20
19
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-command"
4
+
5
+ module CobraCommander
6
+ module Executor
7
+ # Execute a command on a single component
8
+ class ComponentExec
9
+ def initialize(component)
10
+ @component = component
11
+ end
12
+
13
+ def run(command, output: $stdout, **cmd_options)
14
+ tty = TTY::Command.new(pty: true, printer: :quiet, output: output)
15
+ isolate_bundle do
16
+ @component.root_paths.all? do |path|
17
+ tty.run!(command, chdir: path, **cmd_options).success?
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def isolate_bundle(&block)
25
+ if Bundler.respond_to?(:with_unbundled_env)
26
+ Bundler.with_unbundled_env(&block)
27
+ else
28
+ Bundler.with_clean_env(&block)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-spinner"
4
+ require "stringio"
5
+
6
+ require_relative "component_exec"
7
+
8
+ module CobraCommander
9
+ module Executor
10
+ # Executes a command on multiple components simultaniously
11
+ class MultiExec
12
+ def initialize(components)
13
+ @components = components
14
+ end
15
+
16
+ def run(command, output: $stdout, spin_output: $stderr, only_output_on_error: true, **cmd_options)
17
+ cmmd_output = StringIO.new
18
+ multi = TTY::Spinner::Multi.new("Running #{command}", output: spin_output)
19
+ @components.each do |component|
20
+ component_exec(multi, component, command, only_output_on_error: only_output_on_error,
21
+ stderr: :stdout, output: cmmd_output,
22
+ **cmd_options)
23
+ end
24
+ multi.auto_spin
25
+ output << cmmd_output.string
26
+ true
27
+ end
28
+
29
+ private
30
+
31
+ def component_exec(multi, component, command, **options)
32
+ exec = ComponentExec.new(component)
33
+ multi.register(*spinner(component.name)) do |spin|
34
+ exec.run(command, **options) ? spin.success : spin.error
35
+ end
36
+ end
37
+
38
+ def spinner(title)
39
+ pastel = Pastel.new
40
+ [":spinner #{title}", { format: :bouncing,
41
+ success_mark: pastel.green("[DONE]"),
42
+ error_mark: pastel.red("[ERROR]"), },]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -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 %w[png dot].include?(format)
22
+
23
+ raise ArgumentError, "output format must be 'png' or 'dot'"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -21,6 +21,7 @@ module CobraCommander
21
21
 
22
22
  def resolve(component_root_path)
23
23
  return root if root.root_paths.include?(component_root_path)
24
+
24
25
  components.find do |component|
25
26
  component.root_paths.include?(component_root_path)
26
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.9.2"
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.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
@@ -10,8 +10,36 @@ 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-09-23 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'
29
+ - !ruby/object:Gem::Dependency
30
+ name: ruby-graphviz
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: 1.2.3
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 1.2.3
15
43
  - !ruby/object:Gem::Dependency
16
44
  name: thor
17
45
  requirement: !ruby/object:Gem::Requirement
@@ -33,61 +61,61 @@ dependencies:
33
61
  - !ruby/object:Gem::Version
34
62
  version: 0.18.1
35
63
  - !ruby/object:Gem::Dependency
36
- name: ruby-graphviz
64
+ name: tty-command
37
65
  requirement: !ruby/object:Gem::Requirement
38
66
  requirements:
39
67
  - - "~>"
40
68
  - !ruby/object:Gem::Version
41
- version: 1.2.3
69
+ version: 0.9.0
42
70
  type: :runtime
43
71
  prerelease: false
44
72
  version_requirements: !ruby/object:Gem::Requirement
45
73
  requirements:
46
74
  - - "~>"
47
75
  - !ruby/object:Gem::Version
48
- version: 1.2.3
76
+ version: 0.9.0
49
77
  - !ruby/object:Gem::Dependency
50
- name: bundler
78
+ name: tty-spinner
51
79
  requirement: !ruby/object:Gem::Requirement
52
80
  requirements:
53
81
  - - "~>"
54
82
  - !ruby/object:Gem::Version
55
- version: '1.17'
56
- type: :development
83
+ version: 0.9.3
84
+ type: :runtime
57
85
  prerelease: false
58
86
  version_requirements: !ruby/object:Gem::Requirement
59
87
  requirements:
60
88
  - - "~>"
61
89
  - !ruby/object:Gem::Version
62
- version: '1.17'
90
+ version: 0.9.3
63
91
  - !ruby/object:Gem::Dependency
64
- name: rake
92
+ name: aruba
65
93
  requirement: !ruby/object:Gem::Requirement
66
94
  requirements:
67
95
  - - "~>"
68
96
  - !ruby/object:Gem::Version
69
- version: '10.0'
97
+ version: 0.14.2
70
98
  type: :development
71
99
  prerelease: false
72
100
  version_requirements: !ruby/object:Gem::Requirement
73
101
  requirements:
74
102
  - - "~>"
75
103
  - !ruby/object:Gem::Version
76
- version: '10.0'
104
+ version: 0.14.2
77
105
  - !ruby/object:Gem::Dependency
78
- name: rspec
106
+ name: bundler
79
107
  requirement: !ruby/object:Gem::Requirement
80
108
  requirements:
81
109
  - - "~>"
82
110
  - !ruby/object:Gem::Version
83
- version: '3.5'
111
+ version: '1.17'
84
112
  type: :development
85
113
  prerelease: false
86
114
  version_requirements: !ruby/object:Gem::Requirement
87
115
  requirements:
88
116
  - - "~>"
89
117
  - !ruby/object:Gem::Version
90
- version: '3.5'
118
+ version: '1.17'
91
119
  - !ruby/object:Gem::Dependency
92
120
  name: guard-rspec
93
121
  requirement: !ruby/object:Gem::Requirement
@@ -103,47 +131,61 @@ dependencies:
103
131
  - !ruby/object:Gem::Version
104
132
  version: '0'
105
133
  - !ruby/object:Gem::Dependency
106
- name: aruba
134
+ name: pry
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ type: :development
141
+ prerelease: false
142
+ version_requirements: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ - !ruby/object:Gem::Dependency
148
+ name: rake
107
149
  requirement: !ruby/object:Gem::Requirement
108
150
  requirements:
109
151
  - - "~>"
110
152
  - !ruby/object:Gem::Version
111
- version: 0.14.2
153
+ version: '10.0'
112
154
  type: :development
113
155
  prerelease: false
114
156
  version_requirements: !ruby/object:Gem::Requirement
115
157
  requirements:
116
158
  - - "~>"
117
159
  - !ruby/object:Gem::Version
118
- version: 0.14.2
160
+ version: '10.0'
119
161
  - !ruby/object:Gem::Dependency
120
- name: rubocop
162
+ name: rspec
121
163
  requirement: !ruby/object:Gem::Requirement
122
164
  requirements:
123
- - - '='
165
+ - - "~>"
124
166
  - !ruby/object:Gem::Version
125
- version: 0.48.1
167
+ version: '3.5'
126
168
  type: :development
127
169
  prerelease: false
128
170
  version_requirements: !ruby/object:Gem::Requirement
129
171
  requirements:
130
- - - '='
172
+ - - "~>"
131
173
  - !ruby/object:Gem::Version
132
- version: 0.48.1
174
+ version: '3.5'
133
175
  - !ruby/object:Gem::Dependency
134
- name: pry
176
+ name: rubocop
135
177
  requirement: !ruby/object:Gem::Requirement
136
178
  requirements:
137
- - - ">="
179
+ - - '='
138
180
  - !ruby/object:Gem::Version
139
- version: '0'
181
+ version: 0.88.0
140
182
  type: :development
141
183
  prerelease: false
142
184
  version_requirements: !ruby/object:Gem::Requirement
143
185
  requirements:
144
- - - ">="
186
+ - - '='
145
187
  - !ruby/object:Gem::Version
146
- version: '0'
188
+ version: 0.88.0
147
189
  description: |
148
190
  Tools for working with Component Based Rails Apps (see http://shageman.github.io/cbra.info/).
149
191
  Includes tools for graphing the components of an app and their relationships, as well as selectively
@@ -158,10 +200,10 @@ extensions: []
158
200
  extra_rdoc_files: []
159
201
  files:
160
202
  - ".editorconfig"
203
+ - ".github/workflows/ci.yml"
161
204
  - ".gitignore"
162
205
  - ".rspec"
163
206
  - ".rubocop.yml"
164
- - ".travis.yml"
165
207
  - CHANGELOG.md
166
208
  - CODE_OF_CONDUCT.md
167
209
  - Gemfile
@@ -176,20 +218,21 @@ files:
176
218
  - exe/cobra
177
219
  - lib/cobra_commander.rb
178
220
  - lib/cobra_commander/affected.rb
179
- - lib/cobra_commander/cached_component_tree.rb
180
- - lib/cobra_commander/calculated_component_tree.rb
181
221
  - lib/cobra_commander/change.rb
182
222
  - lib/cobra_commander/cli.rb
183
223
  - lib/cobra_commander/component.rb
184
- - lib/cobra_commander/component_tree.rb
185
224
  - lib/cobra_commander/dependencies.rb
186
225
  - lib/cobra_commander/dependencies/bundler.rb
187
226
  - lib/cobra_commander/dependencies/yarn/package.rb
188
227
  - lib/cobra_commander/dependencies/yarn/package_repo.rb
189
228
  - lib/cobra_commander/dependencies/yarn_workspace.rb
190
229
  - lib/cobra_commander/executor.rb
191
- - lib/cobra_commander/graph.rb
230
+ - lib/cobra_commander/executor/component_exec.rb
231
+ - lib/cobra_commander/executor/multi_exec.rb
192
232
  - lib/cobra_commander/output.rb
233
+ - lib/cobra_commander/output/ascii_tree.rb
234
+ - lib/cobra_commander/output/flat_list.rb
235
+ - lib/cobra_commander/output/graph_viz.rb
193
236
  - lib/cobra_commander/umbrella.rb
194
237
  - lib/cobra_commander/version.rb
195
238
  - renovate.json