cobra_commander 0.15.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.rubocop.yml +0 -5
  4. data/Gemfile +7 -0
  5. data/cobra_commander.gemspec +6 -1
  6. data/doc/dependency_decisions.yml +9 -0
  7. data/docs/CHANGELOG.md +13 -0
  8. data/docs/README.md +3 -129
  9. data/exe/cobra +5 -0
  10. data/lib/cobra_commander/affected.rb +17 -49
  11. data/lib/cobra_commander/cli/filters.rb +1 -3
  12. data/lib/cobra_commander/{output → cli/output}/ascii_tree.rb +7 -5
  13. data/lib/cobra_commander/cli/output/change.rb +91 -0
  14. data/lib/cobra_commander/cli/output/dot_graph.rb +21 -0
  15. data/lib/cobra_commander/cli.rb +58 -30
  16. data/lib/cobra_commander/component.rb +6 -10
  17. data/lib/cobra_commander/executor/command.rb +75 -0
  18. data/lib/cobra_commander/executor/execution.rb +52 -0
  19. data/lib/cobra_commander/executor/interactive_printer.rb +53 -0
  20. data/lib/cobra_commander/executor/job.rb +51 -0
  21. data/lib/cobra_commander/executor/markdown_printer.rb +21 -0
  22. data/lib/cobra_commander/executor/package_criteria.rb +21 -0
  23. data/lib/cobra_commander/executor/script.rb +45 -0
  24. data/lib/cobra_commander/executor/spinners.rb +40 -0
  25. data/lib/cobra_commander/executor.rb +37 -6
  26. data/lib/cobra_commander/git_changed.rb +16 -11
  27. data/lib/cobra_commander/package.rb +21 -0
  28. data/lib/cobra_commander/registry.rb +29 -0
  29. data/lib/cobra_commander/source.rb +37 -0
  30. data/lib/cobra_commander/umbrella.rb +70 -17
  31. data/lib/cobra_commander/version.rb +1 -1
  32. data/lib/cobra_commander.rb +10 -16
  33. data/mkdocs.yml +1 -1
  34. metadata +37 -43
  35. data/.editorconfig +0 -11
  36. data/.github/workflows/ci.yml +0 -44
  37. data/.github/workflows/release.yml +0 -20
  38. data/.rubocop_todo.yml +0 -15
  39. data/LICENSE.txt +0 -21
  40. data/_config.yml +0 -1
  41. data/docs/CODE_OF_CONDUCT.md +0 -74
  42. data/gemfiles/bundler1.gemfile +0 -7
  43. data/gemfiles/bundler2.gemfile +0 -7
  44. data/lib/cobra_commander/change.rb +0 -87
  45. data/lib/cobra_commander/dependencies/bundler/package.rb +0 -17
  46. data/lib/cobra_commander/dependencies/bundler.rb +0 -44
  47. data/lib/cobra_commander/dependencies/yarn/package.rb +0 -21
  48. data/lib/cobra_commander/dependencies/yarn.rb +0 -40
  49. data/lib/cobra_commander/dependencies.rb +0 -4
  50. data/lib/cobra_commander/executor/concurrent.rb +0 -51
  51. data/lib/cobra_commander/executor/context.rb +0 -49
  52. data/lib/cobra_commander/output/flat_list.rb +0 -16
  53. data/lib/cobra_commander/output/graph_viz.rb +0 -27
  54. data/lib/cobra_commander/output/interactive_printer.rb +0 -53
  55. data/lib/cobra_commander/output/markdown_printer.rb +0 -24
  56. data/lib/cobra_commander/output.rb +0 -7
  57. data/portal.yml +0 -12
  58. data/renovate.json +0 -8
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CobraCommander
4
- module Dependencies
5
- # Calculates ruby bundler dependencies
6
- class Bundler::Package
7
- attr_reader :path, :name, :dependencies
8
-
9
- def initialize(spec = nil, path: spec&.loaded_from, name: spec&.name, dependencies: spec&.dependencies)
10
- @spec = spec
11
- @path = path
12
- @name = name
13
- @dependencies = dependencies&.map(&:name)
14
- end
15
- end
16
- end
17
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler"
4
- require "bundler/lockfile_parser"
5
- require "pathname"
6
-
7
- module CobraCommander
8
- module Dependencies
9
- # Calculates ruby bundler dependencies
10
- class Bundler
11
- autoload :Package, "cobra_commander/dependencies/bundler/package"
12
-
13
- attr_reader :path
14
-
15
- def initialize(root)
16
- @root = Pathname.new(root)
17
- @path = @root.join("Gemfile.lock").realpath
18
- end
19
-
20
- def root
21
- Package.new(path: path, dependencies: lockfile.dependencies.values)
22
- end
23
-
24
- def packages
25
- @packages ||= components_source.specs.map do |spec|
26
- Package.new(spec)
27
- end
28
- end
29
-
30
- private
31
-
32
- def lockfile
33
- @lockfile ||= ::Bundler::LockfileParser.new(::Bundler.read_file(path))
34
- end
35
-
36
- def components_source
37
- @components_source ||= begin
38
- source = @lockfile.sources.find { |s| s.path.to_s.eql?("components") }
39
- ::Bundler::Source::Path.new(source.options.merge("root_path" => @root))
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CobraCommander
4
- module Dependencies
5
- class Yarn::Package
6
- attr_reader :path, :name, :dependencies
7
-
8
- def initialize(path:, dependencies:, name: nil)
9
- @path = path
10
- @name = untag(name)
11
- @dependencies = dependencies.map { |dep| untag(dep) }
12
- end
13
-
14
- private
15
-
16
- def untag(name)
17
- name&.gsub(%r{^@[\w-]+/}, "")
18
- end
19
- end
20
- end
21
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "open3"
5
- require "pathname"
6
-
7
- module CobraCommander
8
- module Dependencies
9
- # Yarn workspace components source for an umbrella
10
- class Yarn
11
- PACKAGE_FILE = "package.json"
12
-
13
- autoload :Package, "cobra_commander/dependencies/yarn/package"
14
-
15
- def initialize(root_path)
16
- @root_path = Pathname.new(root_path)
17
- end
18
-
19
- def root
20
- @root ||= Package.new(
21
- path: @root_path.join(PACKAGE_FILE).realpath,
22
- dependencies: packages.map(&:name)
23
- )
24
- end
25
-
26
- def packages
27
- @packages ||= begin
28
- output, = Open3.capture2("yarn workspaces --json info", chdir: @root_path.to_s)
29
- JSON.parse(JSON.parse(output)["data"]).map do |name, spec|
30
- Package.new(
31
- path: @root_path.join(spec["location"], PACKAGE_FILE).realpath,
32
- dependencies: spec["workspaceDependencies"],
33
- name: name
34
- )
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "dependencies/yarn"
4
- require_relative "dependencies/bundler"
@@ -1,51 +0,0 @@
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
@@ -1,49 +0,0 @@
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,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CobraCommander
4
- module Output
5
- # Prints a list of components' names sorted alphabetically
6
- class FlatList
7
- def initialize(components)
8
- @components = components
9
- end
10
-
11
- def to_s
12
- @components.map(&:name).sort
13
- end
14
- end
15
- end
16
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "graphviz"
4
-
5
- module CobraCommander
6
- module Output
7
- # Generates graphs of components
8
- module GraphViz
9
- def self.generate(component, output)
10
- g = ::GraphViz.new(:G, type: :digraph, concentrate: true)
11
- ([component] + component.deep_dependencies).each do |comp|
12
- g.add_nodes comp.name
13
- g.add_edges comp.name, comp.dependencies.map(&:name)
14
- end
15
-
16
- g.output(extract_format(output) => output)
17
- end
18
-
19
- private_class_method def self.extract_format(output)
20
- format = output[-3..]
21
- return format if %w[png dot].include?(format)
22
-
23
- raise ArgumentError, "output format must be 'png' or 'dot'"
24
- end
25
- end
26
- end
27
- end
@@ -1,53 +0,0 @@
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 = map_options(contexts)
22
- end
23
-
24
- def run(output)
25
- selected_context = nil
26
- loop do
27
- selected_context = @prompt.select("Print output?", @options, default: @options.key(selected_context))
28
- output.puts selected_context.output
29
- end
30
- rescue TTY::Reader::InputInterrupt
31
- output.puts BYE
32
- end
33
-
34
- private
35
-
36
- def map_options(contexts)
37
- contexts.sort(&method(:sort_contexts))
38
- .reduce({}) do |options, context|
39
- template = context.success? ? SUCCESS : ERROR
40
- options.merge format(template, context.component_name) => context
41
- end
42
- end
43
-
44
- def sort_contexts(context_a, context_b)
45
- if context_a.success? == context_b.success?
46
- context_a.component_name <=> context_b.component_name
47
- else
48
- context_a.success? ? 1 : -1
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,24 +0,0 @@
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
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "output/flat_list"
4
- require_relative "output/ascii_tree"
5
- require_relative "output/graph_viz"
6
- require_relative "output/interactive_printer"
7
- require_relative "output/markdown_printer"
data/portal.yml DELETED
@@ -1,12 +0,0 @@
1
- apiVersion: backstage.io/v1alpha1
2
- kind: Component
3
- metadata:
4
- name: cobra-commander
5
- title: Cobra Commander
6
- description: Tools for working with Component Based Rails Apps
7
- annotations:
8
- backstage.io/techdocs-ref: dir:.
9
- spec:
10
- type: library
11
- owner: heroes-for-hire
12
- lifecycle: production
data/renovate.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": [
3
- "config:base"
4
- ],
5
- "ignorePaths": [
6
- "spec/fixtures"
7
- ]
8
- }