cobra_commander 0.9.2 → 0.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 187d07e9fd8516472b7db839d6b8076f48f579517e1735697e8f97bc41185917
4
- data.tar.gz: a2bac5849bbc81dacd1befa077cb8ba711b653dd3ad586cee7d270342b18dbab
3
+ metadata.gz: 4eb007805f5f11954ed14e349409c7486fc6b52d0a041f8066c75c47f8d045b8
4
+ data.tar.gz: 68ac383fb087d118a276e5aed0199b51fd09b05c110d7ff4539f18097b1ea367
5
5
  SHA512:
6
- metadata.gz: 7cad76ed9b06db481cefb21a7d71072c53ef7f073e4e1afe0185e506b04a1cd197d0551ebf4b3a8c8a3eebd7388f870dd7e8e3824084fafa6607a8d7c9058950
7
- data.tar.gz: 3d7bcff87851fd52d162502282aca2a7f173e78e6f73cf9628ee029b5cdf58c6ad05b8fb211bbd6ad77bbc4aa610a86f207f951f1657d7ba7956d47f1b4a6add
6
+ metadata.gz: 6a5f69e411bf49bd1de717934f27c0d364378e07ce1728855b2783de0c73dff791be433565b689656d4481b2ad294086fc43118beafc16ca445a2c91ea7916c9
7
+ data.tar.gz: afdf327649c953bc155f5e01a6db9f4cef12ed625f7c791534e7ae65ac0d05c2260bfd20b03f382eac9aaee9f2e84612806c050327491c2f806223d7e612aa67
@@ -1,24 +1,33 @@
1
1
  name: CI
2
2
 
3
- on: [push, pull_request]
3
+ on: push
4
4
 
5
5
  jobs:
6
6
  test:
7
7
  name: Tests
8
8
  runs-on: ubuntu-latest
9
9
  strategy:
10
+ fail-fast: false
10
11
  matrix:
11
12
  ruby:
12
13
  - "2.5"
13
14
  - "2.6"
15
+ - "2.7"
16
+ - "3.0"
17
+ bundler:
18
+ - "1"
19
+ - "2"
20
+ env:
21
+ BUNDLE_GEMFILE: gemfiles/bundler${{ matrix.bundler }}.gemfile
14
22
  steps:
15
23
  - uses: actions/checkout@v2
16
- - name: Set up Ruby
17
- uses: ruby/setup-ruby@v1
24
+ - uses: ruby/setup-ruby@v1
18
25
  with:
19
26
  ruby-version: ${{ matrix.ruby }}
20
- bundler: 1
27
+ bundler: ${{ matrix.bundler }}
21
28
  bundler-cache: true
29
+ - name: Install bundler v1 # Even if we're testing bundler v2, we need v1 available because the fixture components have it in their Gemfile.locks
30
+ run: gem install bundler -v "~> 1" --no-document
22
31
  - name: Install Graphviz
23
32
  run: sudo apt -qq install graphviz
24
33
  - name: Run tests
@@ -28,11 +37,10 @@ jobs:
28
37
  runs-on: ubuntu-latest
29
38
  steps:
30
39
  - uses: actions/checkout@v2
31
- - name: rubocop
32
- uses: reviewdog/action-rubocop@v1
40
+ - uses: ruby/setup-ruby@v1
33
41
  with:
34
- rubocop_version: 0.88.0
35
- filter_mode: nofilter
36
- fail_on_error: true
37
- rubocop_extensions: ""
38
- github_token: ${{ secrets.github_token }}
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/.gitignore CHANGED
@@ -13,3 +13,6 @@
13
13
  /spec/fixtures/app/node_manifest/node_modules/
