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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +38 -0
- data/.rubocop.yml +8 -38
- data/CHANGELOG.md +15 -0
- data/README.md +109 -10
- data/cobra_commander.gemspec +11 -9
- data/exe/cobra +1 -1
- data/lib/cobra_commander.rb +0 -9
- data/lib/cobra_commander/affected.rb +9 -1
- data/lib/cobra_commander/change.rb +4 -4
- data/lib/cobra_commander/cli.rb +65 -58
- data/lib/cobra_commander/component.rb +1 -1
- data/lib/cobra_commander/dependencies/bundler.rb +16 -12
- data/lib/cobra_commander/dependencies/yarn/package.rb +3 -2
- data/lib/cobra_commander/dependencies/yarn/package_repo.rb +3 -2
- data/lib/cobra_commander/executor.rb +12 -13
- data/lib/cobra_commander/executor/component_exec.rb +33 -0
- data/lib/cobra_commander/executor/multi_exec.rb +46 -0
- 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/umbrella.rb +1 -0
- data/lib/cobra_commander/version.rb +1 -1
- metadata +76 -33
- data/.travis.yml +0 -12
- 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
data/.travis.yml
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
sudo: true
|
2
|
-
language: ruby
|
3
|
-
before_install:
|
4
|
-
- "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
|
5
|
-
- gem install bundler:"$BUNDLER_VERSION"
|
6
|
-
- sudo apt-get -qq install graphviz
|
7
|
-
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0
|
8
|
-
- export PATH="$HOME/.yarn/bin:$PATH"
|
9
|
-
rvm:
|
10
|
-
- 2.5.1
|
11
|
-
env:
|
12
|
-
- BUNDLER_VERSION=1.17.3
|
@@ -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
|