cobra_commander 0.9.2 → 0.13.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: 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