14
14
  /spec/fixtures/app/components/*/node_modules/
15
15
  /tmp/
16
+
17
+ .bundle
18
+ gemfiles/*.lock
data/CHANGELOG.md CHANGED
@@ -2,9 +2,38 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Version 0.13.0 - 2021-10-21
6
+
7
+ * Sorts the interactive UI alphabetically with failures first [#66](https://github.com/powerhome/cobra_commander/pull/66)
8
+ * Switch linter to standardrb [#67](https://github.com/powerhome/cobra_commander/pull/67)
9
+
10
+ ## Version 0.12.0 - 2021-09-21
11
+
12
+ * Add interactive UI and Markdown output to `cobra exec` [#63](https://github.com/powerhome/cobra_commander/pull/63)
13
+ * Add `--self` and `--no-self` filters to `cobra exec` and `cobra ls`, defaults to `--self` [#61](https://github.com/powerhome/cobra_commander/pull/61)
14
+
15
+ ## Version 0.11.0 - 2021-04-23
16
+
17
+ * Add concurrency limit to multi exec [#57](https://github.com/powerhome/cobra_commander/pull/57)
18
+ * Ruby 3.0 compatibility [#55](https://github.com/powerhome/cobra_commander/pull/55)
19
+
20
+ ## Version 0.10.0 - 2021-02-25
21
+
22
+ * Add support for Bundler 2 [#54](https://github.com/powerhome/cobra_commander/pull/54)
23
+ * Add support for Ruby 2.7 [#53](https://github.com/powerhome/cobra_commander/pull/53)
24
+
25
+ ## Version 0.9.2 -
26
+
27
+ * Another fix for binstubs [#51](https://github.com/powerhome/cobra_commander/pull/51)
28
+
29
+ ## Version 0.9.1 -
30
+
31
+ * Replace bundler encapsulation violation by LockfileParser [#48](https://github.com/powerhome/cobra_commander/pull/48)
32
+ * Fix bundle binstubs [#50](https://github.com/powerhome/cobra_commander/pull/50)
33
+
5
34
  ## Version 0.9.0 - 2020-08-26
6
35
 
7
- * Add support for parallel task execution to `cobra exec`.
36
+ * Add support for parallel task execution to `cobra exec` [#49](https://github.com/powerhome/cobra_commander/pull/49)
8
37
 
9
38
  ## Version 0.8.1 - 2020-07-29
10
39
 
data/Gemfile CHANGED
@@ -2,5 +2,4 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- # Specify your gem's dependencies in cbra.gemspec
6
5
  gemspec
data/README.md CHANGED
@@ -2,11 +2,10 @@
2
2
 
3
3
  [![Gem](https://img.shields.io/gem/dv/cobra_commander/stable.svg)](https://rubygems.org/gems/cobra_commander)
4
4
  [![Gem](https://img.shields.io/gem/v/cobra_commander.svg)](https://rubygems.org/gems/cobra_commander)
5
- [![Travis](https://img.shields.io/travis/powerhome/cobra_commander.svg)](https://travis-ci.org/powerhome/cobra_commander)
6
- [![Code Climate](https://img.shields.io/codeclimate/github/powerhome/cobra_commander.svg)](https://codeclimate.com/github/powerhome/cobra_commander)
7
- [![Gemnasium](https://img.shields.io/gemnasium/powerhome/cobra_commander.svg)](https://gemnasium.com/github.com/powerhome/cobra_commander)
5
+ [![CI](https://github.com/powerhome/cobra_commander/actions/workflows/ci.yml/badge.svg)](https://github.com/powerhome/cobra_commander/actions/workflows/ci.yml)
6
+ [![Maintainability](https://api.codeclimate.com/v1/badges/7fe0781c18f6923ab753/maintainability)](https://codeclimate.com/github/powerhome/cobra_commander/maintainability)
8
7
 
9
- Tools for working with Component Based Rails Apps (see http://shageman.github.io/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.
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
- require "rubocop/rake_task"
9
- RuboCop::RakeTask.new
7
+ RSpec::Core::RakeTask.new(:spec)
10
8
 
11
- task default: %i[spec rubocop]
9
+ task default: %i[spec standard]
@@ -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 = "cobra_commander"
9
- spec.version = CobraCommander::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 = "Tools for working with Component Based Rails Apps"
21
- spec.description = <<~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 = "http://tech.powerhrg.com/cobra_commander/"
27
- spec.license = "MIT"
26
+ spec.homepage = "http://tech.powerhrg.com/cobra_commander/"
27
+ spec.license = "MIT"
28
28
 
29
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
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 = "exe"
33
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
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
- spec.add_dependency "bundler", "~> 1.17"
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.9.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
- spec.add_development_dependency "bundler", "~> 1.17"
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", "~> 10.0"
48
+ spec.add_development_dependency "rake", ">= 12.3.3"
47
49
  spec.add_development_dependency "rspec", "~> 3.5"
48
- spec.add_development_dependency "rubocop", "0.88.0"
50
+ spec.add_development_dependency "standard", ">= 1.3.0"
49
51
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: "../"
6
+
7
+ gem "bundler", "~> 1.17.3"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: "../"
6
+
7
+ gem "bundler", "~> 2.2.11"
@@ -25,21 +25,18 @@ module CobraCommander
25
25
  @transitively.map(&method(:affected_component))
26
26
  end
27
27
 
28
- def json_representation # rubocop:disable Metrics/MethodLength
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
- private
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
- private
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,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
@@ -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
- method_option :dependencies, type: :boolean, aliases: "-d",
26
- desc: "Run the command on each dependency of a given component"
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
- method_option :dependencies, type: :boolean, desc: "Run the command on each dependency of a given component"
38
- 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
+ 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
- private
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
@@ -45,8 +45,8 @@ module CobraCommander
45
45
 
46
46
  def dependencies
47
47
  @dependencies ||= @dependency_names.sort
48
- .map(&@umbrella.method(:find))
49
- .compact
48
+ .map(&@umbrella.method(:find))
49
+ .compact
50
50
  end
51
51
  end
52
52
  end
@@ -11,8 +11,8 @@ module CobraCommander
11
11
  attr_reader :path
12
12
 
13
13
  def initialize(root)
14
- @root = root
15
- @path = Pathname.new(File.join(root, "Gemfile.lock")).realpath
14
+ @root = Pathname.new(root)
15
+ @path = @root.join("Gemfile.lock").realpath
16
16
  end
17
17
 
18
18
  def dependencies
@@ -21,11 +21,11 @@ module CobraCommander
21
21
 
22
22
  def components
23
23
  components_source.specs.map do |spec|
24
- { path: spec.loaded_from, name: spec.name, dependencies: spec.dependencies.map(&:name) }
24
+ {path: spec.loaded_from, name: spec.name, dependencies: spec.dependencies.map(&:name)}
25
25
  end
26
26
  end
27
27
 
28
- private
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
- .merge(json.fetch("devDependencies", {}))
27
+ .merge(json.fetch("devDependencies", {}))
28
28
  end
29
29
 
30
- private
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
- { path: spec.path, name: untag(spec.name), dependencies: spec.dependencies.keys.map(&method(:untag)) }
31
+ {path: spec.path, name: untag(spec.name), dependencies: spec.dependencies.keys.map(&method(:untag))}
32
32
  end
33
33
  end
34
34
 
35
- private
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/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, 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)
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
- private
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,48 @@
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 = contexts.sort(&method(:sort_contexts))
22
+ .reduce({}) do |options, context|
23
+ template = context.success? ? SUCCESS : ERROR
24
+ options.merge format(template, context.component_name) => context
25
+ end
26
+ end
27
+
28
+ def run(output)
29
+ loop do
30
+ context = @prompt.select("Print output?", @options)
31
+ output.puts context.output
32
+ end
33
+ rescue TTY::Reader::InputInterrupt
34
+ output.puts BYE
35
+ end
36
+
37
+ private
38
+
39
+ def sort_contexts(context_a, context_b)
40
+ if context_a.success? == context_b.success?
41
+ context_a.component_name <=> context_b.component_name
42
+ else
43
+ context_a.success? ? 1 : -1
44
+ end
45
+ end
46
+ end
47
+ end
48
+ 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
@@ -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"
@@ -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 |path:, name:, dependencies:|
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CobraCommander
4
- VERSION = "0.9.2"
4
+ VERSION = "0.13.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.9.2
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
@@ -10,22 +10,36 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2020-09-23 00:00:00.000000000 Z
13
+ date: 2021-10-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: concurrent-ruby
17
31
  requirement: !ruby/object:Gem::Requirement
18
32
  requirements:
19
33
  - - "~>"
20
34
  - !ruby/object:Gem::Version
21
- version: '1.17'
35
+ version: '1.1'
22
36
  type: :runtime
23
37
  prerelease: false
24
38
  version_requirements: !ruby/object:Gem::Requirement
25
39
  requirements:
26
40
  - - "~>"
27
41
  - !ruby/object:Gem::Version
28
- version: '1.17'
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.9.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.9.0
104
+ version: 0.23.1
77
105
  - !ruby/object:Gem::Dependency
78
106
  name: tty-spinner
79
107
  requirement: !ruby/object:Gem::Requirement
@@ -106,16 +134,16 @@ dependencies:
106
134
  name: bundler
107
135
  requirement: !ruby/object:Gem::Requirement
108
136
  requirements:
109
- - - "~>"
137
+ - - ">="
110
138
  - !ruby/object:Gem::Version
111
- version: '1.17'
139
+ version: '0'
112
140
  type: :development
113
141
  prerelease: false
114
142
  version_requirements: !ruby/object:Gem::Requirement
115
143
  requirements:
116
- - - "~>"
144
+ - - ">="
117
145
  - !ruby/object:Gem::Version
118
- version: '1.17'
146
+ version: '0'
119
147
  - !ruby/object:Gem::Dependency
120
148
  name: guard-rspec
121
149
  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: '10.0'
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: '10.0'
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: rubocop
204
+ name: standard
177
205
  requirement: !ruby/object:Gem::Requirement
178
206
  requirements:
179
- - - '='
207
+ - - ">="
180
208
  - !ruby/object:Gem::Version
181
- version: 0.88.0
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: 0.88.0
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"
@@ -216,10 +245,13 @@ files:
216
245
  - bin/setup
217
246
  - cobra_commander.gemspec
218
247
  - exe/cobra
248
+ - gemfiles/bundler1.gemfile
249
+ - gemfiles/bundler2.gemfile
219
250
  - lib/cobra_commander.rb
220
251
  - lib/cobra_commander/affected.rb
221
252
  - lib/cobra_commander/change.rb
222
253
  - lib/cobra_commander/cli.rb
254
+ - lib/cobra_commander/cli/filters.rb
223
255
  - lib/cobra_commander/component.rb
224
256
  - lib/cobra_commander/dependencies.rb
225
257
  - lib/cobra_commander/dependencies/bundler.rb
@@ -227,12 +259,14 @@ files:
227
259
  - lib/cobra_commander/dependencies/yarn/package_repo.rb
228
260
  - lib/cobra_commander/dependencies/yarn_workspace.rb
229
261
  - lib/cobra_commander/executor.rb
230
- - lib/cobra_commander/executor/component_exec.rb
231
- - lib/cobra_commander/executor/multi_exec.rb
262
+ - lib/cobra_commander/executor/concurrent.rb
263
+ - lib/cobra_commander/executor/context.rb
232
264
  - lib/cobra_commander/output.rb
233
265
  - lib/cobra_commander/output/ascii_tree.rb
234
266
  - lib/cobra_commander/output/flat_list.rb
235
267
  - lib/cobra_commander/output/graph_viz.rb
268
+ - lib/cobra_commander/output/interactive_printer.rb
269
+ - lib/cobra_commander/output/markdown_printer.rb
236
270
  - lib/cobra_commander/umbrella.rb
237
271
  - lib/cobra_commander/version.rb
238
272
  - renovate.json
@@ -255,8 +289,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
289
  - !ruby/object:Gem::Version
256
290
  version: '0'
257
291
  requirements: []
258
- rubyforge_project:
259
- rubygems_version: 2.7.3
292
+ rubygems_version: 3.0.3
260
293
  signing_key:
261
294
  specification_version: 4
262
295
  summary: Tools for working with Component Based Rails Apps
@@ -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