kettle-family 0.2.5 → 0.2.7
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +34 -1
- data/README.md +1 -1
- data/lib/kettle/family/command_runner.rb +27 -22
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +68 -4
- data.tar.gz.sig +0 -0
- metadata +9 -29
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3dfb2d02f39f7c5b6e0bbf8398440cdce3300490cab3db8aad780b0d4f2b2b23
|
|
4
|
+
data.tar.gz: 3354f6c528b63eaee745d87438fb41b21777d51d0accf30c1d270ac61189db96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2bf468aac31e154c72514ef15100393038244ea081cf4c3d2ee48369e140919edf273029cca70d500dd3f999e9d14cd7165f85560aa63662f1a3c1cacd9761fe
|
|
7
|
+
data.tar.gz: 613218aa97433a66acb95849775523a38749267e7fcbf493998def269ccaadabe15d59d9d91f76a08dd436dcf2f9f73540a40372cf38a062e12dbc9c69e28d20
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,35 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [0.2.7] - 2026-07-15
|
|
34
|
+
|
|
35
|
+
- TAG: [v0.2.7][0.2.7t]
|
|
36
|
+
- COVERAGE: 95.20% -- 2342/2460 lines in 23 files
|
|
37
|
+
- BRANCH COVERAGE: 76.92% -- 780/1014 branches in 23 files
|
|
38
|
+
- 29.29% documented
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Family release now waits for just-published prerequisite family gems to appear
|
|
43
|
+
in the gem server registry before continuing dependent lockfile normalization
|
|
44
|
+
and release steps.
|
|
45
|
+
|
|
46
|
+
## [0.2.6] - 2026-07-14
|
|
47
|
+
|
|
48
|
+
- TAG: [v0.2.6][0.2.6t]
|
|
49
|
+
- COVERAGE: 95.14% -- 2311/2429 lines in 23 files
|
|
50
|
+
- BRANCH COVERAGE: 76.75% -- 769/1002 branches in 23 files
|
|
51
|
+
- 29.54% documented
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- Command runner child processes now use a per-child unbundled environment
|
|
56
|
+
instead of mutating global `ENV`, avoiding threaded release crashes on
|
|
57
|
+
TruffleRuby.
|
|
58
|
+
- Family release commands no longer receive the implicit family local path
|
|
59
|
+
environment variable by default, preventing `kettle-release` from committing
|
|
60
|
+
sibling path sources into release lockfiles.
|
|
61
|
+
|
|
33
62
|
## [0.2.5] - 2026-07-11
|
|
34
63
|
|
|
35
64
|
- TAG: [v0.2.5][0.2.5t]
|
|
@@ -670,7 +699,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
670
699
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
671
700
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
672
701
|
|
|
673
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v0.2.
|
|
702
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v0.2.7...HEAD
|
|
703
|
+
[0.2.7]: https://github.com/kettle-dev/kettle-family/compare/v0.2.6...v0.2.7
|
|
704
|
+
[0.2.7t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.2.7
|
|
705
|
+
[0.2.6]: https://github.com/kettle-dev/kettle-family/compare/v0.2.5...v0.2.6
|
|
706
|
+
[0.2.6t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.2.6
|
|
674
707
|
[0.2.5]: https://github.com/kettle-dev/kettle-family/compare/v0.2.4...v0.2.5
|
|
675
708
|
[0.2.5t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.2.5
|
|
676
709
|
[0.2.4]: https://github.com/kettle-dev/kettle-family/compare/v0.2.3...v0.2.4
|
data/README.md
CHANGED
|
@@ -603,7 +603,7 @@ Thanks for RTFM. ☺️
|
|
|
603
603
|
[📌gitmoji]: https://gitmoji.dev
|
|
604
604
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
605
605
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
606
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.
|
|
606
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.460-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
607
607
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
608
608
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
609
609
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -114,15 +114,14 @@ module Kettle
|
|
|
114
114
|
def call(member:, phase:, command:, env: {}, interactive: false)
|
|
115
115
|
argv = command_argv(member: member, command: command, env: env)
|
|
116
116
|
process_env = process_env(member: member, env: env)
|
|
117
|
+
spawn_options = process_options
|
|
117
118
|
return skipped_result(member: member, phase: phase, argv: argv) unless execute
|
|
118
119
|
|
|
119
120
|
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
120
|
-
stdout, stderr, status =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Open3.capture3(process_env, *argv, chdir: member.root)
|
|
125
|
-
end
|
|
121
|
+
stdout, stderr, status = if interactive
|
|
122
|
+
run_interactive(env: process_env, argv: argv, chdir: member.root, member_name: member.name, process_options: spawn_options)
|
|
123
|
+
else
|
|
124
|
+
Open3.capture3(process_env, *argv, chdir: member.root, **spawn_options)
|
|
126
125
|
end
|
|
127
126
|
stdout = normalize_output(stdout)
|
|
128
127
|
stderr = normalize_output(stderr)
|
|
@@ -146,16 +145,16 @@ module Kettle
|
|
|
146
145
|
|
|
147
146
|
attr_reader :execute, :accept, :gem_signing_password, :otp_coordinator
|
|
148
147
|
|
|
149
|
-
def run_interactive(env:, argv:, chdir:, member_name:)
|
|
150
|
-
return run_interactive_pty(env: env, argv: argv, chdir: chdir, member_name: member_name) if pty_available?
|
|
148
|
+
def run_interactive(env:, argv:, chdir:, member_name:, process_options:)
|
|
149
|
+
return run_interactive_pty(env: env, argv: argv, chdir: chdir, member_name: member_name, process_options: process_options) if pty_available?
|
|
151
150
|
|
|
152
|
-
run_interactive_open3(env: env, argv: argv, chdir: chdir, member_name: member_name)
|
|
151
|
+
run_interactive_open3(env: env, argv: argv, chdir: chdir, member_name: member_name, process_options: process_options)
|
|
153
152
|
end
|
|
154
153
|
|
|
155
|
-
def run_interactive_pty(env:, argv:, chdir:, member_name:)
|
|
154
|
+
def run_interactive_pty(env:, argv:, chdir:, member_name:, process_options:)
|
|
156
155
|
stdout = +""
|
|
157
156
|
status = nil
|
|
158
|
-
PTY.spawn(env, *argv, chdir: chdir) do |output, input, pid|
|
|
157
|
+
PTY.spawn(env, *argv, chdir: chdir, **process_options) do |output, input, pid|
|
|
159
158
|
begin
|
|
160
159
|
loop do
|
|
161
160
|
readers = [output]
|
|
@@ -180,11 +179,11 @@ module Kettle
|
|
|
180
179
|
[stdout, "", status]
|
|
181
180
|
end
|
|
182
181
|
|
|
183
|
-
def run_interactive_open3(env:, argv:, chdir:, member_name:)
|
|
182
|
+
def run_interactive_open3(env:, argv:, chdir:, member_name:, process_options:)
|
|
184
183
|
captured_stdout = +""
|
|
185
184
|
captured_stderr = +""
|
|
186
185
|
status = nil
|
|
187
|
-
Open3.popen3(env, *argv, chdir: chdir) do |input, output, error, wait_thread|
|
|
186
|
+
Open3.popen3(env, *argv, chdir: chdir, **process_options) do |input, output, error, wait_thread|
|
|
188
187
|
readers = [output, error]
|
|
189
188
|
readers << $stdin if $stdin.tty? && !otp_coordinator
|
|
190
189
|
until readers.empty?
|
|
@@ -280,14 +279,6 @@ module Kettle
|
|
|
280
279
|
output.to_s.encode("UTF-8", invalid: :replace, undef: :replace, replace: "")
|
|
281
280
|
end
|
|
282
281
|
|
|
283
|
-
def with_unbundled_environment
|
|
284
|
-
if defined?(Bundler)
|
|
285
|
-
Bundler.with_unbundled_env { yield }
|
|
286
|
-
else
|
|
287
|
-
yield
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
282
|
def command_argv(member:, command:, env: {})
|
|
292
283
|
argv = normalize_command(command)
|
|
293
284
|
return argv unless mise_configured?(member)
|
|
@@ -304,7 +295,21 @@ module Kettle
|
|
|
304
295
|
end
|
|
305
296
|
|
|
306
297
|
def process_env(member:, env:)
|
|
307
|
-
|
|
298
|
+
base_env = unbundled_process_env
|
|
299
|
+
return base_env.merge(env) unless mise_configured?(member)
|
|
300
|
+
|
|
301
|
+
base_env
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def unbundled_process_env
|
|
305
|
+
return Bundler.original_env if defined?(Bundler) && Bundler.respond_to?(:original_env)
|
|
306
|
+
return Bundler.unbundled_env if defined?(Bundler) && Bundler.respond_to?(:unbundled_env)
|
|
307
|
+
|
|
308
|
+
{}
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def process_options
|
|
312
|
+
return {unsetenv_others: true} if defined?(Bundler)
|
|
308
313
|
|
|
309
314
|
{}
|
|
310
315
|
end
|
|
@@ -43,6 +43,8 @@ module Kettle
|
|
|
43
43
|
"BUNDLE_SILENCE_ROOT_WARNING" => "true",
|
|
44
44
|
"BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES" => "true"
|
|
45
45
|
}.freeze
|
|
46
|
+
REGISTRY_WAIT_ATTEMPTS = 15
|
|
47
|
+
REGISTRY_WAIT_INTERVAL_SECONDS = 15
|
|
46
48
|
|
|
47
49
|
def initialize(command:, config:, members:, execute: false, accept: true, commit: true, allow_dirty: false, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, local_ci: false, continue_ci_failures: false, skip_bundle_audit: false, auto_dependency_floors: nil, gha_sha_pins_upgrade: "patch", gha_sha_pins_check: false, env_overrides: {}, debug: false, gem_signing_password: nil, jobs: nil, progress_io: nil, bup_args: [], bex_args: [], start_member: nil, start_branch: nil)
|
|
48
50
|
@command = command
|
|
@@ -497,15 +499,33 @@ module Kettle
|
|
|
497
499
|
|
|
498
500
|
require_relative "dependency_floor"
|
|
499
501
|
|
|
502
|
+
affected_dependent_members = dependent_members_depending_on(released_members: released_members, dependent_members: dependent_members)
|
|
500
503
|
floor_results = DependencyFloor.new(
|
|
501
504
|
released_members: released_members,
|
|
502
505
|
dependent_members: dependent_members,
|
|
503
506
|
mode: execute ? :execute : :dry_run
|
|
504
507
|
).results
|
|
505
508
|
memo.concat(floor_results)
|
|
506
|
-
return
|
|
509
|
+
return if floor_results.any? && !floor_results.all?(&:ok?)
|
|
507
510
|
|
|
508
|
-
commit_dependency_floor_changes(dependent_members: floor_results.map(&:member_name), runner: runner, memo: memo) if execute && commit
|
|
511
|
+
commit_dependency_floor_changes(dependent_members: floor_results.map(&:member_name), runner: runner, memo: memo) if floor_results.any? && execute && commit
|
|
512
|
+
return if memo.any? && !memo.last.ok?
|
|
513
|
+
|
|
514
|
+
append_registry_wait_result(released_members: released_members, dependent_members: affected_dependent_members, memo: memo)
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def dependent_members_depending_on(released_members:, dependent_members:)
|
|
518
|
+
released_names = released_members.map(&:name)
|
|
519
|
+
dependent_members.select do |member|
|
|
520
|
+
Array(member.dependencies).map(&:to_s).any? { |dependency| released_names.include?(dependency) }
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
def append_registry_wait_result(released_members:, dependent_members:, memo:)
|
|
525
|
+
return unless execute && publish
|
|
526
|
+
return if dependent_members.empty?
|
|
527
|
+
|
|
528
|
+
memo << wait_for_registry_result(released_members: released_members, dependent_members: dependent_members)
|
|
509
529
|
end
|
|
510
530
|
|
|
511
531
|
def commit_dependency_floor_changes(dependent_members:, runner:, memo:)
|
|
@@ -635,7 +655,7 @@ module Kettle
|
|
|
635
655
|
end
|
|
636
656
|
|
|
637
657
|
def base_release_env
|
|
638
|
-
env = config.
|
|
658
|
+
env = config.release_env
|
|
639
659
|
env["KETTLE_FAMILY_CONFIG"] = config.path if config.path
|
|
640
660
|
env.merge!(TEMPLATE_QUIET_ENV) unless debug
|
|
641
661
|
env["K_RELEASE_CI_CONTINUE"] = "true" if continue_ci_failures
|
|
@@ -664,6 +684,50 @@ module Kettle
|
|
|
664
684
|
raise Error, "could not parse published versions for #{gem_name}: #{error.message}"
|
|
665
685
|
end
|
|
666
686
|
|
|
687
|
+
def wait_for_registry_result(released_members:, dependent_members:)
|
|
688
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
689
|
+
missing = []
|
|
690
|
+
attempts = 0
|
|
691
|
+
REGISTRY_WAIT_ATTEMPTS.times do |index|
|
|
692
|
+
attempts = index + 1
|
|
693
|
+
missing = released_members.reject { |member| released_version?(member.name, member.version) }
|
|
694
|
+
break if missing.empty?
|
|
695
|
+
|
|
696
|
+
sleep(REGISTRY_WAIT_INTERVAL_SECONDS) if attempts < REGISTRY_WAIT_ATTEMPTS
|
|
697
|
+
end
|
|
698
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
699
|
+
dependency_names = dependent_members.map(&:name).join(", ")
|
|
700
|
+
if missing.empty?
|
|
701
|
+
CommandResult.new(
|
|
702
|
+
member_name: dependency_names,
|
|
703
|
+
phase: "release_wait_for_registry",
|
|
704
|
+
command: ["internal", "wait-for-registry", released_members.map { |member| "#{member.name}-#{member.version}" }.join(",")],
|
|
705
|
+
workdir: config.root,
|
|
706
|
+
status: 0,
|
|
707
|
+
success: true,
|
|
708
|
+
stdout: "registry contains #{released_members.map { |member| "#{member.name} #{member.version}" }.join(", ")} after #{attempts} check(s)",
|
|
709
|
+
stderr: "",
|
|
710
|
+
elapsed_seconds: elapsed,
|
|
711
|
+
skipped: false,
|
|
712
|
+
reason: "dependent members: #{dependency_names}"
|
|
713
|
+
)
|
|
714
|
+
else
|
|
715
|
+
CommandResult.new(
|
|
716
|
+
member_name: dependency_names,
|
|
717
|
+
phase: "release_wait_for_registry",
|
|
718
|
+
command: ["internal", "wait-for-registry", released_members.map { |member| "#{member.name}-#{member.version}" }.join(",")],
|
|
719
|
+
workdir: config.root,
|
|
720
|
+
status: 1,
|
|
721
|
+
success: false,
|
|
722
|
+
stdout: "timed out waiting for #{missing.map { |member| "#{member.name} #{member.version}" }.join(", ")} after #{attempts} check(s)",
|
|
723
|
+
stderr: "",
|
|
724
|
+
elapsed_seconds: elapsed,
|
|
725
|
+
skipped: false,
|
|
726
|
+
reason: "dependent members: #{dependency_names}"
|
|
727
|
+
)
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
|
|
667
731
|
def already_released_result(member)
|
|
668
732
|
tag = release_tag_name(member.version)
|
|
669
733
|
current_release_head = released_version_current_head?(member, tag)
|
|
@@ -917,7 +981,7 @@ module Kettle
|
|
|
917
981
|
end
|
|
918
982
|
|
|
919
983
|
def release_allowed_local_path_roots
|
|
920
|
-
config.
|
|
984
|
+
config.release_env.merge(env_overrides).filter_map do |key, value|
|
|
921
985
|
next unless key.end_with?("_LOCAL", "_DEV")
|
|
922
986
|
next if value.to_s.empty? || value.to_s.casecmp("false").zero?
|
|
923
987
|
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kettle-family
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -99,26 +99,6 @@ dependencies:
|
|
|
99
99
|
- - "~>"
|
|
100
100
|
- !ruby/object:Gem::Version
|
|
101
101
|
version: '0.2'
|
|
102
|
-
- !ruby/object:Gem::Dependency
|
|
103
|
-
name: version_gem
|
|
104
|
-
requirement: !ruby/object:Gem::Requirement
|
|
105
|
-
requirements:
|
|
106
|
-
- - "~>"
|
|
107
|
-
- !ruby/object:Gem::Version
|
|
108
|
-
version: '1.1'
|
|
109
|
-
- - ">="
|
|
110
|
-
- !ruby/object:Gem::Version
|
|
111
|
-
version: 1.1.13
|
|
112
|
-
type: :runtime
|
|
113
|
-
prerelease: false
|
|
114
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
115
|
-
requirements:
|
|
116
|
-
- - "~>"
|
|
117
|
-
- !ruby/object:Gem::Version
|
|
118
|
-
version: '1.1'
|
|
119
|
-
- - ">="
|
|
120
|
-
- !ruby/object:Gem::Version
|
|
121
|
-
version: 1.1.13
|
|
122
102
|
- !ruby/object:Gem::Dependency
|
|
123
103
|
name: bundler-audit
|
|
124
104
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -216,7 +196,7 @@ dependencies:
|
|
|
216
196
|
version: '3.1'
|
|
217
197
|
- - ">="
|
|
218
198
|
- !ruby/object:Gem::Version
|
|
219
|
-
version: 3.1.
|
|
199
|
+
version: 3.1.9
|
|
220
200
|
type: :development
|
|
221
201
|
prerelease: false
|
|
222
202
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -226,7 +206,7 @@ dependencies:
|
|
|
226
206
|
version: '3.1'
|
|
227
207
|
- - ">="
|
|
228
208
|
- !ruby/object:Gem::Version
|
|
229
|
-
version: 3.1.
|
|
209
|
+
version: 3.1.9
|
|
230
210
|
- !ruby/object:Gem::Dependency
|
|
231
211
|
name: ruby-progressbar
|
|
232
212
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -250,7 +230,7 @@ dependencies:
|
|
|
250
230
|
version: '1.0'
|
|
251
231
|
- - ">="
|
|
252
232
|
- !ruby/object:Gem::Version
|
|
253
|
-
version: 1.0.
|
|
233
|
+
version: 1.0.6
|
|
254
234
|
type: :development
|
|
255
235
|
prerelease: false
|
|
256
236
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -260,7 +240,7 @@ dependencies:
|
|
|
260
240
|
version: '1.0'
|
|
261
241
|
- - ">="
|
|
262
242
|
- !ruby/object:Gem::Version
|
|
263
|
-
version: 1.0.
|
|
243
|
+
version: 1.0.6
|
|
264
244
|
- !ruby/object:Gem::Dependency
|
|
265
245
|
name: gitmoji-regex
|
|
266
246
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -341,10 +321,10 @@ licenses:
|
|
|
341
321
|
- AGPL-3.0-only
|
|
342
322
|
metadata:
|
|
343
323
|
homepage_uri: https://kettle-family.galtzo.com
|
|
344
|
-
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v0.2.
|
|
345
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.2.
|
|
324
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v0.2.7
|
|
325
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.2.7/CHANGELOG.md
|
|
346
326
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
|
|
347
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-family/0.2.
|
|
327
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/0.2.7
|
|
348
328
|
funding_uri: https://github.com/sponsors/pboling
|
|
349
329
|
wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
|
|
350
330
|
news_uri: https://www.railsbling.com/tags/kettle-family
|
|
@@ -374,7 +354,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
374
354
|
- !ruby/object:Gem::Version
|
|
375
355
|
version: '0'
|
|
376
356
|
requirements: []
|
|
377
|
-
rubygems_version: 4.0.
|
|
357
|
+
rubygems_version: 4.0.16
|
|
378
358
|
specification_version: 4
|
|
379
359
|
summary: "\U0001F469\U0001F469\U0001F467\U0001F467 CLI helpers for managing related
|
|
380
360
|
Ruby gems as a family."
|
metadata.gz.sig
CHANGED
|
Binary file
|