cobra_commander 0.11.0 → 0.12.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: cf88be500ee51ad9217ce6729399a3dd45ea69bc91f8d0a2f9ffc97521f802a3
4
- data.tar.gz: 13c889293a2173dc698257d1e614a2072b705faca25c3a59d8066e5d09326974
3
+ metadata.gz: 8461ea231fa9be478360d33fbadfb8983512172687831ac7c17ac1f927614200
4
+ data.tar.gz: 0b36b28eadecb6e0bc6e7ee8c3e6db951f7dc7d74be67c91b97659dc7bd52c5e
5
5
  SHA512:
6
- metadata.gz: 2300c5d0b41084a2d6403444c8548710e57a6645e770ee0d1befa029bfa947e738fe0909d33c522a1543b24892580e0acd7dd2504baae7100ed05af5e32e4a45
7
- data.tar.gz: 64e6ff60e979d5ffe2c0c7cceed3cd23733694cc479ad96f484b4af1b6ed421a2aa65ec2e4450699363b3006274784daed0737e27b4b4f4c904123764b1c11ed
6
+ metadata.gz: 39a0956db1750d63553d87e4d13ad75246f4f4633b3d6a42de621c45f11a515506ab5f0d7f14343d61f183d03df7e8218dee8f93a439da023e442d70c7f3b681
7
+ data.tar.gz: 227acbdb961e2903cdaacd44ed4bdd7b07a48e7298737bbe5b6228bd28df8aa6c7aea6a6949de2a3eea999287b633e9bfcd32b7b9e108e3f7f4fcaa7385e2806
@@ -1,9 +1,6 @@
1
1
  name: CI
2
2
 
3
- on:
4
- push:
5
- pull_request:
6
- types: [opened, reopened]
3
+ on: push
7
4
 
8
5
  jobs:
9
6
  test:
@@ -3,7 +3,7 @@ name: Publish Gem
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - master
6
+ - main
7
7
  tags:
8
8
  - v*
9
9
  jobs:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Version 0.12.0 - 2021-09-21
