cobra_commander 0.5.1 → 0.8.1

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.
@@ -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
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CobraCommander
4
+ # An umbrella application
5
+ class Umbrella
6
+ attr_reader :name, :path
7
+
8
+ def initialize(name, path)
9
+ @root_component = Component.new(self, name)
10
+ @path = path
11
+ @components = {}
12
+ end
13
+
14
+ def find(name)
15
+ @components[name]
16
+ end
17
+
18
+ def root
19
+ @root_component
20
+ end
21
+
22
+ def resolve(component_root_path)
23
+ return root if root.root_paths.include?(component_root_path)
24
+ components.find do |component|
25
+ component.root_paths.include?(component_root_path)
26
+ end
27
+ end
28
+
29
+ def add_source(key, source)
30
+ @root_component.add_source key, source.path, source.dependencies
31
+ source.components.each do |path:, name:, dependencies:|
32
+ @components[name] ||= Component.new(self, name)
33
+ @components[name].add_source key, path, dependencies
34
+ end
35
+ end
36
+
37
+ def components
38
+ @components.values
39
+ end
40
+
41
+ def dependents_of(component)
42
+ find(component)&.deep_dependents
43
+ &.sort_by(&:name)
44
+ end
45
+
46
+ def dependencies_of(name)
47
+ find(name)&.deep_dependencies
48
+ &.sort_by(&:name)
49
+ end
50
+ end
51
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CobraCommander
4
- VERSION = "0.5.1"
4
+ VERSION = "0.8.1"
5
5
  end
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": [
3
+ "config:base"
4
+ ],
5
+ "ignorePaths": [
6
+ "spec/fixtures"
7
+ ]
8
+ }
metadata CHANGED
@@ -1,16 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cobra_commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
8
8
  - Garett Arrowood
9
+ - Carlos Palhares
9
10
  autorequire:
10
11
  bindir: exe
11
12
  cert_chain: []
12
- date: 2018-10-16 00:00:00.000000000 Z
13
+ date: 2020-07-29 00:00:00.000000000 Z
13
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'
14
29
  - !ruby/object:Gem::Dependency
15
30
  name: thor
16
31
  requirement: !ruby/object:Gem::Requirement
@@ -51,14 +66,14 @@ dependencies:
51
66
  requirements:
52
67
  - - "~>"
53
68
  - !ruby/object:Gem::Version
54
- version: '1.13'
69
+ version: '1.17'
55
70
  type: :development
56
71
  prerelease: false
57
72
  version_requirements: !ruby/object:Gem::Requirement
58
73
  requirements:
59
74
  - - "~>"
60
75
  - !ruby/object:Gem::Version
61
- version: '1.13'
76
+ version: '1.17'
62
77
  - !ruby/object:Gem::Dependency
63
78
  name: rake
64
79
  requirement: !ruby/object:Gem::Requirement
@@ -150,11 +165,14 @@ description: |
150
165
  email:
151
166
  - blangfeld@powerhrg.com
152
167
  - garett.arrowood@powerhrg.com
168
+ - carlos.palhares@powerhrg.com
153
169
  executables:
154
170
  - cobra
155
171
  extensions: []
156
172
  extra_rdoc_files: []
157
173
  files:
174
+ - ".editorconfig"
175
+ - ".github/workflows/ci.yml"
158
176
  - ".gitignore"
159
177
  - ".rspec"
160
178
  - ".rubocop.yml"
@@ -175,11 +193,20 @@ files:
175
193
  - lib/cobra_commander/affected.rb
176
194
  - lib/cobra_commander/change.rb
177
195
  - lib/cobra_commander/cli.rb
178
- - lib/cobra_commander/component_tree.rb
196
+ - lib/cobra_commander/component.rb
197
+ - lib/cobra_commander/dependencies.rb
198
+ - lib/cobra_commander/dependencies/bundler.rb
199
+ - lib/cobra_commander/dependencies/yarn/package.rb
200
+ - lib/cobra_commander/dependencies/yarn/package_repo.rb
201
+ - lib/cobra_commander/dependencies/yarn_workspace.rb
179
202
  - lib/cobra_commander/executor.rb
180
- - lib/cobra_commander/graph.rb
181
203
  - lib/cobra_commander/output.rb
204
+ - lib/cobra_commander/output/ascii_tree.rb
205
+ - lib/cobra_commander/output/flat_list.rb
206
+ - lib/cobra_commander/output/graph_viz.rb
207
+ - lib/cobra_commander/umbrella.rb
182
208
  - lib/cobra_commander/version.rb
209
+ - renovate.json
183
210
  homepage: http://tech.powerhrg.com/cobra_commander/
184
211
  licenses:
185
212
  - MIT
