cobra_commander 1.0.1 → 1.1.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/cobra_commander.gemspec +4 -10
- data/docs/CHANGELOG.md +7 -0
- data/lib/cobra_commander/affected.rb +6 -41
- data/lib/cobra_commander/cli/filters.rb +1 -1
- data/lib/cobra_commander/cli/output/change.rb +12 -52
- data/lib/cobra_commander/cli.rb +20 -29
- data/lib/cobra_commander/component.rb +4 -0
- data/lib/cobra_commander/executor/buffered_printer.rb +41 -0
- data/lib/cobra_commander/executor/command.rb +21 -46
- data/lib/cobra_commander/executor/isolated_pty.rb +29 -0
- data/lib/cobra_commander/executor/output_prompt.rb +59 -0
- data/lib/cobra_commander/executor/package_criteria.rb +0 -3
- data/lib/cobra_commander/executor/run_script.rb +25 -0
- data/lib/cobra_commander/executor/script.rb +14 -30
- data/lib/cobra_commander/executor/worker_pool.rb +104 -0
- data/lib/cobra_commander/executor.rb +33 -31
- data/lib/cobra_commander/package.rb +4 -0
- data/lib/cobra_commander/version.rb +1 -1
- metadata +8 -46
- data/.gitignore +0 -16
- data/.rspec +0 -3
- data/.rubocop.yml +0 -8
- data/Gemfile +0 -12
- data/Guardfile +0 -14
- data/Rakefile +0 -10
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/doc/dependency_decisions.yml +0 -9
- data/lib/cobra_commander/executor/execution.rb +0 -52
- data/lib/cobra_commander/executor/interactive_printer.rb +0 -53
- data/lib/cobra_commander/executor/job.rb +0 -51
- data/lib/cobra_commander/executor/markdown_printer.rb +0 -21
- data/lib/cobra_commander/executor/spinners.rb +0 -40
- data/mkdocs.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e4912ef5e1373f254652d50f2c88bd5d6fc2785675f2d53bdf3572ffcbfafaf
|
4
|
+
data.tar.gz: 5c0bdd82e4ea3fcffeb1f8d7a44aece59ca929d40171f3c22b6a3c95d951b942
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d8882fe6141cc4ddc6c7ec92616aceb7010c3d889b8c6b7f2d039d589fcd1c9bed43d8dcac946f239057f27eb2ab51c5cd65a181a838401cff2e38f6edb1e8d
|
7
|
+
data.tar.gz: ceda5086044a31eae40445597289fae8c23aef320160b07b8d9a1b3fc33ea9318eb2815e97781af5a70f4295842bc59e07e9cc562473fa92933c837db47a9f36
|
data/cobra_commander.gemspec
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require "cobra_commander/version"
|
3
|
+
require_relative "lib/cobra_commander/version"
|
6
4
|
|
7
5
|
Gem::Specification.new do |spec|
|
8
6
|
spec.name = "cobra_commander"
|
@@ -31,19 +29,15 @@ Gem::Specification.new do |spec|
|
|
31
29
|
spec.metadata["source_code_uri"] = "https://github.com/powerhome/cobra_commander"
|
32
30
|
spec.metadata["changelog_uri"] = "https://github.com/powerhome/cobra_commander/blob/main/cobra_commander/docs/CHANGELOG.md"
|
33
31
|
|
34
|
-
spec.files =
|
35
|
-
f.match(%r{^(test|spec|features)/})
|
36
|
-
end
|
32
|
+
spec.files = Dir["{docs,exe,lib}/**/*"] + ["cobra_commander.gemspec"]
|
37
33
|
spec.bindir = "exe"
|
38
|
-
spec.executables =
|
39
|
-
spec.require_paths = [
|
34
|
+
spec.executables = %w[cobra]
|
35
|
+
spec.require_paths = %w[lib]
|
40
36
|
|
41
37
|
spec.add_dependency "bundler"
|
42
|
-
spec.add_dependency "concurrent-ruby", "~> 1.1"
|
43
38
|
spec.add_dependency "thor", ["< 2.0", ">= 0.18.1"]
|
44
39
|
spec.add_dependency "tty-command", "~> 0.10.0"
|
45
40
|
spec.add_dependency "tty-prompt", "~> 0.23.1"
|
46
|
-
spec.add_dependency "tty-spinner", "~> 0.9.3"
|
47
41
|
|
48
42
|
spec.add_development_dependency "aruba", "~> 0.14.2"
|
49
43
|
spec.add_development_dependency "bundler"
|
data/docs/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
## Version 1.1.0 - 2023-03-09
|
6
|
+
|
7
|
+
* New Executor by @xjunior in [#104](https://github.com/powerhome/cobra_commander/pull/104)
|
8
|
+
* Cleanup cobra changes by @xjunior in [#105](https://github.com/powerhome/cobra_commander/pull/105)
|
9
|
+
|
3
10
|
## Version 1.0.1 - 2023-01-05
|
4
11
|
|
5
12
|
* Fix Umbrella#resolve unable to resolve a path relative to the project @xjunior [#103](https://github.com/powerhome/cobra_commander/pull/103)
|
@@ -1,25 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "json"
|
4
|
-
|
5
3
|
module CobraCommander
|
6
4
|
# Calculates directly & transitively affected components
|
5
|
+
# Takes a list of changes, and resolves all components affected
|
6
|
+
#
|
7
7
|
class Affected
|
8
|
+
include Enumerable
|
9
|
+
|
8
10
|
def initialize(umbrella, changes)
|
9
11
|
@umbrella = umbrella
|
10
12
|
@changes = changes
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def all
|
18
|
-
@all ||= (directly | transitively).sort_by(&:name)
|
19
|
-
end
|
20
|
-
|
21
|
-
def scripts
|
22
|
-
@scripts ||= paths.map { |path| path.join("test.sh") }
|
15
|
+
def each(&block)
|
16
|
+
(directly | transitively).sort_by(&:name).each(&block)
|
23
17
|
end
|
24
18
|
|
25
19
|
def directly
|
@@ -31,34 +25,5 @@ module CobraCommander
|
|
31
25
|
@transitively ||= directly.flat_map(&:deep_dependents)
|
32
26
|
.uniq.sort_by(&:name)
|
33
27
|
end
|
34
|
-
|
35
|
-
def to_json(*_args)
|
36
|
-
{
|
37
|
-
changed_files: @changes,
|
38
|
-
directly_affected_components: directly.map { |c| affected_component(c) },
|
39
|
-
transitively_affected_components: transitively.map { |c| affected_component(c) },
|
40
|
-
test_scripts: scripts,
|
41
|
-
component_names: names,
|
42
|
-
languages: all_affected_packages,
|
43
|
-
}.to_json
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def affected_component(component)
|
49
|
-
{
|
50
|
-
name: component.name,
|
51
|
-
path: component.root_paths.map(&:to_s),
|
52
|
-
type: component.packages.map(&:key).map(&:to_s),
|
53
|
-
}
|
54
|
-
end
|
55
|
-
|
56
|
-
def paths
|
57
|
-
@paths ||= all.map(&:root_paths).flatten
|
58
|
-
end
|
59
|
-
|
60
|
-
def all_affected_packages
|
61
|
-
all.flat_map(&:packages).map(&:key).uniq
|
62
|
-
end
|
63
28
|
end
|
64
29
|
end
|
@@ -6,27 +6,19 @@ require "cobra_commander/affected"
|
|
6
6
|
module CobraCommander
|
7
7
|
class CLI
|
8
8
|
module Output
|
9
|
-
# Calculates and prints affected components & files
|
10
9
|
class Change
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(umbrella, oformat, branch, changes: nil)
|
14
|
-
@format = oformat
|
10
|
+
def initialize(umbrella, branch, changes: nil)
|
15
11
|
@branch = branch
|
16
12
|
@umbrella = umbrella
|
17
13
|
@changes = changes || GitChanged.new(umbrella.path, branch)
|
18
14
|
end
|
19
15
|
|
20
16
|
def run!
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
tests_to_run
|
27
|
-
end
|
28
|
-
rescue GitChanged::InvalidSelectionError => e
|
29
|
-
puts e.message
|
17
|
+
print_changes_since_last_commit
|
18
|
+
puts
|
19
|
+
print_directly_affected_components
|
20
|
+
puts
|
21
|
+
print_transitively_affected_components
|
30
22
|
end
|
31
23
|
|
32
24
|
private
|
@@ -35,55 +27,23 @@ module CobraCommander
|
|
35
27
|
@affected ||= Affected.new(@umbrella, @changes)
|
36
28
|
end
|
37
29
|
|
38
|
-
def
|
39
|
-
changes_since_last_commit
|
40
|
-
directly_affected_components
|
41
|
-
transitively_affected_components
|
42
|
-
end
|
43
|
-
|
44
|
-
def assert_valid_result_choice
|
45
|
-
return if %w[test full name json].include?(@format)
|
46
|
-
|
47
|
-
raise InvalidSelectionError, "--results must be 'test', 'full', 'name' or 'json'"
|
48
|
-
end
|
49
|
-
|
50
|
-
def selected_format?(result)
|
51
|
-
@format == result
|
52
|
-
end
|
53
|
-
|
54
|
-
def changes_since_last_commit
|
30
|
+
def print_changes_since_last_commit
|
55
31
|
puts "<<< Changes since last commit on #{@branch} >>>"
|
56
32
|
puts(*@changes) if @changes.any?
|
57
|
-
puts blank_line
|
58
33
|
end
|
59
34
|
|
60
|
-
def
|
35
|
+
def print_directly_affected_components
|
61
36
|
puts "<<< Directly affected components >>>"
|
62
|
-
affected.directly.each { |component|
|
63
|
-
puts blank_line
|
37
|
+
affected.directly.each { |component| display(component) }
|
64
38
|
end
|
65
39
|
|
66
|
-
def
|
40
|
+
def print_transitively_affected_components
|
67
41
|
puts "<<< Transitively affected components >>>"
|
68
|
-
affected.transitively.each { |component|
|
69
|
-
puts blank_line
|
70
|
-
end
|
71
|
-
|
72
|
-
def tests_to_run
|
73
|
-
puts "<<< Test scripts to run >>>" if selected_format?("full")
|
74
|
-
if selected_format?("name")
|
75
|
-
affected.names.each { |component_name| puts component_name }
|
76
|
-
else
|
77
|
-
affected.scripts.each { |component_script| puts component_script }
|
78
|
-
end
|
42
|
+
affected.transitively.each { |component| display(component) }
|
79
43
|
end
|
80
44
|
|
81
45
|
def display(component)
|
82
|
-
"#{component.name} - #{component.packages.map(&:key).map(&:to_s).map(&:capitalize).join(' & ')}"
|
83
|
-
end
|
84
|
-
|
85
|
-
def blank_line
|
86
|
-
""
|
46
|
+
puts "#{component.name} - #{component.packages.map(&:key).map(&:to_s).map(&:capitalize).join(' & ')}"
|
87
47
|
end
|
88
48
|
end
|
89
49
|
end
|
data/lib/cobra_commander/cli.rb
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "etc"
|
3
4
|
require "thor"
|
4
5
|
require "fileutils"
|
5
|
-
require "concurrent-ruby"
|
6
|
-
|
7
6
|
require "cobra_commander"
|
8
7
|
|
9
8
|
module CobraCommander
|
10
9
|
# Implements the tool's CLI
|
11
10
|
class CLI < Thor
|
11
|
+
DEFAULT_CONCURRENCY = (Etc.nprocessors / 2.0).ceil
|
12
|
+
|
12
13
|
require_relative "cli/filters"
|
13
14
|
require_relative "cli/output/ascii_tree"
|
14
15
|
require_relative "cli/output/change"
|
15
16
|
require_relative "cli/output/dot_graph"
|
16
17
|
|
17
|
-
DEFAULT_CONCURRENCY = (Concurrent.processor_count / 2.0).ceil
|
18
|
-
|
19
18
|
class_option :app, default: Dir.pwd, aliases: "-a", type: :string
|
20
19
|
Source.all.keys.each do |key|
|
21
20
|
class_option key, default: false, type: :boolean, desc: "Consider only the #{key} dependency graph"
|
@@ -39,40 +38,36 @@ module CobraCommander
|
|
39
38
|
"Defaults to all components."
|
40
39
|
filter_options dependents: "Run the script on each dependent of a given component",
|
41
40
|
dependencies: "Run the script on each dependency of a given component"
|
42
|
-
method_option :concurrency, type: :numeric,
|
43
|
-
|
41
|
+
method_option :concurrency, type: :numeric, aliases: "-c", desc: "Max number of jobs to run concurrently",
|
42
|
+
default: DEFAULT_CONCURRENCY
|
44
43
|
method_option :interactive, type: :boolean, default: true, aliases: "-i",
|
45
44
|
desc: "Runs in interactive mode to allow the user to inspect the output of each " \
|
46
45
|
"component"
|
47
46
|
def exec(script_or_components, script = nil)
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
CobraCommander::Executor.execute_and_handle_exit(
|
48
|
+
runner: ::CobraCommander::Executor::Script.new(script || script_or_components),
|
49
|
+
workers: options.concurrency,
|
50
|
+
interactive: options.interactive,
|
51
|
+
jobs: components_filtered(script && script_or_components)
|
51
52
|
)
|
52
|
-
output_mode = options.interactive && jobs.count > 1 ? :interactive : :markdown
|
53
|
-
execution = CobraCommander::Executor.execute(jobs: jobs, workers: options.concurrency,
|
54
|
-
output_mode: output_mode, output: $stdout, status_output: $stderr)
|
55
|
-
exit 1 unless execution.success?
|
56
53
|
end
|
57
54
|
|
58
55
|
desc "cmd [components] <command>", "Executes the command in the context of a given component or set thereof. " \
|
59
56
|
"Defaults to all components."
|
60
57
|
filter_options dependents: "Run the command on each dependent of a given component",
|
61
58
|
dependencies: "Run the command on each dependency of a given component"
|
62
|
-
method_option :concurrency, type: :numeric,
|
63
|
-
|
59
|
+
method_option :concurrency, type: :numeric, aliases: "-c", desc: "Max number of jobs to run concurrently",
|
60
|
+
default: DEFAULT_CONCURRENCY
|
64
61
|
method_option :interactive, type: :boolean, default: true, aliases: "-i",
|
65
62
|
desc: "Runs in interactive mode to allow the user to inspect the output of each " \
|
66
63
|
"component"
|
67
64
|
def cmd(command_or_components, command = nil)
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
CobraCommander::Executor.execute_and_handle_exit(
|
66
|
+
runner: ::CobraCommander::Executor::Command.new(command || command_or_components),
|
67
|
+
workers: options.concurrency,
|
68
|
+
interactive: options.interactive,
|
69
|
+
jobs: components_filtered(command && command_or_components).flat_map(&:packages)
|
71
70
|
)
|
72
|
-
output_mode = options.interactive && jobs.count > 1 ? :interactive : :markdown
|
73
|
-
execution = CobraCommander::Executor.execute(jobs: jobs, workers: options.concurrency,
|
74
|
-
output_mode: output_mode, output: $stdout, status_output: $stderr)
|
75
|
-
exit 1 unless execution.success?
|
76
71
|
end
|
77
72
|
|
78
73
|
desc "tree [component]", "Prints the dependency tree of a given component or umbrella"
|
@@ -97,22 +92,18 @@ module CobraCommander
|
|
97
92
|
end
|
98
93
|
|
99
94
|
desc "changes [--results=RESULTS] [--branch=BRANCH]", "Prints list of changed files"
|
100
|
-
method_option :results, default: "test", aliases: "-r", desc: "Accepts test, full, name or json"
|
101
95
|
method_option :branch, default: "master", aliases: "-b", desc: "Specified target to calculate against"
|
102
96
|
def changes
|
103
|
-
Output::Change.new(umbrella, options.
|
97
|
+
Output::Change.new(umbrella, options.branch).run!
|
104
98
|
end
|
105
99
|
|
106
100
|
private
|
107
101
|
|
108
102
|
def umbrella
|
109
|
-
selector = Source.all.keys.reduce({}) do |sel, key|
|
103
|
+
selector = CobraCommander::Source.all.keys.reduce({}) do |sel, key|
|
110
104
|
sel.merge(key => options.public_send(key))
|
111
105
|
end
|
112
|
-
@umbrella ||= CobraCommander::Umbrella.new(
|
113
|
-
options.app,
|
114
|
-
**selector
|
115
|
-
)
|
106
|
+
@umbrella ||= CobraCommander::Umbrella.new(options.app, **selector)
|
116
107
|
rescue ::CobraCommander::Source::Error => e
|
117
108
|
error(e.message)
|
118
109
|
exit(1)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
module Executor
|
5
|
+
class BufferedPrinter < TTY::Command::Printers::Null
|
6
|
+
TIME_FORMAT = "Finished in %5.3fs"
|
7
|
+
|
8
|
+
def initialize(*)
|
9
|
+
super
|
10
|
+
|
11
|
+
@buffers = Hash.new { |h, k| h[k] = StringIO.new }
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(cmd, message)
|
15
|
+
@buffers[cmd.uuid].write message
|
16
|
+
end
|
17
|
+
|
18
|
+
def print_command_out_data(cmd, *args)
|
19
|
+
write(cmd, args.join)
|
20
|
+
end
|
21
|
+
|
22
|
+
def print_command_err_data(cmd, *args)
|
23
|
+
write(cmd, args.join)
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_command_start(cmd, *args)
|
27
|
+
message = "Running #{decorate(cmd.to_command, :yellow, :bold)} #{args.join(' ')}"
|
28
|
+
write(cmd, message)
|
29
|
+
end
|
30
|
+
|
31
|
+
def print_command_exit(cmd, status, runtime, *)
|
32
|
+
message = TIME_FORMAT % runtime
|
33
|
+
message << " with exit status #{status}" if status
|
34
|
+
|
35
|
+
output.puts @buffers.delete(cmd.uuid).string
|
36
|
+
output.puts decorate(message, status == 0 ? :green : :red)
|
37
|
+
output.puts
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,74 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "./job"
|
4
|
-
require_relative "./package_criteria"
|
5
|
-
|
6
3
|
module CobraCommander
|
7
4
|
module Executor
|
8
5
|
class Command
|
9
|
-
include ::CobraCommander::Executor::Job
|
10
6
|
include ::CobraCommander::Executor::PackageCriteria
|
7
|
+
include ::CobraCommander::Executor::RunScript
|
11
8
|
|
12
9
|
SKIP_UNEXISTING = "Command %s does not exist. Check your cobra.yml for existing commands in %s."
|
13
10
|
SKIP_CRITERIA = "Package %s does not match criteria."
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
# @param components [Enumerable<CobraCommander::Component>] the target components
|
18
|
-
# @param command [String] shell script to run from the directories of the component's packages
|
19
|
-
# @return [Array<CobraCommander::Executor::Command>]
|
20
|
-
def self.for(components, command)
|
21
|
-
components.flat_map(&:packages).map do |package|
|
22
|
-
new(package, command)
|
23
|
-
end
|
12
|
+
def initialize(command_name)
|
13
|
+
@command_name = command_name
|
24
14
|
end
|
25
15
|
|
26
16
|
# Calls the commands sequentially, stopping ony if an :error happens.
|
27
17
|
#
|
28
18
|
# If one of the commands skips, the result will be :success.
|
29
19
|
#
|
30
|
-
# @param
|
31
|
-
# @
|
32
|
-
# @
|
33
|
-
def
|
34
|
-
|
35
|
-
new_output = [prev_output&.strip, output&.strip].join("\n")
|
36
|
-
return [:error, new_output] if result == :error
|
37
|
-
|
38
|
-
[:success, new_output]
|
39
|
-
end
|
20
|
+
# @param tty [CobraComander::Executor::IsolatedPTY] tty to execute shell scripts
|
21
|
+
# @param package [CobraComander::Package] target package to execute the named command
|
22
|
+
# @return [Array<Symbol, String>]
|
23
|
+
def call(tty, package)
|
24
|
+
run_command tty, package, @command_name
|
40
25
|
end
|
41
26
|
|
42
|
-
|
43
|
-
@package = package
|
44
|
-
@command = command
|
45
|
-
end
|
46
|
-
|
47
|
-
def to_s
|
48
|
-
"#{@package.name} (#{@package.key})"
|
49
|
-
end
|
27
|
+
private
|
50
28
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
when
|
56
|
-
when
|
57
|
-
|
58
|
-
else run_script(command, @package.path)
|
29
|
+
def run_command(tty, package, command_name)
|
30
|
+
definition = package.source.config&.dig("commands", command_name)
|
31
|
+
case definition
|
32
|
+
when Array then run_multiple(tty, package, definition)
|
33
|
+
when Hash then run_with_criteria(tty, package, definition)
|
34
|
+
when nil then [:skip, format(SKIP_UNEXISTING, command_name, package.key)]
|
35
|
+
else run_script(tty, definition, package.path)
|
59
36
|
end
|
60
37
|
end
|
61
38
|
|
62
|
-
|
63
|
-
|
64
|
-
def run_with_criteria(command)
|
65
|
-
return skip(format(SKIP_CRITERIA, @package.name)) unless match_criteria?(@package, command.fetch("if", {}))
|
39
|
+
def run_with_criteria(tty, package, command)
|
40
|
+
return [:skip, format(SKIP_CRITERIA, package.name)] unless match_criteria?(package, command.fetch("if", {}))
|
66
41
|
|
67
|
-
run_script(command["run"],
|
42
|
+
run_script(tty, command["run"], package.path)
|
68
43
|
end
|
69
44
|
|
70
|
-
def run_multiple(package, commands)
|
71
|
-
|
45
|
+
def run_multiple(tty, package, commands)
|
46
|
+
run_many(commands) { run_command(tty, package, _1) }
|
72
47
|
end
|
73
48
|
end
|
74
49
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
module Executor
|
5
|
+
#
|
6
|
+
# Executes commands in a clean environment, without the influence
|
7
|
+
# of the current Bundler env vars.
|
8
|
+
#
|
9
|
+
class IsolatedPTY < ::TTY::Command
|
10
|
+
def initialize(**kwargs)
|
11
|
+
super(pty: true, **kwargs)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run!(...)
|
15
|
+
isolate_bundle do
|
16
|
+
super(...)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def isolate_bundle(&block)
|
21
|
+
if Bundler.respond_to?(:with_unbundled_env)
|
22
|
+
Bundler.with_unbundled_env(&block)
|
23
|
+
else
|
24
|
+
Bundler.with_clean_env(&block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
module Executor
|
5
|
+
# Runs an interactive output printer
|
6
|
+
class OutputPrompt
|
7
|
+
def self.run(pool, output = $stdout)
|
8
|
+
new(pool.jobs).run(output)
|
9
|
+
end
|
10
|
+
|
11
|
+
pastel = Pastel.new
|
12
|
+
ICONS = {
|
13
|
+
nil => pastel.dim("⦻"),
|
14
|
+
success: pastel.green("✔"),
|
15
|
+
skip: pastel.yellow("↷"),
|
16
|
+
error: pastel.red("✖"),
|
17
|
+
}.freeze
|
18
|
+
CANCELLED = pastel.dim("(cancelled)")
|
19
|
+
BYE = pastel.bold("\n\n👋 Bye!")
|
20
|
+
|
21
|
+
def initialize(jobs)
|
22
|
+
@jobs = jobs
|
23
|
+
@prompt = TTY::Prompt.new(symbols: { cross: " " })
|
24
|
+
end
|
25
|
+
|
26
|
+
def options
|
27
|
+
@options ||= @jobs.map do |job|
|
28
|
+
{
|
29
|
+
name: format_name(job),
|
30
|
+
value: job,
|
31
|
+
disabled: !job.resolved? && CANCELLED,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def run(output)
|
37
|
+
return unless @jobs.any?(&:resolved?)
|
38
|
+
|
39
|
+
selected = nil
|
40
|
+
output.puts
|
41
|
+
loop do
|
42
|
+
selected = @prompt.select("Print output?", options, default: format_name(selected))
|
43
|
+
output.puts nil, selected.output, nil
|
44
|
+
rescue TTY::Reader::InputInterrupt
|
45
|
+
output.puts BYE
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def format_name(job)
|
53
|
+
return unless job
|
54
|
+
|
55
|
+
"#{ICONS[job.status]} #{job.name}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CobraCommander
|
4
|
+
module Executor
|
5
|
+
module RunScript
|
6
|
+
def run_script(tty, script, path)
|
7
|
+
result = tty.run!(script, chdir: path, err: :out)
|
8
|
+
|
9
|
+
return [:error, result.out] if result.failed?
|
10
|
+
|
11
|
+
[:success, result.out]
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_many(collection, &block)
|
15
|
+
collection.lazy.map(&block)
|
16
|
+
.reduce do |(_, prev_output), (result, output)|
|
17
|
+
new_output = [prev_output&.strip, output&.strip].join("\n")
|
18
|
+
return [:error, new_output] if result == :error
|
19
|
+
|
20
|
+
[:success, new_output]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,44 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "./job"
|
4
|
-
|
5
3
|
module CobraCommander
|
6
4
|
module Executor
|
7
|
-
# This is a script job. It can
|
5
|
+
# This is a script job. It can target any CobraCommander::Component.
|
8
6
|
#
|
9
|
-
#
|
10
|
-
# individual paths for each given component.
|
7
|
+
# Script runs the given script once for each Component#root_paths
|
11
8
|
#
|
12
|
-
# @see Script.for
|
13
9
|
class Script
|
14
|
-
include ::CobraCommander::Executor::
|
15
|
-
|
16
|
-
# Returns a set of scripts to be executed on the given commends.
|
17
|
-
#
|
18
|
-
# If a component has two packages in the same path, only one script for that component will be
|
19
|
-
# returned.
|
20
|
-
#
|
21
|
-
# @param components [Enumerable<CobraCommander::Component>] the target components
|
22
|
-
# @param script [String] shell script to run from the directories of the component's packages
|
23
|
-
# @return [Array<CobraCommander::Executor::Script>]
|
24
|
-
def self.for(components, script)
|
25
|
-
components.flat_map(&:packages).uniq(&:path).map do |package|
|
26
|
-
new(package, script)
|
27
|
-
end
|
28
|
-
end
|
10
|
+
include ::CobraCommander::Executor::RunScript
|
29
11
|
|
30
|
-
def initialize(
|
31
|
-
@package = package
|
12
|
+
def initialize(script)
|
32
13
|
@script = script
|
33
14
|
end
|
34
15
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
|
41
|
-
|
16
|
+
# Runs the script in the given component
|
17
|
+
#
|
18
|
+
# It runs the script once for each Component#root_paths. If a component has two packages in the
|
19
|
+
# same path, it will run the script only once.
|
20
|
+
#
|
21
|
+
# @param tty [CobraComander::Executor::IsolatedPTY] tty to execute shell scripts
|
22
|
+
# @param component [CobraComander::Component] target component
|
23
|
+
# @return [Array<Symbol, String>]
|
24
|
+
def call(tty, component)
|
25
|
+
run_many(component.root_paths) { run_script(tty, @script, _1) }
|
42
26
|
end
|
43
27
|
end
|
44
28
|
end
|