cobra_commander 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +11 -0
- data/.travis.yml +2 -3
- data/CHANGELOG.md +5 -0
- data/cobra_commander.gemspec +3 -1
- data/lib/cobra_commander.rb +11 -0
- data/lib/cobra_commander/affected.rb +37 -30
- data/lib/cobra_commander/calculated_component_tree.rb +2 -2
- data/lib/cobra_commander/change.rb +6 -6
- data/lib/cobra_commander/cli.rb +17 -34
- data/lib/cobra_commander/component.rb +52 -0
- data/lib/cobra_commander/dependencies.rb +4 -0
- data/lib/cobra_commander/dependencies/bundler.rb +38 -0
- data/lib/cobra_commander/dependencies/yarn/package.rb +37 -0
- data/lib/cobra_commander/dependencies/yarn/package_repo.rb +31 -0
- data/lib/cobra_commander/dependencies/yarn_workspace.rb +55 -0
- data/lib/cobra_commander/executor.rb +8 -18
- data/lib/cobra_commander/graph.rb +6 -14
- data/lib/cobra_commander/output.rb +10 -10
- data/lib/cobra_commander/umbrella.rb +51 -0
- data/lib/cobra_commander/version.rb +1 -1
- data/renovate.json +8 -0
- metadata +23 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7155830962a165a9d34da676e053580b6a375543c0823cf89ce72db41b8fdc7e
|
4
|
+
data.tar.gz: db6fd674597394e4c5ee1f8e42b42d9e24305de678a9043f17ed026238f6edcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee123f0d3b655a028e76e991fd3c7c0d411f9ece583965f9f4547a6356cce527049b41e90d665b18c433d5c8a293e43726c9a5b3fa822fb0a433d08b23df8f6e
|
7
|
+
data.tar.gz: 3f9514a4c74710643361ac3d3950e3f3d3b72e864b44c6f5addfc35997ae99a779bf07d5382cb38fa424a9d859de741d7836cd93e57624e55d044d8c0a0cfc0a
|
data/.editorconfig
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# EditorConfig is awesome: https://EditorConfig.org
|
2
|
+
# top-most EditorConfig file
|
3
|
+
root = true
|
4
|
+
|
5
|
+
# Unix-style newlines with a newline ending every file
|
6
|
+
[*]
|
7
|
+
end_of_line = lf
|
8
|
+
insert_final_newline = true
|
9
|
+
trim_trailing_whitespace = true
|
10
|
+
indent_style = space
|
11
|
+
indent_size = 2
|
data/.travis.yml
CHANGED
@@ -4,10 +4,9 @@ before_install:
|
|
4
4
|
- "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
|
5
5
|
- gem install bundler:"$BUNDLER_VERSION"
|
6
6
|
- sudo apt-get -qq install graphviz
|
7
|
-
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0
|
7
|
+
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0
|
8
8
|
- export PATH="$HOME/.yarn/bin:$PATH"
|
9
9
|
rvm:
|
10
10
|
- 2.5.1
|
11
11
|
env:
|
12
|
-
- BUNDLER_VERSION=1.
|
13
|
-
- BUNDLER_VERSION=1.16.3
|
12
|
+
- BUNDLER_VERSION=1.17.3
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## Version 0.7 - 2020-07-08
|
6
|
+
|
7
|
+
* Introduces CobraCommander::Umbrella with optimizations for dependency resolution
|
8
|
+
* Deprecates cobra cache and cache usages
|
9
|
+
|
5
10
|
## Version 0.6.1 - 2019-07-05
|
6
11
|
|
7
12
|
* Better supports yarn workspaces globbing by delegating to yarn to calculate the list of components rather than re-implementing in Ruby. PR [#34](https://github.com/powerhome/cobra_commander/pull/34)
|
data/cobra_commander.gemspec
CHANGED
@@ -11,10 +11,12 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.authors = [
|
12
12
|
"Ben Langfeld",
|
13
13
|
"Garett Arrowood",
|
14
|
+
"Carlos Palhares",
|
14
15
|
]
|
15
16
|
spec.email = [
|
16
17
|
"blangfeld@powerhrg.com",
|
17
18
|
"garett.arrowood@powerhrg.com",
|
19
|
+
"carlos.palhares@powerhrg.com",
|
18
20
|
]
|
19
21
|
spec.summary = "Tools for working with Component Based Rails Apps"
|
20
22
|
spec.description = <<~DESCRIPTION
|
@@ -35,7 +37,7 @@ DESCRIPTION
|
|
35
37
|
spec.add_dependency "thor", ["< 2.0", ">= 0.18.1"]
|
36
38
|
spec.add_dependency "ruby-graphviz", "~> 1.2.3"
|
37
39
|
|
38
|
-
spec.add_development_dependency "bundler", "~> 1.
|
40
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
39
41
|
spec.add_development_dependency "rake", "~> 10.0"
|
40
42
|
spec.add_development_dependency "rspec", "~> 3.5"
|
41
43
|
spec.add_development_dependency "guard-rspec"
|
data/lib/cobra_commander.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "cobra_commander/dependencies"
|
4
|
+
require "cobra_commander/component"
|
5
|
+
require "cobra_commander/umbrella"
|
6
|
+
|
3
7
|
require "cobra_commander/cli"
|
4
8
|
require "cobra_commander/cached_component_tree"
|
5
9
|
require "cobra_commander/calculated_component_tree"
|
@@ -16,6 +20,13 @@ require "cobra_commander/executor"
|
|
16
20
|
module CobraCommander
|
17
21
|
UMBRELLA_APP_NAME = "App"
|
18
22
|
|
23
|
+
def self.umbrella(root_path, yarn: false, bundler: false, name: UMBRELLA_APP_NAME)
|
24
|
+
umbrella = Umbrella.new(name, root_path)
|
25
|
+
umbrella.add_source(:yarn, Dependencies::YarnWorkspace.new(root_path)) unless bundler
|
26
|
+
umbrella.add_source(:bundler, Dependencies::Bundler.new(root_path)) unless yarn
|
27
|
+
umbrella
|
28
|
+
end
|
29
|
+
|
19
30
|
def self.umbrella_tree(path)
|
20
31
|
CalculatedComponentTree.new(UMBRELLA_APP_NAME, path)
|
21
32
|
end
|
@@ -3,28 +3,33 @@
|
|
3
3
|
module CobraCommander
|
4
4
|
# Calculates directly & transitively affected components
|
5
5
|
class Affected
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(tree, changes, path)
|
9
|
-
@tree = tree
|
6
|
+
def initialize(umbrella, changes)
|
7
|
+
@umbrella = umbrella
|
10
8
|
@changes = changes
|
11
|
-
@path = path
|
12
9
|
run!
|
13
10
|
end
|
14
11
|
|
15
12
|
def names
|
16
|
-
@names ||=
|
13
|
+
@names ||= all_affected.map(&:name)
|
17
14
|
end
|
18
15
|
|
19
16
|
def scripts
|
20
17
|
@scripts ||= paths.map { |path| File.join(path, "test.sh") }
|
21
18
|
end
|
22
19
|
|
20
|
+
def directly
|
21
|
+
@directly.map(&method(:affected_component))
|
22
|
+
end
|
23
|
+
|
24
|
+
def transitively
|
25
|
+
@transitively.map(&method(:affected_component))
|
26
|
+
end
|
27
|
+
|
23
28
|
def json_representation # rubocop:disable Metrics/MethodLength
|
24
29
|
{
|
25
30
|
changed_files: @changes,
|
26
|
-
directly_affected_components:
|
27
|
-
transitively_affected_components:
|
31
|
+
directly_affected_components: directly,
|
32
|
+
transitively_affected_components: transitively,
|
28
33
|
test_scripts: scripts,
|
29
34
|
component_names: names,
|
30
35
|
languages: {
|
@@ -39,46 +44,48 @@ module CobraCommander
|
|
39
44
|
def run!
|
40
45
|
@transitively = Set.new
|
41
46
|
@directly = Set.new
|
42
|
-
|
43
|
-
@transitively.
|
44
|
-
@
|
45
|
-
@directly = @directly.to_a.sort_by { |h| h[:name] }
|
47
|
+
@umbrella.components.each(&method(:add_if_changed))
|
48
|
+
@transitively = @transitively.sort_by(&:name)
|
49
|
+
@directly = @directly.sort_by(&:name)
|
46
50
|
end
|
47
51
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
def add_if_changed(component)
|
53
|
+
return if component.root_paths.uniq.none? { |path| @changes.any?(Regexp.new(path)) }
|
54
|
+
|
55
|
+
@directly << component
|
56
|
+
@transitively.merge(component.deep_dependents)
|
53
57
|
end
|
54
58
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
59
|
+
def affected_component(component)
|
60
|
+
{
|
61
|
+
name: component.name,
|
62
|
+
path: component.root_paths,
|
63
|
+
type: component.sources.keys.map(&:to_s).map(&:capitalize).join(" & "),
|
64
|
+
}
|
62
65
|
end
|
63
66
|
|
64
67
|
def all_affected
|
65
|
-
@all_affected ||= (@directly
|
68
|
+
@all_affected ||= (@directly | @transitively).sort_by(&:name)
|
66
69
|
end
|
67
70
|
|
68
71
|
def paths
|
69
|
-
@paths ||= all_affected.map
|
72
|
+
@paths ||= all_affected.map(&:root_paths).flatten.uniq
|
70
73
|
end
|
71
74
|
|
72
|
-
def
|
73
|
-
|
75
|
+
def all_affected_sources
|
76
|
+
all_affected
|
77
|
+
.map(&:sources)
|
78
|
+
.map(&:keys)
|
79
|
+
.flatten
|
80
|
+
.uniq
|
74
81
|
end
|
75
82
|
|
76
83
|
def contains_ruby?
|
77
|
-
|
84
|
+
all_affected_sources.include?(:bundler)
|
78
85
|
end
|
79
86
|
|
80
87
|
def contains_js?
|
81
|
-
|
88
|
+
all_affected_sources.include?(:yarn)
|
82
89
|
end
|
83
90
|
end
|
84
91
|
end
|
@@ -132,8 +132,8 @@ module CobraCommander
|
|
132
132
|
|
133
133
|
def yarn_workspaces
|
134
134
|
@yarn_workspaces ||= begin
|
135
|
-
output, = Open3.capture2("yarn workspaces info
|
136
|
-
JSON.parse(output)
|
135
|
+
output, = Open3.capture2("yarn --json workspaces info", chdir: @root_path)
|
136
|
+
JSON.parse(JSON.parse(output)["data"])
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -8,12 +8,12 @@ module CobraCommander
|
|
8
8
|
class Change
|
9
9
|
InvalidSelectionError = Class.new(StandardError)
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@root_dir = Dir.chdir(
|
11
|
+
def initialize(umbrella, results, branch)
|
12
|
+
@root_dir = Dir.chdir(umbrella.path) { `git rev-parse --show-toplevel`.chomp }
|
13
13
|
@results = results
|
14
14
|
@branch = branch
|
15
|
-
@
|
16
|
-
@affected = Affected.new(@
|
15
|
+
@umbrella = umbrella
|
16
|
+
@affected = Affected.new(@umbrella, changes)
|
17
17
|
end
|
18
18
|
|
19
19
|
def run!
|
@@ -85,8 +85,8 @@ module CobraCommander
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
def display(
|
89
|
-
"#{
|
88
|
+
def display(name:, type:, **)
|
89
|
+
"#{name} - #{type}"
|
90
90
|
end
|
91
91
|
|
92
92
|
def blank_line
|
data/lib/cobra_commander/cli.rb
CHANGED
@@ -6,17 +6,16 @@ require "fileutils"
|
|
6
6
|
module CobraCommander
|
7
7
|
# Implements the tool's CLI
|
8
8
|
class CLI < Thor
|
9
|
-
CACHE_DESCRIPTION = "Path to a cache file to use (default: nil). If specified, this file will
|
10
|
-
"the component tree for the app to speed up subsequent invocations. Must be rotated any time
|
11
|
-
"dependency structure changes."
|
9
|
+
CACHE_DESCRIPTION = "[DEPRECATED] Path to a cache file to use (default: nil). If specified, this file will " \
|
10
|
+
"be used to store the component tree for the app to speed up subsequent invocations. Must be rotated any time " \
|
11
|
+
"the component dependency structure changes."
|
12
12
|
COMMON_OPTIONS = "[--app=pwd] [--format=FORMAT] [--cache=nil]"
|
13
13
|
|
14
14
|
desc "do [command] [--app=pwd] [--cache=nil]", "Executes the command in the context of each component in [app]"
|
15
15
|
method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
|
16
16
|
method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
|
17
17
|
def do(command)
|
18
|
-
|
19
|
-
executor = Executor.new(tree)
|
18
|
+
executor = Executor.new(umbrella(options.app).components)
|
20
19
|
executor.exec(command)
|
21
20
|
end
|
22
21
|
|
@@ -24,10 +23,9 @@ module CobraCommander
|
|
24
23
|
method_option :app, default: Dir.pwd, aliases: "-a", desc: "App path (default: CWD)"
|
25
24
|
method_option :format, default: "tree", aliases: "-f", desc: "Format (list or tree, default: list)"
|
26
25
|
method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
|
27
|
-
def ls(app_path =
|
28
|
-
tree = maybe_cached_tree(app_path || options.app, options.cache)
|
26
|
+
def ls(app_path = Dir.pwd)
|
29
27
|
Output.print(
|
30
|
-
|
28
|
+
umbrella(app_path).root,
|
31
29
|
options.format
|
32
30
|
)
|
33
31
|
end
|
@@ -37,8 +35,8 @@ module CobraCommander
|
|
37
35
|
method_option :format, default: "count", aliases: "-f", desc: "count or list"
|
38
36
|
method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
|
39
37
|
def dependents_of(component)
|
40
|
-
|
41
|
-
|
38
|
+
dependents = umbrella(options.app).dependents_of(component)
|
39
|
+
return unless dependents
|
42
40
|
puts "list" == options.format ? dependents.map(&:name) : dependents.size
|
43
41
|
end
|
44
42
|
|
@@ -47,9 +45,8 @@ module CobraCommander
|
|
47
45
|
method_option :format, default: "list", aliases: "-f", desc: "Format (list or tree, default: list)"
|
48
46
|
method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
|
49
47
|
def dependencies_of(component)
|
50
|
-
tree = maybe_cached_tree(options.app, options.cache)
|
51
48
|
Output.print(
|
52
|
-
|
49
|
+
umbrella(options.app).find(component),
|
53
50
|
options.format
|
54
51
|
)
|
55
52
|
end
|
@@ -59,12 +56,11 @@ module CobraCommander
|
|
59
56
|
puts CobraCommander::VERSION
|
60
57
|
end
|
61
58
|
|
62
|
-
desc "graph APP_PATH [--format=FORMAT] [--cache=nil]", "Outputs graph"
|
59
|
+
desc "graph APP_PATH [--format=FORMAT] [--cache=nil] [--component]", "Outputs graph"
|
63
60
|
method_option :format, default: "png", aliases: "-f", desc: "Accepts png or dot"
|
64
61
|
method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
|
65
62
|
def graph(app_path)
|
66
|
-
|
67
|
-
Graph.new(tree, options.format).generate!
|
63
|
+
Graph.new(umbrella(app_path).root, options.format).generate!
|
68
64
|
end
|
69
65
|
|
70
66
|
desc "changes APP_PATH [--results=RESULTS] [--branch=BRANCH] [--cache=nil]", "Prints list of changed files"
|
@@ -72,34 +68,21 @@ module CobraCommander
|
|
72
68
|
method_option :branch, default: "master", aliases: "-b", desc: "Specified target to calculate against"
|
73
69
|
method_option :cache, default: nil, aliases: "-c", desc: CACHE_DESCRIPTION
|
74
70
|
def changes(app_path)
|
75
|
-
|
76
|
-
Change.new(tree, options.results, options.branch).run!
|
71
|
+
Change.new(umbrella(app_path), options.results, options.branch).run!
|
77
72
|
end
|
78
73
|
|
79
|
-
desc "cache APP_PATH CACHE_PATH", "Caches a representation of the component structure of the app"
|
74
|
+
desc "cache APP_PATH CACHE_PATH", "[DEPRECATED] Caches a representation of the component structure of the app"
|
80
75
|
def cache(app_path, cache_path)
|
81
76
|
tree = CobraCommander.umbrella_tree(app_path)
|
82
|
-
|
77
|
+
FileUtils.mkdir_p(File.dirname(cache_path))
|
78
|
+
File.write(cache_path, tree.to_json)
|
83
79
|
puts "Created cache of component tree at #{cache_path}"
|
84
80
|
end
|
85
81
|
|
86
82
|
private
|
87
83
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
if File.exist?(cache_path)
|
92
|
-
CobraCommander.tree_from_cache(cache_path)
|
93
|
-
else
|
94
|
-
tree = CobraCommander.umbrella_tree(app_path)
|
95
|
-
write_tree_cache(tree, cache_path)
|
96
|
-
tree
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def write_tree_cache(tree, cache_path)
|
101
|
-
FileUtils.mkdir_p(File.dirname(cache_path))
|
102
|
-
File.write(cache_path, tree.to_json)
|
84
|
+
def umbrella(path)
|
85
|
+
CobraCommander.umbrella(path)
|
103
86
|
end
|
104
87
|
end
|
105
88
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
# Represents a component withing an Umbrella
|
5
|
+
class Component
|
6
|
+
attr_reader :name, :dependencies, :sources
|
7
|
+
|
8
|
+
def initialize(umbrella, name)
|
9
|
+
@umbrella = umbrella
|
10
|
+
@name = name
|
11
|
+
@dependency_names = []
|
12
|
+
@sources = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_source(key, path, dependency_names)
|
16
|
+
@sources[key] = path
|
17
|
+
@dependency_names |= dependency_names
|
18
|
+
end
|
19
|
+
|
20
|
+
def root_paths
|
21
|
+
@sources.values.map(&File.method(:dirname)).uniq
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"#<CobraCommander::Component:#{object_id} #{name} dependencies=#{dependencies.map(&:name)} packages=#{sources}>"
|
26
|
+
end
|
27
|
+
|
28
|
+
def deep_dependents
|
29
|
+
@deep_dependents ||= @umbrella.components.find_all do |dep|
|
30
|
+
dep.deep_dependencies.include?(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def deep_dependencies
|
35
|
+
@deep_dependencies ||= dependencies.reduce(dependencies) do |deps, dep|
|
36
|
+
deps | dep.deep_dependencies
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def dependents
|
41
|
+
@dependents ||= @umbrella.components.find_all do |dep|
|
42
|
+
dep.dependencies.include?(self)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def dependencies
|
47
|
+
@dependencies ||= @dependency_names.sort
|
48
|
+
.map(&@umbrella.method(:find))
|
49
|
+
.compact
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
module Dependencies
|
5
|
+
# Calculates ruby bundler dependencies
|
6
|
+
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
|
14
|
+
|
15
|
+
def path
|
16
|
+
@definition.lockfile
|
17
|
+
end
|
18
|
+
|
19
|
+
def dependencies
|
20
|
+
@definition.dependencies.map(&:name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def components
|
24
|
+
components_source.specs.map do |spec|
|
25
|
+
{ path: spec.loaded_from, name: spec.name, dependencies: spec.dependencies.map(&:name) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def components_source
|
32
|
+
@components_source ||= @definition.send(:sources).path_sources.find do |source|
|
33
|
+
source.path.to_s.eql?("components")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module CobraCommander
|
6
|
+
module Dependencies
|
7
|
+
module Yarn
|
8
|
+
# Represents an Yarn package.json file
|
9
|
+
class Package
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
def initialize(path)
|
13
|
+
@path = Pathname.new(File.join(path, "package.json")).realpath
|
14
|
+
end
|
15
|
+
|
16
|
+
def project_tag
|
17
|
+
name.match(%r{^@[\w-]+\/}).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
json["name"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def dependencies
|
25
|
+
json.fetch("dependencies", {})
|
26
|
+
.merge(json.fetch("devDependencies", {}))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def json
|
32
|
+
@json ||= JSON.parse(File.read(@path))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
module Dependencies
|
5
|
+
module Yarn
|
6
|
+
# Yarn package repository to load and cache package.json files
|
7
|
+
class PackageRepo
|
8
|
+
def initialize
|
9
|
+
@specs ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def specs
|
13
|
+
@specs.values
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_linked_specs(package)
|
17
|
+
package.dependencies.values.each do |spec|
|
18
|
+
next unless spec =~ /link:(.+)/
|
19
|
+
load_spec(File.join(package.path, "..", Regexp.last_match(1)))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_spec(path)
|
24
|
+
@specs[path] ||= Package.new(path).tap do |package|
|
25
|
+
load_linked_specs(package)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
require_relative "yarn/package"
|
6
|
+
require_relative "yarn/package_repo"
|
7
|
+
|
8
|
+
module CobraCommander
|
9
|
+
module Dependencies
|
10
|
+
# Yarn workspace components source for an umbrella
|
11
|
+
class YarnWorkspace
|
12
|
+
attr_reader :packages
|
13
|
+
|
14
|
+
def initialize(root_path)
|
15
|
+
@repo = Yarn::PackageRepo.new
|
16
|
+
@root_package = Yarn::Package.new(root_path)
|
17
|
+
@repo.load_linked_specs(@root_package)
|
18
|
+
load_workspace_packages
|
19
|
+
end
|
20
|
+
|
21
|
+
def path
|
22
|
+
@root_package.path
|
23
|
+
end
|
24
|
+
|
25
|
+
def dependencies
|
26
|
+
(workspace_spec.keys | @root_package.dependencies.keys).map(&method(:untag))
|
27
|
+
end
|
28
|
+
|
29
|
+
def components
|
30
|
+
@repo.specs.map do |spec|
|
31
|
+
{ path: spec.path, name: untag(spec.name), dependencies: spec.dependencies.keys.map(&method(:untag)) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def load_workspace_packages
|
38
|
+
workspace_spec.map do |_name, spec|
|
39
|
+
@repo.load_spec File.expand_path(File.join(@root_package.path, "..", spec["location"]))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def workspace_spec
|
44
|
+
@workspace_spec ||= begin
|
45
|
+
output, = Open3.capture2("yarn workspaces --json info", chdir: File.dirname(@root_package.path))
|
46
|
+
JSON.parse(JSON.parse(output)["data"])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def untag(name)
|
51
|
+
name.gsub(@root_package.project_tag, "")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -3,28 +3,18 @@
|
|
3
3
|
module CobraCommander
|
4
4
|
# Execute commands on all components of a ComponentTree
|
5
5
|
class Executor
|
6
|
-
def initialize(
|
7
|
-
@
|
6
|
+
def initialize(components)
|
7
|
+
@components = components
|
8
8
|
end
|
9
9
|
|
10
10
|
def exec(command, printer = $stdout)
|
11
|
-
@
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
15
17
|
end
|
16
18
|
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
19
|
end
|
30
20
|
end
|
@@ -5,31 +5,23 @@ require "graphviz"
|
|
5
5
|
module CobraCommander
|
6
6
|
# Generates graphs of components
|
7
7
|
class Graph
|
8
|
-
def initialize(
|
8
|
+
def initialize(node, format)
|
9
9
|
@format = format
|
10
|
-
@
|
10
|
+
@node = node
|
11
11
|
end
|
12
12
|
|
13
13
|
def generate!
|
14
14
|
return unless valid_format?
|
15
15
|
|
16
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
|
17
21
|
|
18
|
-
app_node = g.add_nodes(@tree[:name])
|
19
|
-
map_nodes(g, app_node, @tree)
|
20
22
|
output(g)
|
21
23
|
end
|
22
24
|
|
23
|
-
private
|
24
|
-
|
25
|
-
def map_nodes(g, parent_node, tree)
|
26
|
-
tree[:dependencies].each do |dep|
|
27
|
-
node = g.find_node(dep[:name]) || g.add_nodes(dep[:name])
|
28
|
-
g.add_edges(parent_node, node)
|
29
|
-
map_nodes(g, node, dep)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
25
|
def output(g)
|
34
26
|
graph = "graph.#{@format}"
|
35
27
|
g.output(@format => graph)
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module CobraCommander
|
4
4
|
# Module for pretty printing dependency trees
|
5
5
|
module Output
|
6
|
-
def self.print(
|
7
|
-
output = format == "list" ? Output::FlatList.new(
|
6
|
+
def self.print(node, format)
|
7
|
+
output = format == "list" ? Output::FlatList.new(node) : Output::Tree.new(node)
|
8
8
|
puts output.to_s
|
9
9
|
end
|
10
10
|
|
@@ -15,7 +15,7 @@ module CobraCommander
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_s
|
18
|
-
@tree.
|
18
|
+
@tree.deep_dependencies.map(&:name).sort
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -28,22 +28,22 @@ module CobraCommander
|
|
28
28
|
TEE = "├── "
|
29
29
|
CORNER = "└── "
|
30
30
|
|
31
|
-
def initialize(
|
32
|
-
@
|
31
|
+
def initialize(node)
|
32
|
+
@node = node
|
33
33
|
end
|
34
34
|
|
35
35
|
def to_s
|
36
36
|
StringIO.new.tap do |io|
|
37
|
-
io.puts @
|
38
|
-
list_dependencies(io, @
|
37
|
+
io.puts @node.name
|
38
|
+
list_dependencies(io, @node)
|
39
39
|
end.string
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
def list_dependencies(io,
|
45
|
-
|
46
|
-
decide_on_line(io,
|
44
|
+
def list_dependencies(io, node, outdents = [])
|
45
|
+
node.dependencies.each do |dep|
|
46
|
+
decide_on_line(io, node, dep, outdents)
|
47
47
|
end
|
48
48
|
nil
|
49
49
|
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
|
data/renovate.json
ADDED
metadata
CHANGED
@@ -1,36 +1,37 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cobra_commander
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
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:
|
13
|
+
date: 2020-07-08 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: thor
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: 0.18.1
|
21
19
|
- - "<"
|
22
20
|
- !ruby/object:Gem::Version
|
23
21
|
version: '2.0'
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.18.1
|
24
25
|
type: :runtime
|
25
26
|
prerelease: false
|
26
27
|
version_requirements: !ruby/object:Gem::Requirement
|
27
28
|
requirements:
|
28
|
-
- - ">="
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: 0.18.1
|
31
29
|
- - "<"
|
32
30
|
- !ruby/object:Gem::Version
|
33
31
|
version: '2.0'
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.18.1
|
34
35
|
- !ruby/object:Gem::Dependency
|
35
36
|
name: ruby-graphviz
|
36
37
|
requirement: !ruby/object:Gem::Requirement
|
@@ -51,14 +52,14 @@ dependencies:
|
|
51
52
|
requirements:
|
52
53
|
- - "~>"
|
53
54
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
55
|
+
version: '1.17'
|
55
56
|
type: :development
|
56
57
|
prerelease: false
|
57
58
|
version_requirements: !ruby/object:Gem::Requirement
|
58
59
|
requirements:
|
59
60
|
- - "~>"
|
60
61
|
- !ruby/object:Gem::Version
|
61
|
-
version: '1.
|
62
|
+
version: '1.17'
|
62
63
|
- !ruby/object:Gem::Dependency
|
63
64
|
name: rake
|
64
65
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,11 +151,13 @@ description: |
|
|
150
151
|
email:
|
151
152
|
- blangfeld@powerhrg.com
|
152
153
|
- garett.arrowood@powerhrg.com
|
154
|
+
- carlos.palhares@powerhrg.com
|
153
155
|
executables:
|
154
156
|
- cobra
|
155
157
|
extensions: []
|
156
158
|
extra_rdoc_files: []
|
157
159
|
files:
|
160
|
+
- ".editorconfig"
|
158
161
|
- ".gitignore"
|
159
162
|
- ".rspec"
|
160
163
|
- ".rubocop.yml"
|
@@ -177,11 +180,19 @@ files:
|
|
177
180
|
- lib/cobra_commander/calculated_component_tree.rb
|
178
181
|
- lib/cobra_commander/change.rb
|
179
182
|
- lib/cobra_commander/cli.rb
|
183
|
+
- lib/cobra_commander/component.rb
|
180
184
|
- lib/cobra_commander/component_tree.rb
|
185
|
+
- lib/cobra_commander/dependencies.rb
|
186
|
+
- lib/cobra_commander/dependencies/bundler.rb
|
187
|
+
- lib/cobra_commander/dependencies/yarn/package.rb
|
188
|
+
- lib/cobra_commander/dependencies/yarn/package_repo.rb
|
189
|
+
- lib/cobra_commander/dependencies/yarn_workspace.rb
|
181
190
|
- lib/cobra_commander/executor.rb
|
182
191
|
- lib/cobra_commander/graph.rb
|
183
192
|
- lib/cobra_commander/output.rb
|
193
|
+
- lib/cobra_commander/umbrella.rb
|
184
194
|
- lib/cobra_commander/version.rb
|
195
|
+
- renovate.json
|
185
196
|
homepage: http://tech.powerhrg.com/cobra_commander/
|
186
197
|
licenses:
|
187
198
|
- MIT
|
@@ -201,7 +212,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
212
|
- !ruby/object:Gem::Version
|
202
213
|
version: '0'
|
203
214
|
requirements: []
|
204
|
-
|
215
|
+
rubyforge_project:
|
216
|
+
rubygems_version: 2.7.3
|
205
217
|
signing_key:
|
206
218
|
specification_version: 4
|
207
219
|
summary: Tools for working with Component Based Rails Apps
|