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.
- checksums.yaml +4 -4
- data/.gitignore +0 -2
- data/.rubocop.yml +0 -5
- data/Gemfile +7 -0
- data/cobra_commander.gemspec +6 -1
- data/doc/dependency_decisions.yml +9 -0
- data/docs/CHANGELOG.md +13 -0
- data/docs/README.md +3 -129
- data/exe/cobra +5 -0
- data/lib/cobra_commander/affected.rb +17 -49
- data/lib/cobra_commander/cli/filters.rb +1 -3
- data/lib/cobra_commander/{output → cli/output}/ascii_tree.rb +7 -5
- data/lib/cobra_commander/cli/output/change.rb +91 -0
- data/lib/cobra_commander/cli/output/dot_graph.rb +21 -0
- data/lib/cobra_commander/cli.rb +58 -30
- data/lib/cobra_commander/component.rb +6 -10
- data/lib/cobra_commander/executor/command.rb +75 -0
- data/lib/cobra_commander/executor/execution.rb +52 -0
- data/lib/cobra_commander/executor/interactive_printer.rb +53 -0
- data/lib/cobra_commander/executor/job.rb +51 -0
- data/lib/cobra_commander/executor/markdown_printer.rb +21 -0
- data/lib/cobra_commander/executor/package_criteria.rb +21 -0
- data/lib/cobra_commander/executor/script.rb +45 -0
- data/lib/cobra_commander/executor/spinners.rb +40 -0
- data/lib/cobra_commander/executor.rb +37 -6
- data/lib/cobra_commander/git_changed.rb +16 -11
- data/lib/cobra_commander/package.rb +21 -0
- data/lib/cobra_commander/registry.rb +29 -0
- data/lib/cobra_commander/source.rb +37 -0
- data/lib/cobra_commander/umbrella.rb +70 -17
- data/lib/cobra_commander/version.rb +1 -1
- data/lib/cobra_commander.rb +10 -16
- data/mkdocs.yml +1 -1
- metadata +37 -43
- data/.editorconfig +0 -11
- data/.github/workflows/ci.yml +0 -44
- data/.github/workflows/release.yml +0 -20
- data/.rubocop_todo.yml +0 -15
- data/LICENSE.txt +0 -21
- data/_config.yml +0 -1
- data/docs/CODE_OF_CONDUCT.md +0 -74
- data/gemfiles/bundler1.gemfile +0 -7
- data/gemfiles/bundler2.gemfile +0 -7
- data/lib/cobra_commander/change.rb +0 -87
- data/lib/cobra_commander/dependencies/bundler/package.rb +0 -17
- data/lib/cobra_commander/dependencies/bundler.rb +0 -44
- data/lib/cobra_commander/dependencies/yarn/package.rb +0 -21
- data/lib/cobra_commander/dependencies/yarn.rb +0 -40
- data/lib/cobra_commander/dependencies.rb +0 -4
- data/lib/cobra_commander/executor/concurrent.rb +0 -51
- data/lib/cobra_commander/executor/context.rb +0 -49
- data/lib/cobra_commander/output/flat_list.rb +0 -16
- data/lib/cobra_commander/output/graph_viz.rb +0 -27
- data/lib/cobra_commander/output/interactive_printer.rb +0 -53
- data/lib/cobra_commander/output/markdown_printer.rb +0 -24
- data/lib/cobra_commander/output.rb +0 -7
- data/portal.yml +0 -12
- 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,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
|
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
|