cobra_commander 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|