@@ -1,170 +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, 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)
40
- end
41
-
42
- def to_h
43
- {
44
- name: @name,
45
- path: path,
46
- type: @type,
47
- ancestry: @ancestry,
48
- dependencies: dependencies.map(&:to_h),
49
- }
50
- end
51
-
52
- def dependencies
53
- @deps ||= begin
54
- deps = @ruby.dependencies + @js.dependencies
55
- deps.sort_by { |dep| dep[:name] }
56
- .map(&method(:dep_representation))
57
- end
58
- end
59
-
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
- presence = _subtree(name, component)
72
- return presence if presence
73
- end
74
- nil
75
- end
76
-
77
- def type_of_component
78
- return "Ruby & JS" if @ruby.gem? && @js.node?
79
- return "Ruby" if @ruby.gem?
80
- return "JS" if @js.node?
81
- end
82
-
83
- def dep_representation(dep)
84
- full_path = File.expand_path(File.join(path, dep[:path]))
85
- ancestry = @ancestry + [{ name: @name, path: path, type: @type }]
86
- ComponentTree.new(dep[:name], full_path, ancestry)
87
- end
88
-
89
- # Calculates ruby dependencies
90
- class Ruby
91
- def initialize(root_path)
92
- @root_path = root_path
93
- end
94
-
95
- def dependencies
96
- @deps ||= begin
97
- return [] unless gem?
98
- gems = bundler_definition.dependencies.select { |dep| path?(dep.source) }
99
- format(gems)
100
- end
101
- end
102
-
103
- def path?(source)
104
- return if source.nil?
105
- source_has_path = source.respond_to?(:path?) ? source.path? : source.is_a_path?
106
- source_has_path && source.path.to_s != "."
107
- end
108
-
109
- def format(deps)
110
- deps.map do |dep|
111
- path = File.join(dep.source.path, dep.name)
112
- { name: dep.name, path: path }
113
- end
114
- end
115
-
116
- def gem?
117
- @gem ||= File.exist?(gemfile_path)
118
- end
119
-
120
- def bundler_definition
121
- ::Bundler::Definition.build(gemfile_path, gemfile_lock_path, nil)
122
- end
123
-
124
- def gemfile_path
125
- File.join(@root_path, "Gemfile")
126
- end
127
-
128
- def gemfile_lock_path
129
- File.join(@root_path, "Gemfile.lock")
130
- end
131
- end
132
-
133
- # Calculates js dependencies
134
- class Js
135
- def initialize(root_path)
136
- @root_path = root_path
137
- end
138
-
139
- def dependencies
140
- @deps ||= begin
141
- return [] unless node?
142
- json = JSON.parse(File.read(package_json_path))
143
- format combined_deps(json)
144
- end
145
- end
146
-
147
- def format(deps)
148
- return [] if deps.nil?
149
- linked_deps = deps.select { |_, v| v.start_with? "link:" }
150
- linked_deps.map do |_, v|
151
- relational_path = v.split("link:")[1]
152
- dep_name = relational_path.split("/")[-1]
153
- { name: dep_name, path: relational_path }
154
- end
155
- end
156
-
157
- def node?
158
- @node ||= File.exist?(package_json_path)
159
- end
160
-
161
- def package_json_path
162
- File.join(@root_path, "package.json")
163
- end
164
-
165
- def combined_deps(json)
166
- Hash(json["dependencies"]).merge(Hash(json["devDependencies"]))
167
- end
168
- end
169
- end
170
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "graphviz"
4
- require "cobra_commander/component_tree"
5
-
6
- module CobraCommander
7
- # Generates graphs of components
8
- class Graph
9
- def initialize(app_path, format)
10
- @format = format
11
- @tree = CobraCommander.umbrella_tree(app_path).to_h
12
- end
13
-
14
- def generate!
15
- return unless valid_format?
16
-
17
- g = GraphViz.new(:G, type: :digraph, concentrate: true)
18
-
19
- app_node = g.add_nodes(@tree[:name])
20
- map_nodes(g, app_node, @tree)
21
- output(g)
22
- end
23
-
24
- private
25
-
26
- def map_nodes(g, parent_node, tree)
27
- tree[:dependencies].each do |dep|
28
- node = g.find_node(dep[:name]) || g.add_nodes(dep[:name])
29
- g.add_edges(parent_node, node)
30
- map_nodes(g, node, dep)
31
- end
32
- end
33
-
34
- def output(g)
35
- graph = "graph.#{@format}"
36
- g.output(@format => graph)
37
- puts "Graph generated at #{`pwd`.chomp}/#{graph}"
38
- end
39
-
40
- def valid_format?
41
- return true if @format == "png" || @format == "dot"
42
- puts "FORMAT must be 'png' or 'dot'"
43
- false
44
- end
45
- end
46
- end