monoz 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3809e6abcde49688db77aeb2c395d7293a68528ac2a3a3fab51a753de5040048
4
+ data.tar.gz: 138e119bf0ba056dd1f1ca54061b1d24e5c507a25e42a035a8639d2d548900e2
5
+ SHA512:
6
+ metadata.gz: 5b52995d260a8ef32d5ee8341624f80e96ab5836b35995033ecbb2cc54987757102d0e4b8b4e9cebe1bf28190ce5895d8d5f6896700ec17e1d43bfccd3c28ff3
7
+ data.tar.gz: fb384e4182854c0ab3633869e62283306af0a24f3bb030e99bc43514f1bf68f5107651b344480f4b22838890963d9233b50a3d7e31a83c55ef53fbeaf1ae2895
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # Monoz
2
+
3
+ Monoz is a command-line tool that helps you manage your **ruby** monorepo. It provides an easy way to manage multiple related **ruby** projects and their dependencies in a single repository. Monoz helps you keep track of your projects and their interdependencies, making it easier to maintain and scale your codebase.
4
+
5
+ ## Installation
6
+
7
+ You can install Monoz by running the following command:
8
+
9
+ ```console
10
+ $ gem install monoz
11
+ ```
12
+
13
+ ## Getting started
14
+
15
+ To initialize a Monoz repository in the current directory, simply run:
16
+
17
+ ```console
18
+ $ monoz init
19
+ ```
20
+ *For a specific directory, provide the path as an argument.*
21
+
22
+
23
+ This will create a new Monoz repo with the following structure:
24
+
25
+ ```
26
+ ├── .git
27
+ ├── apps
28
+ ├── gems
29
+ └── monoz.yml
30
+ ```
31
+
32
+ ### Adding projects
33
+
34
+ Once you've initialized your Monoz repository, you can start adding projects to it.
35
+
36
+ To add a new project, simply create a new directory in either the `apps/` or `gems/` directory, depending on whether it's an application or a library.
37
+
38
+ For example, a Monoz repository with both a frontend and a backend application that shares the same libraries could look like this:
39
+
40
+ ```
41
+ ├── .git
42
+ ├── apps
43
+ │ ├── example-com
44
+ │ └── example-com-admin
45
+ ├── gems
46
+ │ ├── myengine
47
+ │ ├── sharedmodels
48
+ │ └── privatelibrary
49
+ └── monoz.yml
50
+ ```
51
+
52
+ ### Bundle projects
53
+
54
+ To manage the dependencies of your projects, you can use the monoz bundle command. This command will create a new Gemfile.lock file in each project directory based on the dependencies specified in their respective Gemfiles.
55
+
56
+ To bundle all the projects in the Monoz repository, simply run:
57
+
58
+ ```console
59
+ $ monoz bundle
60
+ ```
61
+
62
+ This will create a `Gemfile.lock` file for each project in the repository.
63
+
64
+ If you want to update the dependencies of your projects, you can use the `monoz bundle update` command. This command will update the `Gemfile.lock` file of each project based on the latest available versions of their dependencies.
65
+
66
+ To update the dependencies of all projects in the Monoz repository, simply run:
67
+
68
+ ```console
69
+ $ monoz bundle update
70
+ ```
71
+
72
+ Note that when you add a new dependency to a project, you'll need to run `monoz bundle` to update its `Gemfile.lock` file before you can use the new dependency. Similarly, if you update the dependencies of a project's `Gemfile`, you'll need to run `monoz bundle` to update its `Gemfile.lock` file with the new versions.
73
+
74
+ ### Inspect projects
75
+
76
+ You can inspect the projects in your Monoz repository using the monoz inspect command. This command will display a table that shows the projects in the repository, their type (app or gem), the gem name (if it's a gem), the test framework(s) used, and the projects that depend on them.
77
+
78
+ Here's an example output of the monoz inspect command:
79
+
80
+ ```console
81
+ $ monoz inspect
82
+
83
+ o---------------o--------o-------------o---------------------o-------------------------------------o
84
+ | Project | Type | Gem Name | Test Framework(s) | Dependants |
85
+ o---------------o--------o-------------o---------------------o-------------------------------------o
86
+ | content_api | app | | rspec | |
87
+ | core_api | app | | rspec | |
88
+ | kiqr_cloud | app | | rspec | |
89
+ | kiqr_core | gem | kiqr_core | rspec | content_api, core_api, kiqr_cloud |
90
+ o---------------o--------o-------------o---------------------o-------------------------------------o
91
+ ```
92
+
93
+ ## Contributing
94
+ We welcome contributions from everyone! If you're interested in contributing to Monoz, please check out our contributing guidelines for more information.
95
+
96
+ ## License
97
+
98
+ Monoz is released under the MIT License.
data/bin/monoz ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "monoz"
5
+
6
+ begin
7
+ Monoz::Cli::Main.start(ARGV)
8
+ rescue Monoz::Errors::ConfigurationNotFound
9
+ puts "This is not a valid Monoz directory."
10
+ puts "Run monoz init to initialize Monoz in the current directory."
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "yaml"
5
+
6
+ module Monoz
7
+ module Cli
8
+ class Bundle < Thor
9
+ default_task :install
10
+
11
+ desc "install", "Install dependencies in all projects"
12
+ def install
13
+ Monoz.projects.order(:dependants).each do |project|
14
+ say "#{project.type} ", project.text_color, false
15
+ say "[#{project.name}] ", [:blue, :bold], false
16
+ say "bundle install", :green
17
+ project.run "bundle", "install"
18
+ end
19
+ end
20
+
21
+ desc "update", "Update dependencies in all projects"
22
+ def update
23
+ Monoz.projects.order(:dependants).each do |project|
24
+ say "#{project.type} ", project.text_color, false
25
+ say "[#{project.name}] ", [:blue, :bold], false
26
+ say "bundle update", :green
27
+ project.run "bundle", "update"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "yaml"
5
+
6
+ module Monoz
7
+ module Cli
8
+ class Inspect < Thor
9
+ default_task :all
10
+
11
+ desc "all", "Returns a list of all projects in this monozrepo"
12
+ def all
13
+ Monoz.projects.order(:name).to_table
14
+ end
15
+
16
+ desc "apps", "Returns a list of apps in this monozrepo"
17
+ def apps
18
+ Monoz.projects.order(:name).filter(:apps).to_table
19
+ end
20
+
21
+ desc "gems", "Returns a list of gems in this monozrepo"
22
+ def gems
23
+ Monoz.projects.filter(:gems).order(:name).to_table
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "yaml"
5
+ require "fileutils"
6
+
7
+ module Monoz
8
+ module Cli
9
+ class Main < Thor
10
+ desc "init [PATH]", "Initialize a monozrepo at the specified PATH"
11
+ def init(path = ".")
12
+ Monoz::Services::InitService.new(self).call(path)
13
+ end
14
+
15
+ desc "bundle", "Run bundle commands in all projects"
16
+ subcommand "bundle", Monoz::Cli::Bundle
17
+
18
+ desc "inspect", "Inspect this monozrepo"
19
+ subcommand "inspect", Monoz::Cli::Inspect
20
+
21
+ map "run" => "run_action"
22
+ desc "run [action]", "Run commands in all projects"
23
+ def run_action(keys = nil)
24
+ return help("run") if keys.nil? || keys == "help"
25
+
26
+ Monoz::Services::RunActionService.new(self).call(keys)
27
+ end
28
+
29
+ desc "version", "Get the current version of Monoz"
30
+ def version
31
+ say "Monoz version: #{Monoz::VERSION}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ require "yaml"
2
+ require "active_support/core_ext/module/delegation"
3
+
4
+ module Monoz
5
+ class Configuration
6
+ attr_reader :config_file_path
7
+ delegate_missing_to :@contents
8
+
9
+ def initialize(project_path)
10
+ @config_file_path = find_config_file(project_path)
11
+ raise Monoz::Errors::ConfigurationNotFound.new("Configuration not found at: #{project_path}") unless @config_file_path
12
+ @contents = load_config(@config_file_path) || {}
13
+ end
14
+
15
+ def contents
16
+ @contents ||= load_config(@config_file_path) if @config_file_path
17
+ end
18
+
19
+ def root_path
20
+ File.dirname @config_file_path
21
+ end
22
+
23
+ class << self
24
+ def default_config
25
+ { "folders" => ["apps", "gems"] }
26
+ end
27
+ end
28
+
29
+ private
30
+ def find_config_file(dir_path)
31
+ while dir_path != "/"
32
+ config_file_path = File.join(dir_path, "monoz.yml")
33
+ return config_file_path if File.exist?(config_file_path)
34
+
35
+ dir_path = File.dirname(dir_path)
36
+ end
37
+ end
38
+
39
+ def load_config(config_file_path)
40
+ YAML.load_file(config_file_path)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,123 @@
1
+ module Monoz
2
+ class Project
3
+ attr_reader :name, :root_path, :gemspec, :type, :dependencies, :gem_name, :dependants
4
+
5
+ def initialize(root_path)
6
+ @root_path = root_path
7
+
8
+ parse_project_files
9
+ parse_dependencies
10
+
11
+ setup_instance_variables
12
+ end
13
+
14
+ def is_gem?
15
+ !gemspec_path.nil?
16
+ end
17
+
18
+ def valid?
19
+ @root_path != nil && gemfile_path != nil
20
+ end
21
+
22
+ def run(*command)
23
+ FileUtils.chdir(root_path) do
24
+ system(*command)
25
+ end
26
+ end
27
+
28
+ def text_color
29
+ is_gem? ? :green : :blue
30
+ end
31
+
32
+ def test_frameworks
33
+ frameworks = []
34
+
35
+ rspec_files = Dir.glob(File.join(@root_path, "**/*_spec.rb"))
36
+ if !rspec_files.empty?
37
+ frameworks << "rspec"
38
+ end
39
+
40
+ minitest_files = Dir.glob(File.join(@root_path, "test/**/*_test.rb"))
41
+ if !minitest_files.empty?
42
+ frameworks << "minitest"
43
+ end
44
+
45
+ frameworks
46
+ end
47
+
48
+ private
49
+ def parse_project_files
50
+ @gemspec = parse_gemspec
51
+ end
52
+
53
+ def parse_dependencies
54
+ @dependencies = gem_dependencies
55
+ end
56
+
57
+ def parse_gemspec
58
+ return nil unless (gemspec_file = gemspec_path) && File.exist?(gemspec_file)
59
+
60
+ spec = Gem::Specification.load(gemspec_file)
61
+ {
62
+ name: spec.name,
63
+ version: spec.version.to_s
64
+ }
65
+ end
66
+
67
+ def setup_instance_variables
68
+ @name = File.basename(root_path)
69
+ @gem_name = @gemspec.dig(:name) if is_gem?
70
+ @type = is_gem? ? "gem" : "app"
71
+ @test_frameworks =
72
+ @dependants = []
73
+ end
74
+
75
+ def gemspec_path
76
+ Dir.glob(File.join(@root_path, "*.gemspec")).first
77
+ end
78
+
79
+ def gemfile_path
80
+ Dir.glob(File.join(@root_path, "Gemfile")).first
81
+ end
82
+
83
+ def gem_dependencies
84
+ dependencies = {}
85
+
86
+ # Check if a Gemfile exists
87
+ if (gemfile = gemfile_path) && File.exist?(gemfile)
88
+ gemfile_content = File.read(gemfile)
89
+
90
+ # Use regex to find all gem entries
91
+ gem_entries = gemfile_content.scan(/gem\s+['"]([^'"]+)['"](?:,\s*['"]([^'"]+)['"])?(?:,\s*['"]([^'"]+)['"])?/)
92
+
93
+ # Add each gem and its version to the hash
94
+ gem_entries.each do |gem_entry|
95
+ gem_name = gem_entry[0]
96
+ version_spec = gem_entry[1] || ""
97
+ dependencies[gem_name] = version_spec
98
+ end
99
+ end
100
+
101
+ # Check if a gemspec exists
102
+ if (gemspec_file = gemspec_path) && File.exist?(gemspec_file)
103
+ spec = Gem::Specification.load(gemspec_file)
104
+ spec.dependencies.each do |dep|
105
+ dependencies[dep.name] = dep.requirement.to_s
106
+ end
107
+ end
108
+
109
+ dependencies
110
+ end
111
+
112
+ public
113
+
114
+ # Update dependants of this project based on other projects in the collection
115
+ def update_dependants(collection)
116
+ collection.each do |project|
117
+ if project.dependencies.key?(name)
118
+ project.dependants << self
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,121 @@
1
+ require "pathname"
2
+ require "active_support/core_ext/module/delegation"
3
+ require "terminal-table"
4
+
5
+ module Monoz
6
+ class ProjectCollection
7
+ include Enumerable
8
+ delegate_missing_to :@items
9
+
10
+ def initialize(file_path)
11
+ @items = []
12
+ project_folders = Monoz.config.dig("folders") || ["apps", "gems"]
13
+
14
+ search_paths = project_folders.map { |folder| File.join(file_path, folder) }
15
+
16
+ search_paths.each do |search_path|
17
+ Dir.glob(File.join(search_path, "*/Gemfile")).each do |gemfile_path|
18
+ project_path = File.dirname(gemfile_path)
19
+ project = Project.new(project_path)
20
+ if project.valid?
21
+ @items << project
22
+ refresh_dependants(project)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def exist?(name)
29
+ !!find(name)
30
+ end
31
+
32
+ def find(name)
33
+ @items.select { |i| i.name == name }&.first
34
+ end
35
+
36
+ def all
37
+ @items
38
+ end
39
+
40
+ def order(key)
41
+ if key.to_sym == :name
42
+ @items = order_by_name
43
+ self
44
+ elsif key.to_sym == :dependants
45
+ @items = order_by_dependants
46
+ self
47
+ else
48
+ raise "Invalid order key: #{key}"
49
+ end
50
+ end
51
+
52
+ def filter(key)
53
+ if key.to_sym == :apps
54
+ @items = @items.select { |i| i.type == "app" }
55
+ self
56
+ elsif key.to_sym == :gems
57
+ @items = @items.select { |i| i.type == "gem" }
58
+ self
59
+ else
60
+ raise "Invalid filter key: #{key}"
61
+ end
62
+ end
63
+
64
+ def to_table
65
+ rows = []
66
+ @items.each do |project|
67
+ rows << [project.name, project.type, project.gem_name, project.test_frameworks.join(", "), project.dependants.join(", ")]
68
+ end
69
+ table = Terminal::Table.new(
70
+ headings: ["Project", "Type", "Gem Name", "Test Framework(s)", "Dependants"],
71
+ rows: rows,
72
+ style: { padding_left: 2, padding_right: 2, border_i: "o" },
73
+ )
74
+ puts table
75
+ end
76
+
77
+ private
78
+ def refresh_dependants(project)
79
+ @items.each do |other_project|
80
+ next if other_project == project
81
+ other_project.dependencies.each do |dep_name, _|
82
+ if dep_name == project.gem_name
83
+ project.dependants << other_project.name
84
+ break
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def order_by_name
91
+ @items.sort_by(&:name)
92
+ end
93
+
94
+ def order_by_dependants
95
+ sorted_items = []
96
+ items = @items.dup # make a copy of items to avoid modifying @items
97
+ dependants = Hash.new { |h, k| h[k] = [] }
98
+
99
+ # build a hash of dependants for each project
100
+ items.each do |project|
101
+ project.dependants.each do |dependant|
102
+ dependants[dependant] << project.name
103
+ end
104
+ end
105
+
106
+ # perform a topological sort
107
+ until items.empty?
108
+ no_dependencies = items.select { |project| dependants[project.name].empty? }
109
+ raise "Circular dependency detected" if no_dependencies.empty?
110
+ sorted_items.concat(no_dependencies)
111
+ no_dependencies.each do |project|
112
+ items.delete(project)
113
+ dependants.each { |_, v| v.delete(project.name) }
114
+ end
115
+ end
116
+
117
+ sorted_items
118
+ end
119
+
120
+ end
121
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "fileutils"
5
+
6
+ module Monoz
7
+ module Services
8
+ class BaseService
9
+ include Thor::Shell
10
+
11
+ def initialize(thor_instance)
12
+ @thor = thor_instance
13
+ end
14
+
15
+ class << self
16
+ def call(path)
17
+ new(path).call
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "fileutils"
5
+
6
+ module Monoz
7
+ module Services
8
+ class InitService < Monoz::Services::BaseService
9
+ def call(path)
10
+ config_file_path = File.join(File.expand_path(path), "monoz.yml")
11
+ project_dir = File.dirname(config_file_path)
12
+
13
+ if File.exist?(config_file_path)
14
+ @thor.say "Error: monoz.yaml already exists in #{config_file_path}", :red
15
+ return
16
+ end
17
+
18
+ FileUtils.mkdir_p(project_dir)
19
+ FileUtils.mkdir_p(File.join(project_dir, "apps"))
20
+ FileUtils.mkdir_p(File.join(project_dir, "gems"))
21
+ FileUtils.touch(File.join(project_dir, "apps/.keep"))
22
+ FileUtils.touch(File.join(project_dir, "gems/.keep"))
23
+ File.write(config_file_path, Monoz::Configuration.default_config.to_yaml)
24
+
25
+ FileUtils.chdir(project_dir) do
26
+ system "git", "init"
27
+ end
28
+
29
+ @thor.say "Successfully initialized Monoz in #{project_dir}", :green
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "fileutils"
5
+
6
+ module Monoz
7
+ module Services
8
+ class RunActionService < Monoz::Services::BaseService
9
+ def call(keys = nil)
10
+ action = Monoz.config.dig("actions", *keys)
11
+
12
+ if action.nil?
13
+ say "Invalid action: ", :red, false
14
+ say keys, [:red, :bold]
15
+ exit 0
16
+ end
17
+
18
+ pp action
19
+ # Monoz.projects.order(:dependants).each do |project|
20
+ # project.run "ls"
21
+ # end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Monoz
2
+ VERSION = "0.1.0"
3
+ end
data/lib/monoz.rb ADDED
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "monoz/version"
4
+
5
+ require "pathname"
6
+
7
+ module Monoz
8
+ module Errors
9
+ class ConfigurationNotFound < StandardError; end
10
+ end
11
+
12
+ module Cli
13
+ autoload "Bundle", "monoz/cli/bundle"
14
+ autoload "Inspect", "monoz/cli/inspect"
15
+ autoload "Main", "monoz/cli/main"
16
+ autoload "Run", "monoz/cli/run"
17
+ end
18
+
19
+ module Services
20
+ autoload "BaseService", "monoz/services/base_service"
21
+ autoload "InitService", "monoz/services/init_service"
22
+ autoload "RunActionService", "monoz/services/run_action_service"
23
+ end
24
+
25
+ autoload "Configuration", "monoz/configuration"
26
+ autoload "Project", "monoz/project"
27
+ autoload "ProjectCollection", "monoz/project_collection"
28
+
29
+ class << self
30
+ def config
31
+ @config ||= Monoz::Configuration.new(pwd)
32
+ end
33
+
34
+ def projects
35
+ Monoz::ProjectCollection.new(config.root_path)
36
+ end
37
+
38
+ def pwd
39
+ @pwd ||= Pathname.new(Dir.pwd)
40
+ end
41
+
42
+ def pwd=(value)
43
+ @pwd = value
44
+ end
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monoz
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kjellberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: terminal-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.1
55
+ description: Command line tool for managing ruby monorepos.
56
+ email:
57
+ - 2277443+kjellberg@users.noreply.github.com
58
+ executables:
59
+ - monoz
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - README.md
64
+ - bin/monoz
65
+ - lib/monoz.rb
66
+ - lib/monoz/cli/bundle.rb
67
+ - lib/monoz/cli/inspect.rb
68
+ - lib/monoz/cli/main.rb
69
+ - lib/monoz/configuration.rb
70
+ - lib/monoz/project.rb
71
+ - lib/monoz/project_collection.rb
72
+ - lib/monoz/services/base_service.rb
73
+ - lib/monoz/services/init_service.rb
74
+ - lib/monoz/services/run_action_service.rb
75
+ - lib/monoz/version.rb
76
+ homepage: https://github.com/kjellberg/monoz
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ homepage_uri: https://github.com/kjellberg/monoz
81
+ source_code_uri: https://github.com/kjellberg/monoz
82
+ changelog_uri: https://github.com/kjellberg/monoz
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 2.6.0
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.4.5
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Command line tool for managing ruby monorepos.
102
+ test_files: []