kettle-family 1.2.85 → 1.2.87

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: 91a1c4bf31a3387bf5e7170148accfe48611ebe4596466384e2ac34a228a0a07
4
- data.tar.gz: ac8fc14eae4354a6dc774158a60c4bd522cc0db9ed530b775f310e2c37eb5650
3
+ metadata.gz: 7086b3a051cb1074cba223e715123b349a3e7d4cb5f46722288e445fac66dc7c
4
+ data.tar.gz: e67273a3a81b3a2b8a471357715849f88edb5bcc93c22dc8d8ed3d2b98a20aad
5
5
  SHA512:
6
- metadata.gz: 2495673c5566c9233553c229ac8e8b2973037a563fb97d29c22dc87852cb67811720768bfc290ffa78ac125eafb655614e857f42779f7441b8d6d5ba080bfe6d
7
- data.tar.gz: 3909f42f4f582bd05fe26997963b9778c81afbd129775c9df77d7d229ffffb5a181fccce7a6f46caff377f819b15feb922009e849b75a1815ae94ac5d47e1f2d
6
+ metadata.gz: 225bf1edf702b9012649f4cd6a754a05bfbcec584c2bd2557a786a8b74be74991a63fc6e252902fdd42960bf3bf1b5ad1d3ad111e12d192a4ff11a6749dd5418
7
+ data.tar.gz: 3534c07147cf99302f3afdb23bad476335a4d8c7fba7ac9bf2e83db2895a242e0fb4802ecd3209bad259c7229d6b8cc1dd35f698e18fa40d8f2b17f7c9be2158
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,32 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.2.87] - 2026-09-06
34
+
35
+ - TAG: [v1.2.87][1.2.87t]
36
+ - COVERAGE: 93.10% -- 5813/6244 lines in 34 files
37
+ - BRANCH COVERAGE: 76.17% -- 2356/3093 branches in 34 files
38
+ - 31.79% documented
39
+
40
+ ### Fixed
41
+
42
+ - Allow template-managed local lockfiles and modular Gemfiles to continue a template run without autostashing.
43
+
44
+ ## [1.2.86] - 2026-09-06
45
+
46
+ - TAG: [v1.2.86][1.2.86t]
47
+ - COVERAGE: 93.09% -- 5805/6236 lines in 34 files
48
+ - BRANCH COVERAGE: 76.16% -- 2354/3091 branches in 34 files
49
+ - 31.88% documented
50
+
51
+ ### Fixed
52
+
53
+ - Recover template lockfiles in the configured local dependency graph instead of release normalization mode.
54
+
55
+ - Use the templating dependency graph during initial template lockfile preparation.
56
+
57
+ - Define execution profiles and add real Bundler compatibility scenarios for template and release lockfile isolation.
58
+
33
59
  ## [1.2.85] - 2026-09-06
34
60
 
35
61
  - TAG: [v1.2.85][1.2.85t]
@@ -2523,7 +2549,11 @@ Please file a bug if you notice a violation of semantic versioning.
2523
2549
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
2524
2550
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
2525
2551
 
