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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2bc8f54983986ae1576f3f766324a35d5bc87b2776086a07eb3258d43bba386d
4
- data.tar.gz: edc612181ca90bd0ebdfdc196d88810d82c99cdb54b752c66b3173d85fa896a2
3
+ metadata.gz: 3dfb2d02f39f7c5b6e0bbf8398440cdce3300490cab3db8aad780b0d4f2b2b23
4
+ data.tar.gz: 3354f6c528b63eaee745d87438fb41b21777d51d0accf30c1d270ac61189db96
5
5
  SHA512:
6
- metadata.gz: b3f19512bb9a8b3e66ba7037a44877afc0d76eebccd50b2ea9ff233ed2a3eb74e4e0e4cc14cb2125d059c66b59ecf9f9a238b60e77cc85a81a6ab6c5dfd13749
7
- data.tar.gz: 2abdfc91601545b26d5a3a392f833cb439901a4b2d33b64a1574cbb6a57ae8f71bbbfbd8610d18af80a919bdea1e776063319e6b8670cd3b678950cd4e3964d7
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.5...HEAD
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.425-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
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 = with_unbundled_environment do
121
- if interactive
122
- run_interactive(env: process_env, argv: argv, chdir: member.root, member_name: member.name)
123
- else
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
- return env unless mise_configured?(member)
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
@@ -3,7 +3,7 @@
3
3
  module Kettle
4
4
  module Family
5
5
  module Version
6
- VERSION = "0.2.5"
6
+ VERSION = "0.2.7"
7
7
  end
8
8
  VERSION = Version::VERSION # Traditional Constant Location
9
9
  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 unless floor_results.any? && floor_results.all?(&:ok?)
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.family_local_path_env.merge(config.release_env)
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.family_local_path_env.merge(env_overrides).filter_map do |key, value|
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.5
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.6
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.6
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.5
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.5
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.5
345
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.2.5/CHANGELOG.md
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.5
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.10
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