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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5f6e4d7e8d890c46aed5efff6219f669e79f64325f6f73c899cf6a0c5959088
4
- data.tar.gz: 3fe47e6df4f555345c640051ff6aebf25dea8a5907507e6be4ebcc18876897c1
3
+ metadata.gz: 3e4912ef5e1373f254652d50f2c88bd5d6fc2785675f2d53bdf3572ffcbfafaf
4
+ data.tar.gz: 5c0bdd82e4ea3fcffeb1f8d7a44aece59ca929d40171f3c22b6a3c95d951b942
5
5
  SHA512:
6
- metadata.gz: 0add3576fb98e23aa8332939c2c19d7b4dd04cb82ab3d7cebb12191687fbe72e43efad27ddfa24c80d4393d63898f8f01915af1aa6a74a2331c00c1f57dc99a6
7
- data.tar.gz: '03942e8553ee6163ad3703ce4be836ec2d538ea908cbd32d7fcd274a01b316a37f77407e32758c8d57043ec3e5a0b30007b7398e1c1a0d21018e245d8c2c09d6'
6
+ metadata.gz: 3d8882fe6141cc4ddc6c7ec92616aceb7010c3d889b8c6b7f2d039d589fcd1c9bed43d8dcac946f239057f27eb2ab51c5cd65a181a838401cff2e38f6edb1e8d
7
+ data.tar.gz: ceda5086044a31eae40445597289fae8c23aef320160b07b8d9a1b3fc33ea9318eb2815e97781af5a70f4295842bc59e07e9cc562473fa92933c837db47a9f36
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("lib", __dir__)
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 = `git ls-files -z`.split("\x0").reject do |f|
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 = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
39
- spec.require_paths = ["lib"]
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 names
14
- @names ||= all.map(&:name)
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
@@ -29,7 +29,7 @@ module CobraCommander
29
29
 
30
30
  def affected_by_changes(origin_branch)
31
31
  changes = GitChanged.new(umbrella.path, origin_branch)
32
- Affected.new(umbrella, changes).all
32
+ Affected.new(umbrella, changes).to_a
33
33
  end
34
34
 
35
35
  def filter_component(component_name)
@@ -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
- InvalidSelectionError = Class.new(StandardError)
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
- assert_valid_result_choice
22
- if selected_format?("json")
23
- puts affected.to_json
24
- else
25
- show_full if selected_format?("full")
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 show_full
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 directly_affected_components
35
+ def print_directly_affected_components
61
36
  puts "<<< Directly affected components >>>"
62
- affected.directly.each { |component| puts display(component) }
63
- puts blank_line
37
+ affected.directly.each { |component| display(component) }
64
38
  end
65
39
 
66
- def transitively_affected_components
40
+ def print_transitively_affected_components
67
41
  puts "<<< Transitively affected components >>>"
68
- affected.transitively.each { |component| puts display(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
@@ -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, default: DEFAULT_CONCURRENCY, aliases: "-c",
43
- desc: "Max number of jobs to run concurrently"
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
- jobs = CobraCommander::Executor::Script.for(
49
- components_filtered(script && script_or_components),
50
- script || script_or_components
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, default: DEFAULT_CONCURRENCY, aliases: "-c",
63
- desc: "Max number of jobs to run concurrently"
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
- jobs = CobraCommander::Executor::Command.for(
69
- components_filtered(command && command_or_components),
70
- command || command_or_components
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.results, options.branch).run!
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)
@@ -12,6 +12,10 @@ module CobraCommander
12
12
  @packages = []
13
13
  end
14
14
 
15
+ def describe
16
+ "#{name} (#{packages.map(&:key).join(', ')})"
17
+ end
18
+
15
19
  def add_package(package)
16
20
  @packages << package
17
21
  @dependency_names |= package.dependencies
@@ -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
- # Builds the given commands in all packages of all components.
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 commands [Enumerable<CobraCommander::Component>] the target components
31
- # @return [Array<CobraCommander::Executor::Command>]
32
- # @see CobraCommander::Executor::Job
33
- def self.join(commands)
34
- commands.lazy.map(&:call).reduce do |(_, prev_output), (result, output)|
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
- def initialize(package, command)
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
- # @see CobraCommander::Executor::Job
52
- def call
53
- command = @package.source.config&.dig("commands", @command)
54
- case command
55
- when Array then run_multiple(@package, command)
56
- when Hash then run_with_criteria(command)
57
- when nil then skip(format(SKIP_UNEXISTING, @command, @package.key))
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
- private
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"], @package.path)
42
+ run_script(tty, command["run"], package.path)
68
43
  end
69
44
 
70
- def run_multiple(package, commands)
71
- Command.join(commands.map { |command| Command.new(package, command) })
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
@@ -1,8 +1,5 @@
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
  module PackageCriteria
@@ -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 tarket any CobraCommander::Package.
5
+ # This is a script job. It can target any CobraCommander::Component.
8
6
  #
9
- # If you want to target a Component, you can use Script.for to target
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::Job
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(package, script)
31
- @package = package
12
+ def initialize(script)
32
13
  @script = script
33
14
  end
34
15
 
35
- def to_s
36
- @package.name
37
- end
38
-
39
- # @see CobraCommander::Executor::Job
40
- def call
41
- run_script @script, @package.path
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