2526
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.85...HEAD
2552
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.87...HEAD
2553
+ [1.2.87]: https://github.com/kettle-dev/kettle-family/compare/v1.2.86...v1.2.87
2554
+ [1.2.87t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.87
2555
+ [1.2.86]: https://github.com/kettle-dev/kettle-family/compare/v1.2.85...v1.2.86
2556
+ [1.2.86t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.86
2527
2557
  [1.2.85]: https://github.com/kettle-dev/kettle-family/compare/v1.2.84...v1.2.85
2528
2558
  [1.2.85t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.85
2529
2559
  [1.2.84]: https://github.com/kettle-dev/kettle-family/compare/v1.2.83...v1.2.84
data/README.md CHANGED
@@ -799,9 +799,13 @@ kettle-family template --execute \
799
799
 
800
800
  ### Template Bootstrap Environment
801
801
 
802
+ The complete path-gem, lockfile, install, platform, and mutation policy is
803
+ defined in [Execution Profiles](EXECUTION_PROFILES.md). The rules below are
804
+ the template-local portion of that policy.
805
+
802
806
  Before invoking `kettle-jem`, `kettle-family template` may refresh `nomono` so
803
- the member Gemfile can activate its generated local dependency wiring. That
804
- bootstrap uses the same development dependency graph as template preparation:
807
+ the member Gemfile can activate its generated local dependency wiring. Both
808
+ bootstrap and lockfile preparation use the same development dependency graph:
805
809
 
806
810
  - `K_JEM_TEMPLATING=true` is enforced. It selects the templating Gemfile graph
807
811
  and activates generated local sibling closures. It is not a release-lockfile
@@ -820,6 +824,11 @@ bootstrap uses the same development dependency graph as template preparation:
820
824
  Release lockfile normalization is a separate operation with its own local-path
821
825
  policy; it must not be inferred from template bootstrap settings.
822
826
 
827
+ If template lockfile preparation needs a recovery update, that retry also uses
828
+ the template dependency graph. It must not invoke release-lockfile reset logic:
829
+ release normalization intentionally disables local paths, while template
830
+ recovery needs configured sibling paths to resolve unreleased family versions.
831
+
823
832
  ## 🔐 Security
824
833
 
825
834
  See [SECURITY.md][🔐security].
@@ -1072,7 +1081,7 @@ Thanks for RTFM. ☺️
1072
1081
  [📌gitmoji]: https://gitmoji.dev
1073
1082
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1074
1083
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1075
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.215-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1084
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.244-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1076
1085
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
1077
1086
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1078
1087
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kettle
4
+ module Family
5
+ # The execution context is a compatibility boundary, not a collection of
6
+ # incidental ENV assignments. Each workflow command selects one profile so
7
+ # path-gem, lockfile, install, and mutation rules remain reviewable.
8
+ class ExecutionProfile
9
+ Definition = Struct.new(
10
+ :name,
11
+ :path_gem_policy,
12
+ :lockfile_role,
13
+ :install_location,
14
+ :host_platform_policy,
15
+ :allowed_mutations
16
+ ) do
17
+ def local_path_gems?
18
+ path_gem_policy != :registry_only
19
+ end
20
+
21
+ def canonical_lockfile?
22
+ lockfile_role == :canonical
23
+ end
24
+ end
25
+
26
+ DEFINITIONS = {
27
+ development_local: Definition.new(
28
+ name: :development_local,
29
+ path_gem_policy: :configured_siblings,
30
+ lockfile_role: :canonical,
31
+ install_location: :member,
32
+ host_platform_policy: :active_platform_must_resolve,
33
+ allowed_mutations: %i[development_lockfiles source test]
34
+ ),
35
+ template_local: Definition.new(
36
+ name: :template_local,
37
+ path_gem_policy: :configured_siblings,
38
+ lockfile_role: :canonical,
39
+ install_location: :member,
40
+ host_platform_policy: :active_platform_must_resolve,
41
+ allowed_mutations: %i[template development_lockfiles generated]
42
+ ),
43
+ release_registry: Definition.new(
44
+ name: :release_registry,
45
+ path_gem_policy: :registry_only,
46
+ lockfile_role: :canonical,
47
+ install_location: :disposable,
48
+ host_platform_policy: :active_platform_must_resolve,
49
+ allowed_mutations: %i[canonical_release_lockfiles]
50
+ ),
51
+ release_monorepo: Definition.new(
52
+ name: :release_monorepo,
53
+ path_gem_policy: :configured_release_wave,
54
+ lockfile_role: :canonical,
55
+ install_location: :disposable,
56
+ host_platform_policy: :active_platform_must_resolve,
57
+ allowed_mutations: %i[canonical_release_lockfiles configured_wave_paths]
58
+ ),
59
+ release_recovery: Definition.new(
60
+ name: :release_recovery,
61
+ path_gem_policy: :configured_release_wave,
62
+ lockfile_role: :canonical,
63
+ install_location: :disposable,
64
+ host_platform_policy: :active_platform_must_resolve,
65
+ allowed_mutations: %i[canonical_release_lockfiles configured_wave_paths]
66
+ )
67
+ }.each_value(&:freeze).freeze
68
+
69
+ def self.fetch(name)
70
+ DEFINITIONS.fetch(name.to_sym)
71
+ rescue KeyError
72
+ raise ArgumentError, "unknown execution profile: #{name.inspect}"
73
+ end
74
+ end
75
+ end
76
+ end
@@ -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.85"
8
+ VERSION = "1.2.87"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -60,6 +60,7 @@ module Kettle
60
60
  "bupb" => %w[bundle update --bundler]
61
61
  }.freeze
62
62
  TEMPLATE_AUTOSTASH_ALLOWED_DIRECTORIES = %w[lib spec test].freeze
63
+ TEMPLATE_MANAGED_DIRTY_PATH_PATTERN = %r{(?:\A|/)(?:Gemfile\.lock|Appraisal\.root\.gemfile\.lock|\.structuredmerge/kettle-jem\.lock|gemfiles/modular/[^/]+_local\.gemfile)\z}
63
64
  PRE_TEMPLATE_BOOTSTRAP_GEMS = %w[nomono kettle-dev].freeze
64
65
  GIT_SYNC_COMMANDS = {
65
66
  "push" => [["push", %w[git push]]],
@@ -266,20 +267,23 @@ module Kettle
266
267
  break
267
268
  end
268
269
 
269
- stash_result = runner.call(
270
- member: member,
271
- phase: "template_autostash",
272
- command: ["sh", "-lc", template_autostash_command(member)]
273
- )
274
- results << stash_result
275
- break unless stash_result.ok?
270
+ stashable_paths = template_autostashable_dirty_paths(dirty_paths)
271
+ if stashable_paths.any?
272
+ stash_result = runner.call(
273
+ member: member,
274
+ phase: "template_autostash",
275
+ command: ["sh", "-lc", template_autostash_command(member, stashable_paths)]
276
+ )
277
+ results << stash_result
278
+ break unless stash_result.ok?
276
279
 
277
- stash_ref = stash_result.stdout.to_s.lines.last.to_s.strip
278
- if stash_ref.empty?
279
- results << template_sync_failure_result(member, "could not determine the temporary template autostash reference")
280
- break
280
+ stash_ref = stash_result.stdout.to_s.lines.last.to_s.strip
281
+ if stash_ref.empty?
282
+ results << template_sync_failure_result(member, "could not determine the temporary template autostash reference")
283
+ break
284
+ end
285
+ stashes << {member: member, ref: stash_ref}
281
286
  end
282
- stashes << {member: member, ref: stash_ref}
283
287
  end
284
288
 
285
289
  upstream = git_upstream_for(member)
@@ -304,7 +308,11 @@ module Kettle
304
308
  def template_blocking_dirty_paths(dirty_paths)
305
309
  return dirty_paths unless autostash
306
310
 
307
- dirty_paths.reject { |path| template_autostash_allowed_path?(path) }
311
+ dirty_paths.reject { |path| template_autostash_allowed_path?(path) || template_managed_dirty_path?(path) }
312
+ end
313
+
314
+ def template_autostashable_dirty_paths(dirty_paths)
315
+ dirty_paths.select { |path| template_autostash_allowed_path?(path) }
308
316
  end
309
317
 
310
318
  def template_autostash_allowed_path?(status_line)
@@ -312,6 +320,10 @@ module Kettle
312
320
  TEMPLATE_AUTOSTASH_ALLOWED_DIRECTORIES.include?(path.split("/", 2).first)
313
321
  end
314
322
 
323
+ def template_managed_dirty_path?(status_line)
324
+ GitStatus.path_from_status_line(status_line).match?(TEMPLATE_MANAGED_DIRTY_PATH_PATTERN)
325
+ end
326
+
315
327
  def restore_template_autostashes(stashes, runner:, preserve_members: [])
316
328
  preserve_members = Array(preserve_members).map(&:to_s)
317
329
  stashes.reverse_each.map do |stash|
@@ -467,9 +479,10 @@ module Kettle
467
479
  status.success? ? stdout.strip : nil
468
480
  end
469
481
 
470
- def template_autostash_command(member)
482
+ def template_autostash_command(member, paths)
471
483
  label = "kettle-family-template-#{member.name}-#{Process.pid}-#{Time.now.to_i}"
472
- "git stash push --include-untracked --message #{Shellwords.escape(label)} && " \
484
+ pathspecs = paths.map { |path| Shellwords.escape(GitStatus.path_from_status_line(path)) }.join(" ")
485
+ "git stash push --include-untracked --message #{Shellwords.escape(label)} -- #{pathspecs} && " \
473
486
  "ref=$(git stash list -1 --format=%gd) && " \
474
487
  "git checkout \"$ref\" -- .tool-versions 2>/dev/null || true; " \
475
488
  "for path in mise.toml .mise.toml; do " \
@@ -479,7 +492,7 @@ module Kettle
479
492
 
480
493
  def template_dirty_worktree_result(member, dirty_paths)
481
494
  stash_guidance = if autostash
482
- "automatic template autostash is limited to lib/, spec/, and test/ files"
495
+ "automatic template autostash is limited to lib/, spec/, and test/ files; generated lockfiles and modular *_local.gemfile files are left in place"
483
496
  else
484
497
  "automatic template autostash is disabled"
485
498
  end
@@ -833,8 +846,6 @@ module Kettle
833
846
  # available only for an explicit, known optional-dependency constraint
834
847
  # conflict; it is not a general solution for local-path resolution.
835
848
  env["KETTLE_DEV_SKIP_CHANGELOG_DEPENDENCY"] = template_bootstrap_changelog_dependency_setting
836
- env["K_JEM_TEMPLATING"] = "true"
837
- env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
838
849
  env
839
850
  end
840
851
 
@@ -4386,13 +4397,13 @@ module Kettle
4386
4397
  end
4387
4398
 
4388
4399
  def recover_template_lockfiles(member:, runner:, memo:, phase:)
4389
- reset_gemfile_lock(
4400
+ result = runner.call(
4390
4401
  member: member,
4391
- runner: runner,
4392
- memo: memo,
4393
4402
  phase: phase,
4394
- env: template_lockfile_recovery_env(member)
4403
+ command: normalize_lockfiles_command(member: member, phase: "prepare_lockfiles"),
4404
+ env: template_lockfile_recovery_env
4395
4405
  )
4406
+ memo << result
4396
4407
  return unless memo.last&.ok?
4397
4408
 
4398
4409
  commit_normalized_lockfiles(
@@ -4417,15 +4428,11 @@ module Kettle
4417
4428
  [result.stdout, result.stderr].join("\n").match?(/Unknown switches? .*--add-checksums/)
4418
4429
  end
4419
4430
 
4420
- def template_lockfile_recovery_env(member)
4421
- env = release_lockfile_env(member).merge(workflow_env)
4422
- ENV.each do |key, value|
4423
- env[key] = value if key.end_with?("_DEV", "_LOCAL") && local_path_env_requested?(key)
4424
- end
4425
- env.merge!(env_overrides)
4426
- env["K_JEM_TEMPLATING"] = "false"
4427
- env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
4428
- env
4431
+ def template_lockfile_recovery_env(_member = nil)
4432
+ # A failed template preparation can be caused by an unreleased sibling
4433
+ # version. Recover in the template graph, not the release graph, so
4434
+ # Bundler uses the configured monorepo paths.
4435
+ execution_profile(template_execution_profile, workflow_env)
4429
4436
  end
4430
4437
 
4431
4438
  def normalize_lockfiles_command(member:, phase:, skip_checksum_option: false)
@@ -4631,7 +4638,37 @@ module Kettle
4631
4638
  # Templating is development work. It must resolve the configured
4632
4639
  # family graph exactly as template application does; release-only
4633
4640
  # lockfile cleanup owns disabling local path sources.
4634
- workflow_env
4641
+ execution_profile(template_execution_profile, workflow_env)
4642
+ end
4643
+
4644
+ def template_execution_profile
4645
+ ExecutionProfile.fetch(:template_local)
4646
+ end
4647
+
4648
+ def release_lockfile_execution_profile
4649
+ return ExecutionProfile.fetch(:release_recovery) if release_recovery?
4650
+ return ExecutionProfile.fetch(:release_monorepo) if preserve_monorepo_template_context?
4651
+
4652
+ ExecutionProfile.fetch(:release_registry)
4653
+ end
4654
+
4655
+ def release_recovery?
4656
+ !start_step.nil? || !skip_steps.to_s.empty? || fast_recovery || skip_ci
4657
+ end
4658
+
4659
+ def execution_profile(profile, env)
4660
+ profile = ExecutionProfile.fetch(profile) unless profile.is_a?(ExecutionProfile::Definition)
4661
+ env = env.dup
4662
+ case profile.name
4663
+ when :template_local
4664
+ env["K_JEM_TEMPLATING"] = "true"
4665
+ env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
4666
+ when :release_registry, :release_monorepo, :release_recovery
4667
+ # Release profiles deliberately leave Bundler mutation decisions to
4668
+ # kettle-dev. The profile selection controls which local-path
4669
+ # overrides this workflow contributes below.
4670
+ end
4671
+ env
4635
4672
  end
4636
4673
 
4637
4674
  def local_path_env_requested?(name)
@@ -4685,10 +4722,13 @@ module Kettle
4685
4722
  end
4686
4723
 
4687
4724
  def release_lockfile_env(member = nil)
4688
- base_release_env
4725
+ execution_profile(
4726
+ release_lockfile_execution_profile,
4727
+ base_release_env
4689
4728
  .merge(release_lockfile_bundler_env_resets)
4690
4729
  .merge(env_overrides)
4691
4730
  .merge(release_lockfile_local_path_env_overrides(member))
4731
+ )
4692
4732
  end
4693
4733
 
4694
4734
  def release_lockfile_bundler_env_resets
data/lib/kettle/family.rb CHANGED
@@ -21,6 +21,7 @@ require_relative "family/nomono_bootstrap"
21
21
  require_relative "family/debugger_bootstrap"
22
22
  require_relative "family/appraisal_bootstrap"
23
23
  require_relative "family/release_waves"
24
+ require_relative "family/execution_profile"
24
25
  require_relative "family/workflow"
25
26
  require_relative "family/discovery"
26
27
  require_relative "family/orderer"
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.85
4
+ version: 1.2.87
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -91,7 +91,7 @@ dependencies:
91
91
  requirements:
92
92
  - - ">="
93
93
  - !ruby/object:Gem::Version
94
- version: 3.0.30
94
+ version: 3.0.31
95
95
  - - "<"
96
96
  - !ruby/object:Gem::Version
97
97
  version: 4.0.0
@@ -101,7 +101,7 @@ dependencies:
101
101
  requirements:
102
102
  - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: 3.0.30
104
+ version: 3.0.31
105
105
  - - "<"
106
106
  - !ruby/object:Gem::Version
107
107
  version: 4.0.0
@@ -414,6 +414,7 @@ files:
414
414
  - lib/kettle/family/debugger_bootstrap.rb
415
415
  - lib/kettle/family/dependency_floor.rb
416
416
  - lib/kettle/family/discovery.rb
417
+ - lib/kettle/family/execution_profile.rb
417
418
  - lib/kettle/family/git_status.rb
418
419
  - lib/kettle/family/local_install.rb
419
420
  - lib/kettle/family/member.rb
@@ -441,10 +442,10 @@ licenses:
441
442
  - AGPL-3.0-only
442
443
  metadata:
443
444
  homepage_uri: https://kettle-family.galtzo.com
444
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.85
445
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.85/CHANGELOG.md
445
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.87
446
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.87/CHANGELOG.md
446
447
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
447
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.85
448
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.87
448
449
  funding_uri: https://github.com/sponsors/pboling
449
450
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
450
451
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file