6
+
7
+ * Add interactive UI and Markdown output to `cobra exec` [#63](https://github.com/powerhome/cobra_commander/pull/63)
8
+ * Add `--self` and `--no-self` filters to `cobra exec` and `cobra ls`, defaults to `--self` [#61](https://github.com/powerhome/cobra_commander/pull/61)
9
+
5
10
  ## Version 0.11.0 - 2021-04-23
6
11
 
7
12
  * Add concurrency limit to multi exec [#57](https://github.com/powerhome/cobra_commander/pull/57)
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "ruby-graphviz", "~> 1.2.3"
39
39
  spec.add_dependency "thor", ["< 2.0", ">= 0.18.1"]
40
40
  spec.add_dependency "tty-command", "~> 0.10.0"
41
+ spec.add_dependency "tty-prompt", "~> 0.23.1"
41
42
  spec.add_dependency "tty-spinner", "~> 0.9.3"
42
43
 
43
44
  spec.add_development_dependency "aruba", "~> 0.14.2"
@@ -0,0 +1,30 @@
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, try one of `cobra ls`") || exit(1)
18
+ end
19
+
20
+ def components_filtered(component_name)
21
+ return umbrella.components unless component_name
22
+
23
+ component = find_component(component_name)
24
+ components = options.self ? [component] : []
25
+ components.concat component.deep_dependencies if options.dependencies
26
+ components.concat component.deep_dependents if options.dependents
27
+ components
28
+ end
29
+ end
30
+ end
@@ -13,6 +13,8 @@ require "cobra_commander/output"
13
13
  module CobraCommander
14
14
  # Implements the tool's CLI
15
15
  class CLI < Thor
16
+ require "cobra_commander/cli/filters"
17
+
16
18
  DEFAULT_CONCURRENCY = (Concurrent.processor_count / 2.0).ceil
17
19
 
18
20
  class_option :app, default: Dir.pwd, aliases: "-a", type: :string
@@ -25,10 +27,8 @@ module CobraCommander
25
27
  end
26
28
 
27
29
  desc "ls [component]", "Lists the components in the context of a given component or umbrella"
28
- method_option :dependencies, type: :boolean, aliases: "-d",
29
- desc: "Run the command on each dependency of a given component"
30
- method_option :dependents, type: :boolean, aliases: "-D",
31
- 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"
32
32
  method_option :total, type: :boolean, aliases: "-t", desc: "Prints the total count of components"
33
33
  def ls(component = nil)
34
34
  components = components_filtered(component)
@@ -37,16 +37,24 @@ module CobraCommander
37
37
 
38
38
  desc "exec [component] <command>", "Executes the command in the context of a given component or set thereof. " \
39
39
  "Defaults to all components."
40
- method_option :dependencies, type: :boolean, desc: "Run the command on each dependency of a given component"
41
- method_option :dependents, type: :boolean, desc: "Run the command on each dependency of a given component"
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
42
  method_option :concurrency, type: :numeric, default: DEFAULT_CONCURRENCY, aliases: "-c",
43
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 component"
44
46
  def exec(command_or_component, command = nil)
45
- CobraCommander::Executor.exec(
47
+ results = CobraCommander::Executor.exec(
46
48
  components: components_filtered(command && command_or_component),
47
49
  command: command || command_or_component,
48
- concurrency: options.concurrency
50
+ concurrency: options.concurrency,
51
+ status_output: $stderr
49
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
50
58
  end
51
59
 
52
60
  desc "tree [component]", "Prints the dependency tree of a given component or umbrella"
@@ -80,22 +88,5 @@ module CobraCommander
80
88
  def umbrella
81
89
  @umbrella ||= CobraCommander.umbrella(options.app, yarn: options.js, bundler: options.ruby)
82
90
  end
83
-
84
- def find_component(name)
85
- return umbrella.root unless name
86
-
87
- umbrella.find(name) || error("Component #{name} not found, try one of `cobra ls`") || exit(1)
88
- end
89
-
90
- def components_filtered(component_name)
91
- return umbrella.components unless component_name
92
-
93
- component = find_component(component_name)
94
-
95
- return component.deep_dependencies if options.dependencies
96
- return component.deep_dependents if options.dependents
97
-
98
- [component]
99
- end
100
91
  end
101
92
  end
@@ -1,32 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "tty-spinner"
4
- require "stringio"
5
4
  require "concurrent-ruby"
6
5
 
7
- require_relative "component_exec"
8
-
9
6
  module CobraCommander
10
7
  module Executor
11
- # Execute a command on multiple components
12
- class MultiExec
13
- def initialize(components, concurrency:, spin_output: $stderr)
8
+ # Execute a command on multiple components concurrently
9
+ class Concurrent
10
+ def initialize(components, concurrency:, spin_output:)
14
11
  @components = components
15
12
  @multi = TTY::Spinner::Multi.new(":spinner :task", output: spin_output)
16
- @semaphore = Concurrent::Semaphore.new(concurrency)
13
+ @semaphore = ::Concurrent::Semaphore.new(concurrency)
17
14
  end
18
15
 
19
- def run(command, output: $stdout, **kwargs)
20
- buffer = StringIO.new
16
+ def exec(command)
21
17
  @multi.top_spinner.update(task: "Running #{command}")
18
+ @results = []
22
19
  @components.each do |component|
23
- register_job(command: command, component: component,
24
- output: buffer, stderr: :stdout,
25
- only_output_on_error: true, **kwargs)
20
+ register_job(component, command)
26
21
  end
27
22
  @multi.auto_spin
28
- output << buffer.string
29
- @multi.success?
23
+ @results
30
24
  end
31
25
 
32
26
  private
@@ -43,15 +37,12 @@ module CobraCommander
43
37
  }
44
38
  end
45
39
 
46
- def register_job(component:, command:, **kwargs)
40
+ def register_job(component, command)
47
41
  @multi.register(":spinner #{component.name}", **spinner_options) do |spinner|
48
42
  @semaphore.acquire
49
- exec = ComponentExec.new(component)
50
- if exec.run(command, **kwargs)
51
- spinner.success
52
- else
53
- spinner.error
54
- end
43
+ context = Context.new(component, command)
44
+ context.success? ? spinner.success : spinner.error
45
+ @results << context
55
46
  @semaphore.release
56
47
  end
57
48
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-command"
4
+
5
+ module CobraCommander
6
+ module Executor
7
+ class Context
8
+ attr_reader :component, :command
9
+
10
+ def initialize(component, command)
11
+ @component = component
12
+ @command = command
13
+ @tty = TTY::Command.new(pty: true, printer: :null, stderr: :stdout)
14
+ end
15
+
16
+ def results
17
+ @results ||= @component.root_paths.map do |path|
18
+ isolate_bundle do
19
+ @tty.run!(command, chdir: path)
20
+ end
21
+ end
22
+ end
23
+
24
+ def component_name
25
+ component.name
26
+ end
27
+
28
+ def success?
29
+ results.all?(&:success?)
30
+ end
31
+
32
+ def output
33
+ results.join("\n")
34
+ end
35
+
36
+ private
37
+
38
+ def isolate_bundle(&block)
39
+ if Bundler.respond_to?(:with_unbundled_env)
40
+ Bundler.with_unbundled_env(&block)
41
+ else
42
+ Bundler.with_clean_env(&block)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,19 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "executor/component_exec"
4
- require_relative "executor/multi_exec"
3
+ require_relative "executor/context"
4
+ require_relative "executor/concurrent"
5
5
 
6
6
  module CobraCommander
7
- # Execute commands on all components of a ComponentTree
7
+ # Execute a command on all given components
8
8
  module Executor
9
- def self.exec(components:, command:, concurrency:, output: $stdout, status_output: $stderr)
10
- components = Array(components)
11
- exec = if components.size == 1
12
- ComponentExec.new(components.first)
13
- else
14
- MultiExec.new(components, concurrency: concurrency, spin_output: status_output)
15
- end
16
- exec.run(command, output: 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,5 +1,7 @@
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
@@ -0,0 +1,32 @@
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
+ module InteractivePrinter
10
+ pastel = Pastel.new
11
+ SUCCESS = "#{pastel.green("✔")} %s".freeze
12
+ ERROR = "#{pastel.red("✖")} %s".freeze
13
+ BYE = pastel.decorate("👋 Bye!", :white, :on_black, :bold).freeze
14
+
15
+ def self.run(contexts, output)
16
+ prompt = TTY::Prompt.new
17
+ context_options = contexts.reduce({}) do |options, context|
18
+ template = context.success? ? SUCCESS : ERROR
19
+ options.merge(
20
+ format(template, context.component_name) => context
21
+ )
22
+ end
23
+ loop do
24
+ context = prompt.select("Print output?", context_options)
25
+ output.puts context.output
26
+ end
27
+ rescue TTY::Reader::InputInterrupt
28
+ output.puts "\n\n", BYE
29
+ end
30
+ end
31
+ end
32
+ 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".freeze
11
+ ERROR = "\n## ✖ %s\n".freeze
12
+ OUTPUT = "\n```\n$ %s\n\n%s\n```\n".freeze
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
@@ -3,3 +3,5 @@
3
3
  require_relative "output/flat_list"
4
4
  require_relative "output/ascii_tree"
5
5
  require_relative "output/graph_viz"
6
+ require_relative "output/interactive_printer"
7
+ require_relative "output/markdown_printer"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CobraCommander
4
- VERSION = "0.11.0"
4
+ VERSION = "0.12.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cobra_commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-04-23 00:00:00.000000000 Z
13
+ date: 2021-09-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -88,6 +88,20 @@ dependencies:
88
88
  - - "~>"
89
89
  - !ruby/object:Gem::Version
90
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
98
+ type: :runtime
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 0.23.1
91
105
  - !ruby/object:Gem::Dependency
92
106
  name: tty-spinner
93
107
  requirement: !ruby/object:Gem::Requirement
@@ -237,6 +251,7 @@ files:
237
251
  - lib/cobra_commander/affected.rb
238
252
  - lib/cobra_commander/change.rb
239
253
  - lib/cobra_commander/cli.rb
254
+ - lib/cobra_commander/cli/filters.rb
240
255
  - lib/cobra_commander/component.rb
241
256
  - lib/cobra_commander/dependencies.rb
242
257
  - lib/cobra_commander/dependencies/bundler.rb
@@ -244,12 +259,14 @@ files:
244
259
  - lib/cobra_commander/dependencies/yarn/package_repo.rb
245
260
  - lib/cobra_commander/dependencies/yarn_workspace.rb
246
261
  - lib/cobra_commander/executor.rb
247
- - lib/cobra_commander/executor/component_exec.rb
248
- - lib/cobra_commander/executor/multi_exec.rb
262
+ - lib/cobra_commander/executor/concurrent.rb
263
+ - lib/cobra_commander/executor/context.rb
249
264
  - lib/cobra_commander/output.rb
250
265
  - lib/cobra_commander/output/ascii_tree.rb
251
266
  - lib/cobra_commander/output/flat_list.rb
252
267
  - lib/cobra_commander/output/graph_viz.rb
268
+ - lib/cobra_commander/output/interactive_printer.rb
269
+ - lib/cobra_commander/output/markdown_printer.rb
253
270
  - lib/cobra_commander/umbrella.rb
254
271
  - lib/cobra_commander/version.rb
255
272
  - renovate.json
@@ -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