cobra_commander 0.10.0 → 0.14.0
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 +9 -10
- data/.github/workflows/release.yml +20 -0
- data/CHANGELOG.md +36 -1
- data/README.md +3 -4
- data/Rakefile +3 -5
- data/cobra_commander.gemspec +17 -15
- data/lib/cobra_commander/affected.rb +4 -7
- data/lib/cobra_commander/change.rb +3 -3
- data/lib/cobra_commander/cli/filters.rb +41 -0
- data/lib/cobra_commander/cli.rb +24 -27
- data/lib/cobra_commander/component.rb +2 -2
- data/lib/cobra_commander/dependencies/bundler.rb +2 -2
- data/lib/cobra_commander/dependencies/yarn/package.rb +2 -2
- data/lib/cobra_commander/dependencies/yarn_workspace.rb +2 -2
- data/lib/cobra_commander/executor/concurrent.rb +51 -0
- data/lib/cobra_commander/executor/context.rb +49 -0
- data/lib/cobra_commander/executor.rb +6 -11
- data/lib/cobra_commander/output/ascii_tree.rb +6 -4
- data/lib/cobra_commander/output/graph_viz.rb +1 -1
- data/lib/cobra_commander/output/interactive_printer.rb +53 -0
- data/lib/cobra_commander/output/markdown_printer.rb +24 -0
- data/lib/cobra_commander/output.rb +2 -0
- data/lib/cobra_commander/umbrella.rb +3 -3
- data/lib/cobra_commander/version.rb +1 -1
- metadata +57 -26
- data/lib/cobra_commander/executor/component_exec.rb +0 -33
- data/lib/cobra_commander/executor/multi_exec.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3e2348a3ed7ceecd361b13fe0632bb5daa54a59652d4dd9eac5646c2ccc5878
|
4
|
+
data.tar.gz: 49dc4f2937e87a8146c685d82d7d45fea82dd16e3e0b880ef9944c8ac0710b0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ca9041bfb43cbec99252e00f52e117d8e2f09778afc686f1b204c4ff2e5733841f10b727adc3e3c9f28b121d61dff6ef4c79745a9e7574eb9bc36c3dd6ee5a9
|
7
|
+
data.tar.gz: befffb97f5743fce5066db00f5c434053bdebc275c12b74d95c3d33a375b8c9e6bf5e10fa186188a163f1f9c8fd91f01055d56c8880ee40815915878077aebc9
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
name: CI
|
2
2
|
|
3
|
-
on:
|
3
|
+
on: push
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
test:
|
@@ -13,6 +13,7 @@ jobs:
|
|
13
13
|
- "2.5"
|
14
14
|
- "2.6"
|
15
15
|
- "2.7"
|
16
|
+
- "3.0"
|
16
17
|
bundler:
|
17
18
|
- "1"
|
18
19
|
- "2"
|
@@ -20,8 +21,7 @@ jobs:
|
|
20
21
|
BUNDLE_GEMFILE: gemfiles/bundler${{ matrix.bundler }}.gemfile
|
21
22
|
steps:
|
22
23
|
- uses: actions/checkout@v2
|
23
|
-
-
|
24
|
-
uses: ruby/setup-ruby@v1
|
24
|
+
- uses: ruby/setup-ruby@v1
|
25
25
|
with:
|
26
26
|
ruby-version: ${{ matrix.ruby }}
|
27
27
|
bundler: ${{ matrix.bundler }}
|
@@ -37,11 +37,10 @@ jobs:
|
|
37
37
|
runs-on: ubuntu-latest
|
38
38
|
steps:
|
39
39
|
- uses: actions/checkout@v2
|
40
|
-
-
|
41
|
-
uses: reviewdog/action-rubocop@v1
|
40
|
+
- uses: ruby/setup-ruby@v1
|
42
41
|
with:
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
ruby-version: 3.0
|
43
|
+
- name: Bundle
|
44
|
+
run: bundle
|
45
|
+
- name: Run standard
|
46
|
+
run: bundle exec rake standard
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Publish Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
tags:
|
8
|
+
- v*
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Release Gem
|
15
|
+
uses: cadwallion/publish-rubygems-action@master
|
16
|
+
if: contains(github.ref, 'refs/tags/v')
|
17
|
+
env:
|
18
|
+
GITHUB_TOKEN: ${{ secrets.github_token }}
|
19
|
+
RUBYGEMS_API_KEY: ${{ secrets.rubygems_api_key }}
|
20
|
+
RELEASE_COMMAND: rake release
|
data/CHANGELOG.md
CHANGED
@@ -2,9 +2,44 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## Version 0.14.0 - 2021-11-24
|
6
|
+
|
7
|
+
* Retain selection on Interactive Printer [#69](https://github.com/powerhome/cobra_commander/pull/69)
|
8
|
+
* Sorts the interactive UI alphabetically with failures first [#68](https://github.com/powerhome/cobra_commander/pull/68)
|
9
|
+
* Switch from Rubocop to Standardrb [#67](https://github.com/powerhome/cobra_commander/pull/67)
|
10
|
+
|
11
|
+
## Version 0.13.0 - 2021-10-21
|
12
|
+
|
13
|
+
* Sorts the interactive UI alphabetically with failures first [#66](https://github.com/powerhome/cobra_commander/pull/66)
|
14
|
+
* Switch linter to standardrb [#67](https://github.com/powerhome/cobra_commander/pull/67)
|
15
|
+
|
16
|
+
## Version 0.12.0 - 2021-09-21
|
17
|
+
|
18
|
+
* Add interactive UI and Markdown output to `cobra exec` [#63](https://github.com/powerhome/cobra_commander/pull/63)
|
19
|
+
* Add `--self` and `--no-self` filters to `cobra exec` and `cobra ls`, defaults to `--self` [#61](https://github.com/powerhome/cobra_commander/pull/61)
|
20
|
+
|
21
|
+
## Version 0.11.0 - 2021-04-23
|
22
|
+
|
23
|
+
* Add concurrency limit to multi exec [#57](https://github.com/powerhome/cobra_commander/pull/57)
|
24
|
+
* Ruby 3.0 compatibility [#55](https://github.com/powerhome/cobra_commander/pull/55)
|
25
|
+
|
26
|
+
## Version 0.10.0 - 2021-02-25
|
27
|
+
|
28
|
+
* Add support for Bundler 2 [#54](https://github.com/powerhome/cobra_commander/pull/54)
|
29
|
+
* Add support for Ruby 2.7 [#53](https://github.com/powerhome/cobra_commander/pull/53)
|
30
|
+
|
31
|
+
## Version 0.9.2 -
|
32
|
+
|
33
|
+
* Another fix for binstubs [#51](https://github.com/powerhome/cobra_commander/pull/51)
|
34
|
+
|
35
|
+
## Version 0.9.1 -
|
36
|
+
|
37
|
+
* Replace bundler encapsulation violation by LockfileParser [#48](https://github.com/powerhome/cobra_commander/pull/48)
|
38
|
+
* Fix bundle binstubs [#50](https://github.com/powerhome/cobra_commander/pull/50)
|
39
|
+
|
5
40
|
## Version 0.9.0 - 2020-08-26
|
6
41
|
|
7
|
-
* Add support for parallel task execution to `cobra exec
|
42
|
+
* Add support for parallel task execution to `cobra exec` [#49](https://github.com/powerhome/cobra_commander/pull/49)
|
8
43
|
|
9
44
|
## Version 0.8.1 - 2020-07-29
|
10
45
|
|
data/README.md
CHANGED
@@ -2,11 +2,10 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/cobra_commander)
|
4
4
|
[](https://rubygems.org/gems/cobra_commander)
|
5
|
-
[](https://gemnasium.com/github.com/powerhome/cobra_commander)
|
5
|
+
[](https://github.com/powerhome/cobra_commander/actions/workflows/ci.yml)
|
6
|
+
[](https://codeclimate.com/github/powerhome/cobra_commander/maintainability)
|
8
7
|
|
9
|
-
Tools for working with Component Based Rails Apps (see
|
8
|
+
Tools for working with Component Based Rails Apps (see https://cbra.info/). Includes tools for graphing both Ruby and Javascript components in an application and their relationships, as well as selectively testing components based on changes made.
|
10
9
|
|
11
10
|
## Installation
|
12
11
|
|
data/Rakefile
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
|
-
|
4
|
+
require "standard/rake"
|
5
5
|
require "rspec/core/rake_task"
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
7
6
|
|
8
|
-
|
9
|
-
RuboCop::RakeTask.new
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
8
|
|
11
|
-
task default: %i[spec
|
9
|
+
task default: %i[spec standard]
|
data/cobra_commander.gemspec
CHANGED
@@ -5,45 +5,47 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
require "cobra_commander/version"
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name
|
9
|
-
spec.version
|
10
|
-
spec.authors
|
8
|
+
spec.name = "cobra_commander"
|
9
|
+
spec.version = CobraCommander::VERSION
|
10
|
+
spec.authors = [
|
11
11
|
"Ben Langfeld",
|
12
12
|
"Garett Arrowood",
|
13
|
-
"Carlos Palhares"
|
13
|
+
"Carlos Palhares"
|
14
14
|
]
|
15
15
|
spec.email = [
|
16
16
|
"blangfeld@powerhrg.com",
|
17
17
|
"garett.arrowood@powerhrg.com",
|
18
|
-
"carlos.palhares@powerhrg.com"
|
18
|
+
"carlos.palhares@powerhrg.com"
|
19
19
|
]
|
20
|
-
spec.summary
|
21
|
-
spec.description
|
20
|
+
spec.summary = "Tools for working with Component Based Rails Apps"
|
21
|
+
spec.description = <<~DESCRIPTION
|
22
22
|
Tools for working with Component Based Rails Apps (see http://shageman.github.io/cbra.info/).
|
23
23
|
Includes tools for graphing the components of an app and their relationships, as well as selectively
|
24
24
|
testing components based on changes made.
|
25
25
|
DESCRIPTION
|
26
|
-
spec.homepage
|
27
|
-
spec.license
|
26
|
+
spec.homepage = "http://tech.powerhrg.com/cobra_commander/"
|
27
|
+
spec.license = "MIT"
|
28
28
|
|
29
|
-
spec.files
|
29
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
30
30
|
f.match(%r{^(test|spec|features)/})
|
31
31
|
end
|
32
|
-
spec.bindir
|
33
|
-
spec.executables
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
34
|
spec.require_paths = ["lib"]
|
35
35
|
|
36
36
|
spec.add_dependency "bundler"
|
37
|
+
spec.add_dependency "concurrent-ruby", "~> 1.1"
|
37
38
|
spec.add_dependency "ruby-graphviz", "~> 1.2.3"
|
38
39
|
spec.add_dependency "thor", ["< 2.0", ">= 0.18.1"]
|
39
|
-
spec.add_dependency "tty-command", "~> 0.
|
40
|
+
spec.add_dependency "tty-command", "~> 0.10.0"
|
41
|
+
spec.add_dependency "tty-prompt", "~> 0.23.1"
|
40
42
|
spec.add_dependency "tty-spinner", "~> 0.9.3"
|
41
43
|
|
42
44
|
spec.add_development_dependency "aruba", "~> 0.14.2"
|
43
45
|
spec.add_development_dependency "bundler"
|
44
46
|
spec.add_development_dependency "guard-rspec"
|
45
47
|
spec.add_development_dependency "pry"
|
46
|
-
spec.add_development_dependency "rake", "
|
48
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
47
49
|
spec.add_development_dependency "rspec", "~> 3.5"
|
48
|
-
spec.add_development_dependency "
|
50
|
+
spec.add_development_dependency "standard", ">= 1.3.0"
|
49
51
|
end
|
@@ -25,21 +25,18 @@ module CobraCommander
|
|
25
25
|
@transitively.map(&method(:affected_component))
|
26
26
|
end
|
27
27
|
|
28
|
-
def json_representation
|
28
|
+
def json_representation
|
29
29
|
{
|
30
30
|
changed_files: @changes,
|
31
31
|
directly_affected_components: directly,
|
32
32
|
transitively_affected_components: transitively,
|
33
33
|
test_scripts: scripts,
|
34
34
|
component_names: names,
|
35
|
-
languages: {
|
36
|
-
ruby: contains_ruby?,
|
37
|
-
javascript: contains_js?,
|
38
|
-
},
|
35
|
+
languages: {ruby: contains_ruby?, javascript: contains_js?}
|
39
36
|
}.to_json
|
40
37
|
end
|
41
38
|
|
42
|
-
|
39
|
+
private
|
43
40
|
|
44
41
|
def run!
|
45
42
|
@transitively = Set.new
|
@@ -68,7 +65,7 @@ module CobraCommander
|
|
68
65
|
{
|
69
66
|
name: component.name,
|
70
67
|
path: component.root_paths,
|
71
|
-
type: component.sources.keys.map(&:to_s).map(&:capitalize).join(" & ")
|
68
|
+
type: component.sources.keys.map(&:to_s).map(&:capitalize).join(" & ")
|
72
69
|
}
|
73
70
|
end
|
74
71
|
|
@@ -28,7 +28,7 @@ module CobraCommander
|
|
28
28
|
puts e.message
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
private
|
32
32
|
|
33
33
|
def show_full
|
34
34
|
changes_since_last_commit
|
@@ -66,13 +66,13 @@ module CobraCommander
|
|
66
66
|
|
67
67
|
def directly_affected_components
|
68
68
|
puts "<<< Directly affected components >>>"
|
69
|
-
@affected.directly.each { |component| puts display(component) }
|
69
|
+
@affected.directly.each { |component| puts display(**component) }
|
70
70
|
puts blank_line
|
71
71
|
end
|
72
72
|
|
73
73
|
def transitively_affected_components
|
74
74
|
puts "<<< Transitively affected components >>>"
|
75
|
-
@affected.transitively.each { |component| puts display(component) }
|
75
|
+
@affected.transitively.each { |component| puts display(**component) }
|
76
76
|
puts blank_line
|
77
77
|
end
|
78
78
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
# @private
|
5
|
+
class CLI
|
6
|
+
private_class_method def self.filter_options(dependents:, dependencies:)
|
7
|
+
method_option :dependencies, type: :boolean, aliases: "-d", desc: dependencies
|
8
|
+
method_option :dependents, type: :boolean, aliases: "-D", desc: dependents
|
9
|
+
method_option :self, type: :boolean, default: true, desc: "Include the own component"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def find_component(name)
|
15
|
+
return umbrella.root unless name
|
16
|
+
|
17
|
+
umbrella.find(name) || error("Component #{name} not found, maybe #{suggestion(name)}") || exit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def suggestion(name)
|
21
|
+
[*suggestions(name), 'one of "cobra ls"'].join(", ")
|
22
|
+
end
|
23
|
+
|
24
|
+
def suggestions(name)
|
25
|
+
spell_checker = DidYouMean::SpellChecker.new(dictionary: umbrella.components.map(&:name))
|
26
|
+
spell_checker.correct(name)
|
27
|
+
rescue NameError
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
def components_filtered(component_name)
|
32
|
+
return umbrella.components unless component_name
|
33
|
+
|
34
|
+
component = find_component(component_name)
|
35
|
+
components = options.self ? [component] : []
|
36
|
+
components.concat component.deep_dependencies if options.dependencies
|
37
|
+
components.concat component.deep_dependents if options.dependents
|
38
|
+
components
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/cobra_commander/cli.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "thor"
|
4
4
|
require "fileutils"
|
5
|
+
require "concurrent-ruby"
|
5
6
|
|
6
7
|
require "cobra_commander"
|
7
8
|
require "cobra_commander/affected"
|
@@ -12,6 +13,10 @@ require "cobra_commander/output"
|
|
12
13
|
module CobraCommander
|
13
14
|
# Implements the tool's CLI
|
14
15
|
class CLI < Thor
|
16
|
+
require "cobra_commander/cli/filters"
|
17
|
+
|
18
|
+
DEFAULT_CONCURRENCY = (Concurrent.processor_count / 2.0).ceil
|
19
|
+
|
15
20
|
class_option :app, default: Dir.pwd, aliases: "-a", type: :string
|
16
21
|
class_option :js, default: false, type: :boolean, desc: "Consider only the JS dependency graph"
|
17
22
|
class_option :ruby, default: false, type: :boolean, desc: "Consider only the Ruby dependency graph"
|
@@ -22,10 +27,8 @@ module CobraCommander
|
|
22
27
|
end
|
23
28
|
|
24
29
|
desc "ls [component]", "Lists the components in the context of a given component or umbrella"
|
25
|
-
|
26
|
-
|
27
|
-
method_option :dependents, type: :boolean, aliases: "-D",
|
28
|
-
desc: "Run the command on each dependency of a given component"
|
30
|
+
filter_options dependents: "Lists all dependents of a given component",
|
31
|
+
dependencies: "Lists all dependencies of a given component"
|
29
32
|
method_option :total, type: :boolean, aliases: "-t", desc: "Prints the total count of components"
|
30
33
|
def ls(component = nil)
|
31
34
|
components = components_filtered(component)
|
@@ -34,13 +37,24 @@ module CobraCommander
|
|
34
37
|
|
35
38
|
desc "exec [component] <command>", "Executes the command in the context of a given component or set thereof. " \
|
36
39
|
"Defaults to all components."
|
37
|
-
|
38
|
-
|
40
|
+
filter_options dependents: "Run the command on each dependent of a given component",
|
41
|
+
dependencies: "Run the command on each dependency of a given component"
|
42
|
+
method_option :concurrency, type: :numeric, default: DEFAULT_CONCURRENCY, aliases: "-c",
|
43
|
+
desc: "Max number of jobs to run concurrently"
|
44
|
+
method_option :interactive, type: :boolean, default: true, aliases: "-i",
|
45
|
+
desc: "Runs in interactive mode to allow the user to inspect the output of each " \
|
46
|
+
"component"
|
39
47
|
def exec(command_or_component, command = nil)
|
40
|
-
CobraCommander::Executor.exec(
|
41
|
-
components_filtered(command && command_or_component),
|
42
|
-
command || command_or_component
|
48
|
+
results = CobraCommander::Executor.exec(
|
49
|
+
components: components_filtered(command && command_or_component),
|
50
|
+
command: command || command_or_component,
|
51
|
+
concurrency: options.concurrency, status_output: $stderr
|
43
52
|
)
|
53
|
+
if options.interactive && results.size > 1
|
54
|
+
CobraCommander::Output::InteractivePrinter.run(results, $stdout)
|
55
|
+
else
|
56
|
+
CobraCommander::Output::MarkdownPrinter.run(results, $stdout)
|
57
|
+
end
|
44
58
|
end
|
45
59
|
|
46
60
|
desc "tree [component]", "Prints the dependency tree of a given component or umbrella"
|
@@ -69,27 +83,10 @@ module CobraCommander
|
|
69
83
|
Change.new(umbrella, options.results, options.branch).run!
|
70
84
|
end
|
71
85
|
|
72
|
-
|
86
|
+
private
|
73
87
|
|
74
88
|
def umbrella
|
75
89
|
@umbrella ||= CobraCommander.umbrella(options.app, yarn: options.js, bundler: options.ruby)
|
76
90
|
end
|
77
|
-
|
78
|
-
def find_component(name)
|
79
|
-
return umbrella.root unless name
|
80
|
-
|
81
|
-
umbrella.find(name) || error("Component #{name} not found, try one of `cobra ls`") || exit(1)
|
82
|
-
end
|
83
|
-
|
84
|
-
def components_filtered(component_name)
|
85
|
-
return umbrella.components unless component_name
|
86
|
-
|
87
|
-
component = find_component(component_name)
|
88
|
-
|
89
|
-
return component.deep_dependencies if options.dependencies
|
90
|
-
return component.deep_dependents if options.dependents
|
91
|
-
|
92
|
-
[component]
|
93
|
-
end
|
94
91
|
end
|
95
92
|
end
|
@@ -21,11 +21,11 @@ module CobraCommander
|
|
21
21
|
|
22
22
|
def components
|
23
23
|
components_source.specs.map do |spec|
|
24
|
-
{
|
24
|
+
{path: spec.loaded_from, name: spec.name, dependencies: spec.dependencies.map(&:name)}
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
private
|
29
29
|
|
30
30
|
def lockfile
|
31
31
|
@lockfile ||= ::Bundler::LockfileParser.new(::Bundler.read_file(path))
|
@@ -24,10 +24,10 @@ module CobraCommander
|
|
24
24
|
|
25
25
|
def dependencies
|
26
26
|
json.fetch("dependencies", {})
|
27
|
-
|
27
|
+
.merge(json.fetch("devDependencies", {}))
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
private
|
31
31
|
|
32
32
|
def json
|
33
33
|
@json ||= JSON.parse(File.read(@path))
|
@@ -28,11 +28,11 @@ module CobraCommander
|
|
28
28
|
|
29
29
|
def components
|
30
30
|
@repo.specs.map do |spec|
|
31
|
-
{
|
31
|
+
{path: spec.path, name: untag(spec.name), dependencies: spec.dependencies.keys.map(&method(:untag))}
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
private
|
36
36
|
|
37
37
|
def load_workspace_packages
|
38
38
|
workspace_spec.map do |_name, spec|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-spinner"
|
4
|
+
require "concurrent-ruby"
|
5
|
+
|
6
|
+
module CobraCommander
|
7
|
+
module Executor
|
8
|
+
# Execute a command on multiple components concurrently
|
9
|
+
class Concurrent
|
10
|
+
def initialize(components, concurrency:, spin_output:)
|
11
|
+
@components = components
|
12
|
+
@multi = TTY::Spinner::Multi.new(":spinner :task", output: spin_output)
|
13
|
+
@semaphore = ::Concurrent::Semaphore.new(concurrency)
|
14
|
+
end
|
15
|
+
|
16
|
+
def exec(command)
|
17
|
+
@multi.top_spinner.update(task: "Running #{command}")
|
18
|
+
@results = []
|
19
|
+
@components.each do |component|
|
20
|
+
register_job(component, command)
|
21
|
+
end
|
22
|
+
@multi.auto_spin
|
23
|
+
@results
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def pastel
|
29
|
+
@pastel ||= Pastel.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def spinner_options
|
33
|
+
@spinner_options ||= {
|
34
|
+
format: :bouncing,
|
35
|
+
success_mark: pastel.green("[DONE]"),
|
36
|
+
error_mark: pastel.red("[ERROR]")
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def register_job(component, command)
|
41
|
+
@multi.register(":spinner #{component.name}", **spinner_options) do |spinner|
|
42
|
+
@semaphore.acquire
|
43
|
+
context = Context.new(component, command)
|
44
|
+
context.success? ? spinner.success : spinner.error
|
45
|
+
@results << context
|
46
|
+
@semaphore.release
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-command"
|
4
|
+
|
5
|
+
module CobraCommander
|
6
|
+
module Executor
|
7
|
+
# Represents a component context to execute a command
|
8
|
+
# @private
|
9
|
+
class Context
|
10
|
+
attr_reader :component, :command
|
11
|
+
|
12
|
+
def initialize(component, command)
|
13
|
+
@component = component
|
14
|
+
@command = command
|
15
|
+
@tty = TTY::Command.new(pty: true, printer: :null, stderr: :stdout)
|
16
|
+
end
|
17
|
+
|
18
|
+
def results
|
19
|
+
@results ||= @component.root_paths.map do |path|
|
20
|
+
isolate_bundle do
|
21
|
+
@tty.run!(command, chdir: path)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def component_name
|
27
|
+
component.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def success?
|
31
|
+
results.all?(&:success?)
|
32
|
+
end
|
33
|
+
|
34
|
+
def output
|
35
|
+
results.join("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def isolate_bundle(&block)
|
41
|
+
if Bundler.respond_to?(:with_unbundled_env)
|
42
|
+
Bundler.with_unbundled_env(&block)
|
43
|
+
else
|
44
|
+
Bundler.with_clean_env(&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,19 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "executor/
|
4
|
-
require_relative "executor/
|
3
|
+
require_relative "executor/context"
|
4
|
+
require_relative "executor/concurrent"
|
5
5
|
|
6
6
|
module CobraCommander
|
7
|
-
# Execute
|
7
|
+
# Execute a command on all given components
|
8
8
|
module Executor
|
9
|
-
def self.exec(components
|
10
|
-
components
|
11
|
-
|
12
|
-
ComponentExec.new(components.first)
|
13
|
-
else
|
14
|
-
MultiExec.new(components)
|
15
|
-
end
|
16
|
-
exec.run(command, output: output, spin_output: status_output)
|
9
|
+
def self.exec(components:, command:, concurrency:, status_output:)
|
10
|
+
Concurrent.new(components, concurrency: concurrency, spin_output: status_output)
|
11
|
+
.exec(command)
|
17
12
|
end
|
18
13
|
end
|
19
14
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "stringio"
|
4
|
+
|
3
5
|
module CobraCommander
|
4
6
|
module Output
|
5
7
|
# Prints the tree in a nice tree form
|
6
8
|
class AsciiTree
|
7
|
-
SPACE
|
8
|
-
BAR
|
9
|
-
TEE
|
9
|
+
SPACE = " "
|
10
|
+
BAR = "│ "
|
11
|
+
TEE = "├── "
|
10
12
|
CORNER = "└── "
|
11
13
|
|
12
14
|
def initialize(component)
|
@@ -20,7 +22,7 @@ module CobraCommander
|
|
20
22
|
end.string
|
21
23
|
end
|
22
24
|
|
23
|
-
|
25
|
+
private
|
24
26
|
|
25
27
|
def list_dependencies(io, component, outdents = [])
|
26
28
|
component.dependencies.each do |dep|
|
@@ -17,7 +17,7 @@ module CobraCommander
|
|
17
17
|
end
|
18
18
|
|
19
19
|
private_class_method def self.extract_format(output)
|
20
|
-
format = output[-3..-1]
|
20
|
+
format = output[-3..-1] # standard:disable Style/SlicingWithRange
|
21
21
|
return format if %w[png dot].include?(format)
|
22
22
|
|
23
23
|
raise ArgumentError, "output format must be 'png' or 'dot'"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pastel"
|
4
|
+
require "tty-prompt"
|
5
|
+
|
6
|
+
module CobraCommander
|
7
|
+
module Output
|
8
|
+
# Runs an interactive output printer
|
9
|
+
class InteractivePrinter
|
10
|
+
pastel = Pastel.new
|
11
|
+
SUCCESS = "#{pastel.green("✔")} %s"
|
12
|
+
ERROR = "#{pastel.red("✖")} %s"
|
13
|
+
BYE = pastel.decorate("\n\n👋 Bye!", :white, :on_black, :bold).freeze
|
14
|
+
|
15
|
+
def self.run(contexts, output)
|
16
|
+
new(contexts).run(output)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(contexts)
|
20
|
+
@prompt = TTY::Prompt.new
|
21
|
+
@options = map_options(contexts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(output)
|
25
|
+
selected_context = nil
|
26
|
+
loop do
|
27
|
+
selected_context = @prompt.select("Print output?", @options, default: @options.key(selected_context))
|
28
|
+
output.puts selected_context.output
|
29
|
+
end
|
30
|
+
rescue TTY::Reader::InputInterrupt
|
31
|
+
output.puts BYE
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def map_options(contexts)
|
37
|
+
contexts.sort(&method(:sort_contexts))
|
38
|
+
.reduce({}) do |options, context|
|
39
|
+
template = context.success? ? SUCCESS : ERROR
|
40
|
+
options.merge format(template, context.component_name) => context
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sort_contexts(context_a, context_b)
|
45
|
+
if context_a.success? == context_b.success?
|
46
|
+
context_a.component_name <=> context_b.component_name
|
47
|
+
else
|
48
|
+
context_a.success? ? 1 : -1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pastel"
|
4
|
+
require "tty-prompt"
|
5
|
+
|
6
|
+
module CobraCommander
|
7
|
+
module Output
|
8
|
+
# Prints the given CobraCommander::Executor::Context to [output] collection in markdown
|
9
|
+
module MarkdownPrinter
|
10
|
+
SUCCESS = "\n## ✔ %s\n"
|
11
|
+
ERROR = "\n## ✖ %s\n"
|
12
|
+
OUTPUT = "\n```\n$ %s\n\n%s\n```\n"
|
13
|
+
|
14
|
+
def self.run(contexts, output)
|
15
|
+
contexts.each do |context|
|
16
|
+
template = context.success? ? SUCCESS : ERROR
|
17
|
+
|
18
|
+
output.print format(template, context.component_name)
|
19
|
+
output.print format(OUTPUT, context.command, context.output)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -29,9 +29,9 @@ module CobraCommander
|
|
29
29
|
|
30
30
|
def add_source(key, source)
|
31
31
|
@root_component.add_source key, source.path, source.dependencies
|
32
|
-
source.components.each do |
|
33
|
-
@components[name] ||= Component.new(self, name)
|
34
|
-
@components[name].add_source key, path, dependencies
|
32
|
+
source.components.each do |component|
|
33
|
+
@components[component[:name]] ||= Component.new(self, component[:name])
|
34
|
+
@components[component[:name]].add_source key, component[:path], component[:dependencies]
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cobra_commander
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Langfeld
|
8
8
|
- Garett Arrowood
|
9
9
|
- Carlos Palhares
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2021-
|
13
|
+
date: 2021-11-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -26,6 +26,20 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: concurrent-ruby
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.1'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.1'
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: ruby-graphviz
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,36 +58,50 @@ dependencies:
|
|
44
58
|
name: thor
|
45
59
|
requirement: !ruby/object:Gem::Requirement
|
46
60
|
requirements:
|
47
|
-
- - "<"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '2.0'
|
50
61
|
- - ">="
|
51
62
|
- !ruby/object:Gem::Version
|
52
63
|
version: 0.18.1
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '2.0'
|
53
67
|
type: :runtime
|
54
68
|
prerelease: false
|
55
69
|
version_requirements: !ruby/object:Gem::Requirement
|
56
70
|
requirements:
|
57
|
-
- - "<"
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
version: '2.0'
|
60
71
|
- - ">="
|
61
72
|
- !ruby/object:Gem::Version
|
62
73
|
version: 0.18.1
|
74
|
+
- - "<"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '2.0'
|
63
77
|
- !ruby/object:Gem::Dependency
|
64
78
|
name: tty-command
|
65
79
|
requirement: !ruby/object:Gem::Requirement
|
66
80
|
requirements:
|
67
81
|
- - "~>"
|
68
82
|
- !ruby/object:Gem::Version
|
69
|
-
version: 0.
|
83
|
+
version: 0.10.0
|
84
|
+
type: :runtime
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.10.0
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: tty-prompt
|
93
|
+
requirement: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 0.23.1
|
70
98
|
type: :runtime
|
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: 0.
|
104
|
+
version: 0.23.1
|
77
105
|
- !ruby/object:Gem::Dependency
|
78
106
|
name: tty-spinner
|
79
107
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,16 +176,16 @@ dependencies:
|
|
148
176
|
name: rake
|
149
177
|
requirement: !ruby/object:Gem::Requirement
|
150
178
|
requirements:
|
151
|
-
- - "
|
179
|
+
- - ">="
|
152
180
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
181
|
+
version: 12.3.3
|
154
182
|
type: :development
|
155
183
|
prerelease: false
|
156
184
|
version_requirements: !ruby/object:Gem::Requirement
|
157
185
|
requirements:
|
158
|
-
- - "
|
186
|
+
- - ">="
|
159
187
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
188
|
+
version: 12.3.3
|
161
189
|
- !ruby/object:Gem::Dependency
|
162
190
|
name: rspec
|
163
191
|
requirement: !ruby/object:Gem::Requirement
|
@@ -173,19 +201,19 @@ dependencies:
|
|
173
201
|
- !ruby/object:Gem::Version
|
174
202
|
version: '3.5'
|
175
203
|
- !ruby/object:Gem::Dependency
|
176
|
-
name:
|
204
|
+
name: standard
|
177
205
|
requirement: !ruby/object:Gem::Requirement
|
178
206
|
requirements:
|
179
|
-
- -
|
207
|
+
- - ">="
|
180
208
|
- !ruby/object:Gem::Version
|
181
|
-
version:
|
209
|
+
version: 1.3.0
|
182
210
|
type: :development
|
183
211
|
prerelease: false
|
184
212
|
version_requirements: !ruby/object:Gem::Requirement
|
185
213
|
requirements:
|
186
|
-
- -
|
214
|
+
- - ">="
|
187
215
|
- !ruby/object:Gem::Version
|
188
|
-
version:
|
216
|
+
version: 1.3.0
|
189
217
|
description: |
|
190
218
|
Tools for working with Component Based Rails Apps (see http://shageman.github.io/cbra.info/).
|
191
219
|
Includes tools for graphing the components of an app and their relationships, as well as selectively
|
@@ -201,6 +229,7 @@ extra_rdoc_files: []
|
|
201
229
|
files:
|
202
230
|
- ".editorconfig"
|
203
231
|
- ".github/workflows/ci.yml"
|
232
|
+
- ".github/workflows/release.yml"
|
204
233
|
- ".gitignore"
|
205
234
|
- ".rspec"
|
206
235
|
- ".rubocop.yml"
|
@@ -222,6 +251,7 @@ files:
|
|
222
251
|
- lib/cobra_commander/affected.rb
|
223
252
|
- lib/cobra_commander/change.rb
|
224
253
|
- lib/cobra_commander/cli.rb
|
254
|
+
- lib/cobra_commander/cli/filters.rb
|
225
255
|
- lib/cobra_commander/component.rb
|
226
256
|
- lib/cobra_commander/dependencies.rb
|
227
257
|
- lib/cobra_commander/dependencies/bundler.rb
|
@@ -229,12 +259,14 @@ files:
|
|
229
259
|
- lib/cobra_commander/dependencies/yarn/package_repo.rb
|
230
260
|
- lib/cobra_commander/dependencies/yarn_workspace.rb
|
231
261
|
- lib/cobra_commander/executor.rb
|
232
|
-
- lib/cobra_commander/executor/
|
233
|
-
- lib/cobra_commander/executor/
|
262
|
+
- lib/cobra_commander/executor/concurrent.rb
|
263
|
+
- lib/cobra_commander/executor/context.rb
|
234
264
|
- lib/cobra_commander/output.rb
|
235
265
|
- lib/cobra_commander/output/ascii_tree.rb
|
236
266
|
- lib/cobra_commander/output/flat_list.rb
|
237
267
|
- lib/cobra_commander/output/graph_viz.rb
|
268
|
+
- lib/cobra_commander/output/interactive_printer.rb
|
269
|
+
- lib/cobra_commander/output/markdown_printer.rb
|
238
270
|
- lib/cobra_commander/umbrella.rb
|
239
271
|
- lib/cobra_commander/version.rb
|
240
272
|
- renovate.json
|
@@ -242,7 +274,7 @@ homepage: http://tech.powerhrg.com/cobra_commander/
|
|
242
274
|
licenses:
|
243
275
|
- MIT
|
244
276
|
metadata: {}
|
245
|
-
post_install_message:
|
277
|
+
post_install_message:
|
246
278
|
rdoc_options: []
|
247
279
|
require_paths:
|
248
280
|
- lib
|
@@ -257,9 +289,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
257
289
|
- !ruby/object:Gem::Version
|
258
290
|
version: '0'
|
259
291
|
requirements: []
|
260
|
-
|
261
|
-
|
262
|
-
signing_key:
|
292
|
+
rubygems_version: 3.0.3
|
293
|
+
signing_key:
|
263
294
|
specification_version: 4
|
264
295
|
summary: Tools for working with Component Based Rails Apps
|
265
296
|
test_files: []
|
@@ -1,33 +0,0 @@
|
|
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
|
@@ -1,46 +0,0 @@
|
|
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
|