kettle-family 1.2.98 → 1.2.100

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: a170049375aa0330277e878596e92a61b58655a29ca9aeba1e9ab0006fa46c04
4
- data.tar.gz: 5bfcd0c99c774fad470b8510188e6eaad700c2ca3157f64eb1a9b52ac320d6c6
3
+ metadata.gz: 8b8d7a476e1af0096eca0bc0870cd9a8623b2ad2ccdcfa5080ec184b5b561096
4
+ data.tar.gz: df5707cdac3c7838df5cbbc83731ffb7741dfe818e440a83d917792a1f4290b3
5
5
  SHA512:
6
- metadata.gz: c527202894d4bca3ada36684b9a7f3700052ebd53967ebb7ea7a65cc0db5cc20a5bf04eec6730f380a444fd69ed6828428fc29e16a1718371eab48eea0647601
7
- data.tar.gz: b147945409bca869bf23ea6ab8773b7fc8fc6c7677ef877367d2970b3c73edb1efe628606c267de48f58af8cb4db761d527ce8df457b1fe6d4f729ae8c662229
6
+ metadata.gz: 1cc3290cedbd5e35c1c77f4810b6fba3e562297e66a8f1c0d5a0f575110c231771cb460ae67617d289dcf845cde1f0ef4ed09aa996ca5774631fbe1f0c8a4556
7
+ data.tar.gz: f6e8a65bfd5e7dd3c544a6b47443383a7a9d4cfc7597a52e509bbc9cf765cf1835624f8b4022db1b80b45b2cb0e40c3a5de77acb08f0cfd953c44913d6611454
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,34 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.2.100] - 2026-09-09
34
+
35
+ - TAG: [v1.2.100][1.2.100t]
36
+ - COVERAGE: 93.51% -- 6283/6719 lines in 35 files
37
+ - BRANCH COVERAGE: 78.15% -- 2553/3267 branches in 35 files
38
+ - 32.84% documented
39
+
40
+ ### Fixed
41
+
42
+ - Run each family member through its own mise runtime instead of inheriting the orchestrator runtime.
43
+
44
+ - Refresh dependent release lockfiles after a runtime sibling publishes without waiting for later Gemfile-only tooling dependencies.
45
+
46
+ - Preserve the Mise launcher path when running commands in member worktrees from a Bundler environment.
47
+
48
+ ## [1.2.99] - 2026-09-09
49
+
50
+ - TAG: [v1.2.99][1.2.99t]
51
+ - COVERAGE: 93.52% -- 6261/6695 lines in 35 files
52
+ - BRANCH COVERAGE: 78.19% -- 2542/3251 branches in 35 files
53
+ - 32.75% documented
54
+
55
+ ### Fixed
56
+
57
+ - Keep resumed family releases on the prepared CI commit by skipping dependency-floor reconciliation and disabling sibling paths for child release validation.
58
+
59
+ - Scope branch-stack release resumes to the prepared branch so later targets run their complete release lifecycle before CI monitoring.
60
+
33
61
  ## [1.2.98] - 2026-09-09
34
62
 
35
63
  - TAG: [v1.2.98][1.2.98t]
@@ -2692,7 +2720,11 @@ Please file a bug if you notice a violation of semantic versioning.
2692
2720
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
2693
2721
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
2694
2722
 
2695
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.98...HEAD
2723
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.100...HEAD
2724
+ [1.2.100]: https://github.com/kettle-dev/kettle-family/compare/v1.2.99...v1.2.100
2725
+ [1.2.100t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.100
2726
+ [1.2.99]: https://github.com/kettle-dev/kettle-family/compare/v1.2.98...v1.2.99
2727
+ [1.2.99t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.99
2696
2728
  [1.2.98]: https://github.com/kettle-dev/kettle-family/compare/v1.2.97...v1.2.98
2697
2729
  [1.2.98t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.98
2698
2730
  [1.2.97]: https://github.com/kettle-dev/kettle-family/compare/v1.2.96...v1.2.97
data/README.md CHANGED
@@ -435,6 +435,12 @@ metadata fix requires a new full release run from step 0:
435
435
  kettle-family release --publish --start-step 10 --local-ci
436
436
  ```
437
437
 
438
+ For `release.target_branches`, a resume position applies only to the first
439
+ selected branch, which is the one whose already-pushed release commit is being
440
+ resumed. Each later branch starts from step 0 so it creates, commits, and pushes
441
+ its own release candidate before `kettle-release` begins remote CI monitoring.
442
+ Use `--start-at member@branch` when the resumed branch is not the first target.
443
+
438
444
  `--skip-changelog` skips only `kettle-changelog`; `--skip-appraisals` skips
439
445
  only the Appraisal generation portion of release step 5 and still runs
440
446
  `bin/rake yard`; and `--skip-ci` skips only remote CI monitoring. The generic
@@ -1180,7 +1186,7 @@ Thanks for RTFM. ☺️
1180
1186
  [📌gitmoji]: https://gitmoji.dev
1181
1187
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1182
1188
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1183
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.679-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1189
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.719-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1184
1190
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
1185
1191
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1186
1192
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -10,6 +10,7 @@ module Kettle
10
10
  SENSITIVE_ENV_KEYS = [
11
11
  "KETTLE_RELEASE_GEM_SIGNING_PASSPHRASE"
12
12
  ].freeze
13
+ MISE_SESSION_ENV_PREFIX = "__MISE_"
13
14
 
14
15
  class OtpCoordinator
15
16
  def initialize(input: $stdin, output: $stdout, queue_total: nil, secrets_provider: nil, event_handler: nil)
@@ -616,11 +617,14 @@ module Kettle
616
617
 
617
618
  def process_env(member:, env:)
618
619
  base_env = unbundled_process_env
620
+ clear_outer_mise_session!(base_env) if mise_configured?(member)
619
621
  # Bundler's unbundled environment intentionally omits activation
620
622
  # variables, but it may also omit PATH. When `unsetenv_others` is used
621
623
  # for mise-managed members, dropping PATH makes nested commands such
622
624
  # as `bundle` unresolvable inside kettle-jem.
623
- base_env["PATH"] = if base_env.key?("PATH")
625
+ base_env["PATH"] = if mise_configured?(member)
626
+ mise_member_path(base_env["PATH"], ENV["PATH"])
627
+ elsif base_env.key?("PATH")
624
628
  executable_path(base_env["PATH"])
625
629
  else
626
630
  ENV["PATH"]
@@ -647,6 +651,33 @@ module Kettle
647
651
  paths.compact.map(&:to_s).flat_map { |value| value.split(File::PATH_SEPARATOR) }.reject(&:empty?).uniq.join(File::PATH_SEPARATOR)
648
652
  end
649
653
 
654
+ # `mise exec` selects a member's declared runtime. Do not let the
655
+ # orchestrator's Ruby or gem bin directory precede it on PATH: Bundler
656
+ # otherwise executes the outer runtime after `mise` has entered the
657
+ # member directory, and can rewrite that member's lockfile with the
658
+ # wrong Bundler version.
659
+ def mise_member_path(*paths)
660
+ outer_runtime_paths = [Gem.bindir, File.dirname(RbConfig.ruby)].filter_map do |entry|
661
+ canonical_path(entry) unless entry.to_s.empty?
662
+ end
663
+ paths.flat_map { |path| path.to_s.split(File::PATH_SEPARATOR) }.reject(&:empty?).reject do |entry|
664
+ outer_runtime_paths.include?(canonical_path(entry))
665
+ end.uniq.join(File::PATH_SEPARATOR)
666
+ end
667
+
668
+ # Mise records its active configuration in private `__MISE_*` variables.
669
+ # Retaining them across a nested `mise exec -C member` makes Mise reuse
670
+ # the orchestrator's tool set instead of loading the member's mise.toml.
671
+ def clear_outer_mise_session!(env)
672
+ env.delete_if { |key, _value| key.start_with?(MISE_SESSION_ENV_PREFIX) }
673
+ end
674
+
675
+ def canonical_path(path)
676
+ File.realpath(path)
677
+ rescue Errno::ENOENT, Errno::ENOTDIR
678
+ File.expand_path(path)
679
+ end
680
+
650
681
  def unbundled_process_env
651
682
  return Bundler.unbundled_env if defined?(Bundler) && Bundler.respond_to?(:unbundled_env)
652
683
  return Bundler.original_env if defined?(Bundler) && Bundler.respond_to?(:original_env)
@@ -7,6 +7,24 @@ module Kettle
7
7
  class DependencyFloor
8
8
  DEPENDENCY_METHODS = %i[add_dependency add_runtime_dependency add_development_dependency].freeze
9
9
 
10
+ # Return only dependencies declared by the gemspec itself. Release
11
+ # ordering also observes Gemfile-only tooling dependencies, but those
12
+ # cannot have their published floor rewritten by this class.
13
+ def self.gemspec_dependency_names(member)
14
+ return [] unless member.gemspec_path && File.file?(member.gemspec_path)
15
+
16
+ source = File.read(member.gemspec_path)
17
+ parse_result = Kettle::Dev::VersionBump.parse_source(source, member.gemspec_path)
18
+ Kettle::Dev::VersionBump.each_node(parse_result.value).filter_map do |node|
19
+ next unless node.is_a?(Prism::CallNode) && DEPENDENCY_METHODS.include?(node.name)
20
+
21
+ name_node = node.arguments&.arguments&.first
22
+ name_node.unescaped if name_node.is_a?(Prism::StringNode)
23
+ end.uniq
24
+ rescue Kettle::Dev::Error => error
25
+ raise Error, error.message
26
+ end
27
+
10
28
  def initialize(released_members:, dependent_members:, mode: :dry_run)
11
29
  @released_members = released_members
12
30
  @dependent_members = dependent_members
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "1.2.98"
8
+ VERSION = "1.2.100"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -1492,7 +1492,7 @@ module Kettle
1492
1492
  memo.concat(template_branch_sync_results(branch_members, runner: runner))
1493
1493
  break memo unless memo.last&.ok?
1494
1494
  end
1495
- branch_results = current_branch_results(branch_members)
1495
+ branch_results = with_release_branch_resume_scope { current_branch_results(branch_members) }
1496
1496
  tag_branch_results(branch_results, branch)
1497
1497
  memo.concat(branch_results)
1498
1498
 
@@ -2604,6 +2604,10 @@ module Kettle
2604
2604
  end
2605
2605
 
2606
2606
  def release_dependency_floor_reconciliation_results(release_members)
2607
+ # A step resume must continue the already-prepared release commit.
2608
+ # Reconciliation can rewrite lockfiles and create a new commit, which
2609
+ # invalidates the CI SHA selected by the caller's --start-step.
2610
+ return [] if release_step_resume?
2607
2611
  return [] unless execute && auto_dependency_floors
2608
2612
  # Monorepo members intentionally keep their family dependencies as
2609
2613
  # paths in the canonical development lockfile. Registry reconciliation
@@ -2671,6 +2675,7 @@ module Kettle
2671
2675
  end
2672
2676
 
2673
2677
  def append_dependency_floor_results(released_members:, dependent_members:, runner:, memo:)
2678
+ return if release_step_resume?
2674
2679
  return unless auto_dependency_floors
2675
2680
  return if dependent_members.empty?
2676
2681
 
@@ -2718,13 +2723,16 @@ module Kettle
2718
2723
  end
2719
2724
  end
2720
2725
 
2721
- # A release-valid lockfile must resolve every selected sibling dependency
2722
- # from the registry. Defer refreshes for dependents that still require a
2723
- # selected sibling from a later release wave; their gemspec floors can be
2724
- # updated now, but Bundler cannot resolve the future sibling remotely yet.
2726
+ # A release-valid lockfile must resolve every selected sibling whose
2727
+ # gemspec floor can be rewritten by DependencyFloor. Release ordering
2728
+ # deliberately also includes Gemfile-only tooling dependencies, but
2729
+ # those do not have a floor to reconcile and must not delay a runtime
2730
+ # dependency's lockfile refresh until a later wave.
2725
2731
  def dependency_floor_refresh_ready?(member, released_members:)
2726
2732
  selected_names = members.map(&:name)
2727
- selected_dependencies = release_dependency_names(member).select { |dependency| selected_names.include?(dependency) }
2733
+ selected_dependencies = DependencyFloor.gemspec_dependency_names(member).select do |dependency|
2734
+ selected_names.include?(dependency)
2735
+ end
2728
2736
  completed_names = Array(@release_completed_member_names) + released_members.map(&:name)
2729
2737
  selected_dependencies.all? { |dependency| completed_names.include?(dependency) }
2730
2738
  end
@@ -3486,11 +3494,33 @@ module Kettle
3486
3494
  end
3487
3495
 
3488
3496
  def release_start_step_for(member)
3497
+ return nil if release_branch_resume_scoped? && !@release_branch_resume_active
3489
3498
  return start_step unless fast_recovery_for?(member)
3490
3499
 
3491
3500
  (fast_recovery == "retry-ci") ? 10 : 11
3492
3501
  end
3493
3502
 
3503
+ # A release resume identifies one already-prepared branch commit. Later
3504
+ # branches in the same stack still need their complete lifecycle: build,
3505
+ # commit, push, and only then CI monitoring. Do not carry start_step (or
3506
+ # named CI recovery) beyond that first branch.
3507
+ def with_release_branch_resume_scope
3508
+ return yield unless release_branch_resume_scoped?
3509
+
3510
+ previous = @release_branch_resume_active
3511
+ @release_branch_resume_active = !@release_branch_resume_consumed
3512
+ yield
3513
+ ensure
3514
+ @release_branch_resume_consumed = true if @release_branch_resume_active
3515
+ @release_branch_resume_active = previous
3516
+ end
3517
+
3518
+ def release_branch_resume_scoped?
3519
+ command == "release" &&
3520
+ !config.release_target_branches.empty? &&
3521
+ (!start_step.nil? || !fast_recovery.nil?)
3522
+ end
3523
+
3494
3524
  def release_skip_steps_for(member)
3495
3525
  steps = Array(skip_steps).flat_map { |entry| entry.to_s.split(",") }.map(&:strip).reject(&:empty?)
3496
3526
  steps << "10" if skip_ci
@@ -3641,6 +3671,13 @@ module Kettle
3641
3671
  env = execution_profile(release_bootstrap_execution_profile, release_env)
3642
3672
  env.merge!(release_wave_local_path_env_for(member, family_root: family_root))
3643
3673
  env.merge!(release_local_path_policy_env(family_root: family_root))
3674
+ # A resumed child kettle-release starts in the member's mise
3675
+ # environment after the release candidate was already prepared.
3676
+ # Disable sibling paths so mise cannot reactivate them before the
3677
+ # resumed release validates that immutable lockfile. A new release
3678
+ # retains the staged local-graph policy until kettle-release reaches
3679
+ # its own release-lockfile boundary.
3680
+ env.merge!(release_lockfile_local_path_env_overrides(member)) if release_step_resume?
3644
3681
  # Family release performs one live pin review before member releases.
3645
3682
  # Child kettle-release must consume that reviewed cache instead of
3646
3683
  # repeating the GitHub API lookup for every member.
@@ -5390,6 +5427,10 @@ module Kettle
5390
5427
  !start_step.nil? || !skip_steps.to_s.empty? || fast_recovery || skip_ci
5391
5428
  end
5392
5429
 
5430
+ def release_step_resume?
5431
+ !start_step.nil? || !skip_steps.to_s.empty?
5432
+ end
5433
+
5393
5434
  def execution_profile(profile, env)
5394
5435
  profile = ExecutionProfile.fetch(profile) unless profile.is_a?(ExecutionProfile::Definition)
5395
5436
  env = env.dup
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: 1.2.98
4
+ version: 1.2.100
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -94,7 +94,7 @@ dependencies:
94
94
  version: '3.0'
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
- version: 3.0.33
97
+ version: 3.0.34
98
98
  type: :runtime
99
99
  prerelease: false
100
100
  version_requirements: !ruby/object:Gem::Requirement
@@ -104,7 +104,7 @@ dependencies:
104
104
  version: '3.0'
105
105
  - - ">="
106
106
  - !ruby/object:Gem::Version
107
- version: 3.0.33
107
+ version: 3.0.34
108
108
  - !ruby/object:Gem::Dependency
109
109
  name: kettle-gha-pins
110
110
  requirement: !ruby/object:Gem::Requirement
@@ -443,10 +443,10 @@ licenses:
443
443
  - AGPL-3.0-only
444
444
  metadata:
445
445
  homepage_uri: https://kettle-family.galtzo.com
446
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.98
447
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.98/CHANGELOG.md
446
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.100
447
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.100/CHANGELOG.md
448
448
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
449
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.98
449
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.100
450
450
  funding_uri: https://github.com/sponsors/pboling
451
451
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
452
452
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file