kettle-jem 7.1.28 → 7.1.30

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +220 -1
  4. data/README.md +2 -114
  5. data/lib/kettle/jem/maintenance_changelog.rb +1 -1
  6. data/lib/kettle/jem/relative_paths.rb +61 -0
  7. data/lib/kettle/jem/tasks/install_task.rb +9 -5
  8. data/lib/kettle/jem/tasks/prepare_task.rb +29 -10
  9. data/lib/kettle/jem/tasks/template_task.rb +18 -3
  10. data/lib/kettle/jem/templates/.devcontainer/devcontainer.json.example +1 -1
  11. data/lib/kettle/jem/templates/.git-hooks/prepare-commit-msg.example +3 -4
  12. data/lib/kettle/jem/templates/.github/workflows/coverage.yml.example +24 -2
  13. data/lib/kettle/jem/templates/.github/workflows/current.yml.example +13 -14
  14. data/lib/kettle/jem/templates/.github/workflows/dep-heads.yml.example +8 -5
  15. data/lib/kettle/jem/templates/.github/workflows/framework-ci.yml.example +4 -1
  16. data/lib/kettle/jem/templates/.github/workflows/heads.yml.example +16 -66
  17. data/lib/kettle/jem/templates/.github/workflows/jruby-10.0.yml.example +16 -3
  18. data/lib/kettle/jem/templates/.github/workflows/jruby-9.1.yml.example +16 -3
  19. data/lib/kettle/jem/templates/.github/workflows/jruby-9.2.yml.example +16 -3
  20. data/lib/kettle/jem/templates/.github/workflows/jruby-9.3.yml.example +16 -3
  21. data/lib/kettle/jem/templates/.github/workflows/jruby-9.4.yml.example +16 -3
  22. data/lib/kettle/jem/templates/.github/workflows/jruby.yml.example +16 -3
  23. data/lib/kettle/jem/templates/.github/workflows/license-eye.yml.example +22 -1
  24. data/lib/kettle/jem/templates/.github/workflows/locked_deps.yml.example +2 -1
  25. data/lib/kettle/jem/templates/.github/workflows/opencollective.yml.example +1 -1
  26. data/lib/kettle/jem/templates/.github/workflows/ruby-2.3.yml.example +8 -2
  27. data/lib/kettle/jem/templates/.github/workflows/ruby-2.4.yml.example +8 -2
  28. data/lib/kettle/jem/templates/.github/workflows/ruby-2.5.yml.example +8 -2
  29. data/lib/kettle/jem/templates/.github/workflows/ruby-2.6.yml.example +8 -2
  30. data/lib/kettle/jem/templates/.github/workflows/ruby-2.7.yml.example +8 -2
  31. data/lib/kettle/jem/templates/.github/workflows/ruby-3.0.yml.example +9 -3
  32. data/lib/kettle/jem/templates/.github/workflows/ruby-3.1.yml.example +8 -2
  33. data/lib/kettle/jem/templates/.github/workflows/ruby-3.2.yml.example +8 -2
  34. data/lib/kettle/jem/templates/.github/workflows/ruby-3.3.yml.example +8 -2
  35. data/lib/kettle/jem/templates/.github/workflows/ruby-3.4.yml.example +8 -2
  36. data/lib/kettle/jem/templates/.github/workflows/style.yml.example +5 -2
  37. data/lib/kettle/jem/templates/.github/workflows/templating.yml.example +18 -2
  38. data/lib/kettle/jem/templates/.github/workflows/truffle.yml.example +16 -3
  39. data/lib/kettle/jem/templates/.github/workflows/truffleruby-22.3.yml.example +16 -3
  40. data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.1.yml.example +12 -2
  41. data/lib/kettle/jem/templates/.github/workflows/truffleruby-24.2.yml.example +16 -3
  42. data/lib/kettle/jem/templates/.github/workflows/truffleruby-25.0.yml.example +16 -3
  43. data/lib/kettle/jem/templates/.github/workflows/truffleruby-33.0.yml.example +16 -3
  44. data/lib/kettle/jem/templates/.github/workflows/unlocked_deps.yml.example +6 -2
  45. data/lib/kettle/jem/templates/.structuredmerge/kettle-jem.yml.example +19 -0
  46. data/lib/kettle/jem/templates/Appraisal.root.gemfile.example +2 -1
  47. data/lib/kettle/jem/templates/Appraisals.example +4 -1
  48. data/lib/kettle/jem/templates/CHANGELOG.transfer.md +6 -0
  49. data/lib/kettle/jem/templates/Gemfile.example +12 -3
  50. data/lib/kettle/jem/templates/README.md.example +5 -5
  51. data/lib/kettle/jem/templates/Rakefile.example +2 -0
  52. data/lib/kettle/jem/templates/bin/turbo_tests2.example +14 -0
  53. data/lib/kettle/jem/templates/gem.gemspec.example +6 -6
  54. data/lib/kettle/jem/templates/gemfiles/modular/coverage.gemfile.example +1 -1
  55. data/lib/kettle/jem/templates/gemfiles/modular/coverage_local.gemfile.example +10 -12
  56. data/lib/kettle/jem/templates/gemfiles/modular/documentation.gemfile.example +5 -5
  57. data/lib/kettle/jem/templates/gemfiles/modular/ostruct/r4/v0.6.gemfile.example +2 -0
  58. data/lib/kettle/jem/templates/gemfiles/modular/style.gemfile.example +15 -8
  59. data/lib/kettle/jem/templates/gemfiles/modular/style_local.gemfile.example +7 -1
  60. data/lib/kettle/jem/templates/gemfiles/modular/templating.gemfile.example +3 -2
  61. data/lib/kettle/jem/templates/gemfiles/modular/templating_local.gemfile.example +18 -19
  62. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r4/libs.gemfile.example +1 -0
  63. data/lib/kettle/jem/templates/mise.toml.example +1 -1
  64. data/lib/kettle/jem/templates/mise.toml.no-osc.example +1 -1
  65. data/lib/kettle/jem/templates/readme/partials/configuration.md.example +4 -3
  66. data/lib/kettle/jem/templates/shim/Gemfile.example +9 -3
  67. data/lib/kettle/jem/templates/shim/gem.gemspec.example +3 -3
  68. data/lib/kettle/jem/version.rb +1 -1
  69. data/lib/kettle/jem.rb +1214 -723
  70. data.tar.gz.sig +0 -0
  71. metadata +28 -67
  72. metadata.gz.sig +0 -0
  73. data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.0.yml.example +0 -92
  74. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_0.gemfile.example +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d376a29518ac65e717447c082905690ce8c35165dab1f2daa4f7d9ed39c6fafc
4
- data.tar.gz: 4e1fce2ccfdcbe1359a51884c70a226b3727e0f035cd338ea9f8d4f29e5c386b
3
+ metadata.gz: 8009dad8893b30d251357d02f553a4d32af34d29bffcb274ce911eeb69d30326
4
+ data.tar.gz: 3b19519b494622d3c73ef6e6f92ef94cd82230a98351969bd00cea49a6d6bdbc
5
5
  SHA512:
6
- metadata.gz: a8935378edb64106770005344bb70bed79d6cfed2549021ab2dc9211b7b247be51558e22d65b2b51383e0c5b381307280beac7b67ff18742cde977046fac1dbd
7
- data.tar.gz: e3f8a7616be8133fa86f75621fd435aed010144f71ad587b222dd0cd2fe311c773db06b0df8ade57f43b909ae42fdb9eda96fb268110f9b5f23bc6ac7e4e1580
6
+ metadata.gz: fe7724acb8c7af1f5f2a2f58e18b601aef0e94b74555c70215b5f2fd78e5119a05c5641df4e2352c25464f48cfadbf48b83737050966ea96f48d6ef7e1fd1215
7
+ data.tar.gz: 51286f474e10f2da5e83c19672ae074f175cbcb41dbc813adeb4bc57d5f05328192335eb31e1854f362a925ec97ea10d3351b57570cf8dd5146250e54ed3e44c
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,221 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [7.1.30] - 2026-10-15
34
+
35
+ - TAG: [v7.1.30][7.1.30t]
36
+ - COVERAGE: 93.50% -- 11359/12149 lines in 17 files
37
+ - BRANCH COVERAGE: 77.27% -- 4917/6363 branches in 17 files
38
+ - 19.72% documented
39
+
40
+ ### Changed
41
+
42
+ - [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
43
+ - kettle-changelog (>= 1.0.9 -> >= 1.0.10)
44
+ - kettle-dev (>= 3.1.11 -> >= 3.1.13)
45
+ - kettle-test (>= 2.0.24 -> >= 2.0.25)
46
+ - rspec-stubbed_env (>= 1.0.6 -> >= 1.0.17)
47
+ - turbo_tests2 (>= 3.2.14 -> >= 4.0.0)
48
+
49
+ - [kc] kettle-jem/template: updated 7 project files:
50
+ - code and tests (1)
51
+ - dependencies (4)
52
+ - documentation (1)
53
+ - other (1)
54
+
55
+ - JRuby workflow templates now cap worker JVMs at 2 (TURBO_TESTS2_MAX_PROCESSES=2) alongside the -Xmx1280m heap cap, and truffleruby workflow templates no longer force serial-only lanes (MAX_PROCESSES 1 -> 2): turbo_tests2 workers are spawn-based and engine-portable, so JVM engines run parallel workers safely once workers x per-worker heap is bounded under the runner RAM; the old serial-only setting wasted CI time and made lanes trip the stall watchdog on legitimately long tests
56
+
57
+ - [kc] kettle-jem-workflow-pins: Update pinned GitHub Actions in kettle-jem templates:
58
+ - ruby/setup-ruby v1.328.0 (e81a8fa391b11a595c1b7ed84177cc0fe02faf83) -> v1.329.0 (651ff0d62c5c79bed6d53ec518387aae20e72c1a)
59
+
60
+ - Dependency floors raised: turbo_tests2 4.0.0 (drops parallel_tests for runtime_groups) in the template gemspec and managed-floors registry
61
+
62
+ ### Fixed
63
+
64
+ - JRuby workflow templates now set TURBO_TESTS2_WATCHDOG_SECONDS=1500 (matching the truffleruby serial-lane margin): JRuby engines cannot fork, so turbo_tests2 runs serial and a single slow spec could exceed the previous 900s watchdog and fail the whole lane (jruby-9.2 killed mid-run after 805 passing examples)
65
+
66
+ - JRuby workflow templates now cap each turbo_tests2 worker JVM at -Xmx1280m via JRUBY_OPTS: without a cap JRuby defaults to 1/4 of runner RAM per worker and concurrent workers get OOM-killed silently mid-run, which the watchdog then reports as total worker silence (the jruby-9.2 lane died this way with 805 examples passing)
67
+
68
+ ## [7.1.29] - 2026-10-11
69
+
70
+ - TAG: [v7.1.29][7.1.29t]
71
+ - COVERAGE: 93.49% -- 11342/12132 lines in 17 files
72
+ - BRANCH COVERAGE: 77.26% -- 4907/6351 branches in 17 files
73
+ - 19.68% documented
74
+
75
+ ### Added
76
+
77
+ - Engine-incompatible dev-dependency detection (case 3 of the dev-dependency
78
+ conflict policy, GEMSPEC_DEV_DEPENDENCY_PLAN.md Phase 4): a direct
79
+ development dependency with a `KNOWN_GEM_CONFLICT_RESOLUTIONS`
80
+ `:engine_incompatible` entry now surfaces a `dependency_conflicts.resolve`
81
+ review entry when it doesn't support one or more of the project's declared
82
+ `engines:` and has no template-managed modular home. Pilot registry entry:
83
+ `sqlite3` (no `jruby`-platform release ever published). Shares the same
84
+ `dependency_conflicts.resolve` list, decision-matching, and review gate as
85
+ the existing template-managed (case 1) conflicts, and the same
86
+ auto-resolution path for any future entry in this category with
87
+ `force_review: false`.
88
+ - `workflows.framework_matrix` gemfiles for gems tracked by kettle-rb's
89
+ `Kettle::Rb::GemFloors` (activemodel, activerecord, activesupport, sqlite3)
90
+ require the minor series' security floor when the version's requirement
91
+ still admits the series' first release (e.g.
92
+ `gem "activerecord", "~> 7.1.0", ">= 7.1.6"`), and carry a `# kettle-rb:`
93
+ warning comment when no release in the series fixes an advisory.
94
+
95
+ - kettle-jem-template-20260913-001 - Templating now also surfaces a review
96
+ entry in `dependency_conflicts.resolve` when a direct development
97
+ dependency doesn't support one or more of this project's declared
98
+ `engines:` and has no template-managed modular home (e.g. `sqlite3` on
99
+ `jruby`). Review each entry and pick a resolution per the project's own
100
+ engine support needs.
101
+
102
+ - Templating plan phases now prefetch the tree-sitter-language-pack grammars used by template merges (bash, go, html, json, json5, markdown, rbs, ruby, rust, toml, tsx, typescript, yaml) through the language pack's hot-load API before recipes run, so on-demand grammar downloads happen up front with attributable diagnostics instead of failing inside a merge. Configure via KJ_TSLP_PREFETCH_LANGUAGES (space/comma list, or false to disable).
103
+
104
+ - kettle-jem-deps-floor: add FLOOR_HOLDS registry to exclude a dependency floor from automatic raising when a newer release would produce an unresolvable dependency set in generated projects. Held floors are skipped during planning and reported separately (held_floors in JSON, a 'held' line in text output) so the exclusion is auditable rather than silent. First entry: rubocop-minitest (0.41.0 needs rubocop >= 1.89, but standard ~> 1.56 caps rubocop at ~> 1.88).
105
+
106
+ - Add Kettle::Rb::PlatformSupport: generated OS-platform support evidence for CI matrix generation. Introduces the CI OS-family vocabulary (linux, macos, windows) as a fourth, explicitly distinct platform vocabulary, with runner-label mapping and per-engine support claims that each carry a citation. TruffleRuby Windows support (zero Windows assets across all releases) and the RubyInstaller series (including ARM64 from 3.4) are derived from live APIs on regeneration; every other claim is documentation-sourced. Regenerate with rake kettle:rb:platform_support:generate.
107
+
108
+ - kettle-jem: first-class OS-platform support for the CI matrix (spec slice 1038). A new documented `platforms:` config key (CI OS families linux/macos/windows; default linux+macos, Windows opt-in only) drives generation of the current.yml OS matrix from facts via new KJ|CI:OS_MATRIX_ENTRIES and KJ|CI:OS_TEST_STEPS tokens, and a generated KJ|README:PLATFORM_SUPPORT_TABLE tier table under Runtime and Platform Support. Re-templating a repo with existing Windows CI detects and preserves windows via workflow evidence, both in the injected config key and in the facts themselves, so first-run templating cannot silently drop Windows coverage. Platform evidence lives in Kettle::Rb::PlatformSupport (kettle-rb 0.1.17+), with per-claim citations.
109
+
110
+ ### Changed
111
+
112
+ - Require kettle-rb `>= 0.1.14` (security floor data).
113
+ - The `sqlite3` `KNOWN_GEM_CONFLICT_RESOLUTIONS` reason now points at a
114
+ `platforms: [:ruby]` modular gemfile (kettle-jem-appraisals generates
115
+ `gemfiles/modular/activerecord_support/*.gemfile`) instead of saying sqlite3
116
+ has no modular home.
117
+
118
+ - [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
119
+ - gitmoji-regex (>= 2.0.13 -> >= 2.0.15)
120
+ - kettle-changelog (>= 1.0.8 -> >= 1.0.9)
121
+ - kettle-dev (>= 3.1.5 -> >= 3.1.10)
122
+ - kettle-drift (>= 1.0.15 -> >= 1.0.16)
123
+ - kettle-family (>= 1.3.3 -> >= 1.3.10)
124
+ - kettle-soup-cover (>= 3.0.11 -> >= 3.0.12)
125
+ - kettle-test (>= 2.0.23 -> >= 2.0.24)
126
+ - nomono (>= 1.1.6 -> >= 1.1.7)
127
+ - reek (>= 6.5.0 -> >= 6.6.0)
128
+ - rubocop-lts-rspec (>= 1.0.6 -> >= 1.0.7)
129
+ - rubocop-minitest (>= 0.40.0 -> >= 0.41.0)
130
+ - standard (>= 1.56.0 -> >= 1.57.0)
131
+ - turbo_tests2 (>= 3.2.12 -> >= 3.2.14)
132
+ - yaml-converter (>= 0.2.6 -> >= 0.2.7)
133
+ - yard-fence (>= 0.9.9 -> >= 0.9.10)
134
+ - yard-timekeeper (>= 0.2.7 -> >= 0.2.9)
135
+
136
+ - [kc] kettle-jem-workflow-pins: Update pinned GitHub Actions in kettle-jem templates:
137
+ - actions/upload-artifact v7.0.1 (043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) -> v7.0.2 (cf430e030ddbb5b0abf93d22962f4752f3646cd9)
138
+ - appraisal-rb/setup-ruby-flash v2.7 (b7ed1caf57f7986cedd2acca8624960b0820bc9a) -> v2.14 (6f8ad36ba7488db591541483afb5464d01e8861b)
139
+ - github/codeql-action/analyze v4.38.2 (2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2) -> v4.38.3 (24c54180a607b1449ed407dd24f251e4e9147c8d)
140
+ - github/codeql-action/autobuild v4.38.2 (2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2) -> v4.38.3 (24c54180a607b1449ed407dd24f251e4e9147c8d)
141
+ - github/codeql-action/init v4.38.2 (2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2) -> v4.38.3 (24c54180a607b1449ed407dd24f251e4e9147c8d)
142
+ - ruby/setup-ruby v1.327.0 (14594264cd68ce8a2345dd349bc3d138a4ef85c8) -> v1.328.0 (e81a8fa391b11a595c1b7ed84177cc0fe02faf83)
143
+
144
+ - [kc] kettle-jem/prepare: updated 14 project files:
145
+ - dependencies (14)
146
+
147
+ - [kc] kettle-jem/template: updated 12 project files:
148
+ - code and tests (1)
149
+ - other (1)
150
+ - workflows (10)
151
+
152
+ - The templating workflow template now restores tree-sitter-language-pack grammar downloads from actions/cache keyed by the templating lockfiles, keeping the self-test off the network in the common case; a cache miss falls back to the in-run tslp_prefetch phase.
153
+
154
+ - [kc] license-eye-workflow-retry: The generated `.github/workflows/license-eye.yml` retries the license check once before failing. The pinned `apache/skywalking-eyes/dependency` action is a composite that builds license-eye from source on every run, and its own dependency cache is broken upstream: `dependency/action.yml` passes `cache-dependency-path: ${{ github.action_path }}/go.sum`, but `go.sum` lives at the action's repository root rather than in `dependency/` (404 versus 200). setup-go therefore logs "Restore cache failed: Some specified paths were not resolved", the module cache never restores, and each run re-downloads roughly 35 Go modules and re-runs `make`. Every templated repository is consequently exposed to a transient `proxy.golang.org` error on every push; one such run failed with `stream error: stream ID 133; INTERNAL_ERROR; received from peer` after eleven consecutive successes. The retry uses the same `[Attempt 1]`/`[Attempt 2]` idiom as the generated Ruby matrix workflows, and the action pin is unchanged.
155
+
156
+ - Drop TruffleRuby 23.0 and TruffleRuby HEAD from generated CI. Both deadlocked in the parallel test worker pool during the turbo_tests2 v3.2.14 release: 23.0 was cancelled after hitting its own 30-minute cap with orphan ruby processes terminated, and HEAD ran to GitHub's 6-hour ceiling before being cancelled. 23.0 already carried the serial-worker mitigation (TURBO_TESTS2_MAX_PROCESSES=1), and its log shows '1 processes for 13 specs' followed by 29 minutes of silence, so forcing a single worker did not resolve the deadlock. Removes the truffleruby-23.0 workflow and json gemfile templates, the truffleruby job from the heads workflow template, and the matching README compatibility badges and reference definitions. TruffleRuby 23.1, 24.2, 25.0, 33.0 and current all pass and remain generated; the heads workflow keeps its ruby-head and jruby-head jobs, which both succeed.
157
+
158
+ - [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
159
+ - kettle-dev (>= 3.1.10 -> >= 3.1.11)
160
+
161
+ ### Removed
162
+
163
+ - The `commonmarker-merge` and `kramdown-merge` runtime dependencies, and their
164
+ entries in the local sibling and `templating_local.gemfile` path lists.
165
+ kettle-jem merges Markdown with markly (via `ast-crispr-markdown-markly` and
166
+ `markly-merge`); other Markdown backends are still used when a project installs
167
+ them, but are no longer forced on every bundle. This also stops `commonmarker`'s
168
+ native extension from being compiled, which fails on ruby-head.
169
+
170
+ ### Fixed
171
+
172
+ - The packaged `Gemfile` no longer overrides `git_source(:github)` with an SSH
173
+ URL. The override was recorded in `Gemfile.lock` as `git@github.com` remotes,
174
+ which CI runners cannot clone; Bundler's built-in HTTPS source is used again.
175
+ Templating now also removes existing overrides of Bundler's built-in git
176
+ sources (`git_source(:github)`, `:gist`, `:bitbucket`) from destination
177
+ Gemfiles, including the managed monorepo root Gemfile.
178
+
179
+ - Generated Ruby 4 extracted-stdlib Gemfiles now include `ostruct` so suites
180
+ that require it can run on Rubies where it is no longer bundled by default.
181
+ - Regenerate `sig/*.rbs` signature stubs from the template instead of merging them, preventing a duplicate top-level declaration when the detected namespace kind (module vs. class) changes between runs.
182
+ - Gemspec `spec.files` merging now preserves a destination-only `enumerate_package_files.call("...")` splat for any project-specific directory, matching how `enumerate_package_glob.call(...)` splats were already preserved regardless of the glob pattern, instead of raising an unsupported-assignment error.
183
+
184
+ - Generated current-MRI workflows now use setup-ruby-flash v2.7, which validates rv-installed bundles before Appraisal and repairs incomplete native-extension installs through Bundler.
185
+
186
+ - Local template runs retain the released kettle-jem floor in remote templating Gemfiles.
187
+
188
+ - Run duplicate drift detection after all template orchestration mutations, and generate shared nomono resolver options only once in local Gemfiles.
189
+
190
+ - Template the bin/turbo_tests2 launcher required by Windows current-workflow tests.
191
+
192
+ - Refresh locked StructuredMerge sibling gems during local template preparation so Bundler cannot retain stale registry versions.
193
+
194
+ - Local template runs now preserve the released kettle-jem floor in gemspecs as well as remote Gemfiles.
195
+
196
+ - Local Gemfile bootstraps now load nomono's Bundler DSL from the path source recorded in Gemfile.lock, avoiding activation of an older installed release during local templating.
197
+
198
+ - Root and Kettle Changelog Gemfile bootstraps now load Nomono's Bundler DSL from a path-locked checkout instead of activating an installed Nomono release.
199
+
200
+ - Nomono bootstrap normalization is now idempotent and removes duplicate generated loader blocks after repeated template preparation.
201
+
202
+ - Kettle Changelog local Gemfile bootstraps now preserve their conditional block indentation when loading Nomono from a path-locked checkout.
203
+
204
+ - Kettle Changelog's modular bootstrap now reads the project-root Gemfile.lock, so path-locked Nomono is not mistaken for an installed release.
205
+
206
+ - Prevent commit hooks from resolving repository-local Bundler binstubs when checking commit messages.
207
+
208
+ - Keep local template dependencies out of generated Appraisal bundles.
209
+
210
+ - Use Kettle Dev's shared path identity logic when reconciling template and project paths.
211
+
212
+ - Update the generated appraisal style-toolchain gate to Ruby 3.3 so Ruby 3.2 builds do not resolve unsupported lint dependencies.
213
+
214
+ - Normalize local nomono bootstrap blocks as a single AST-bounded region, including Gemfiles whose local_gems assignment uses an array literal; keep appraisal style-gate comments attached to their Ruby-version condition.
215
+
216
+ - Preserve destination Markdown link definitions referenced by README sections retained during templating.
217
+
218
+ - Include files created by template orchestration steps in change accounting and maintenance changelog entries.
219
+
220
+ - Local style Gemfiles now include the configured rubocop-packaging plugin, matching the generated RuboCop configuration.
221
+
222
+ - Generated Gemfiles now include extracted standard-library dependencies on Windows, and only include rubocop-on-rbs on Ruby 3.3 and newer.
223
+
224
+ - Generated GitHub Actions workflows now pin setup-ruby-flash v2.13 so Windows native gems receive the MSYS2 dependency prefixes required by Psych and OpenSSL.
225
+
226
+ - Generated GitHub Actions workflows now pin setup-ruby-flash v2.14, which passes Windows-native MSYS2 prefixes and installs the CA trust bundle for OpenSSL.
227
+
228
+ - Avoid duplicate rubocop-packaging declarations when local style Gemfiles are evaluated.
229
+
230
+ - The StructuredMerge family link in generated READMEs now points to the consolidated StructuredMerge repository.
231
+
232
+ - Hold template style.gemfile rubocop-minitest floor at 0.40: version 0.41.0 requires rubocop >= 1.89.0 while standard (~> 1.56) caps rubocop at ~> 1.88.0, making the generated style bundle unsatisfiable and breaking family re-templating (kettle-family template --execute).
233
+
234
+ - Include modular dependency conflict decisions in template checksums so corrected Gemfile policies are reapplied.
235
+
236
+ - [kc] deps-floor-changelog-carry-forward: kettle-jem-deps-floor dropped changelog records for dependency floors raised earlier in the same Unreleased cycle that the current run did not touch, because its consolidated [kc] entry was rebuilt from only the current run's changes while the keyed upserter replaces the prior entry wholesale. Existing ranges are now parsed and carried forward, so each floor keeps its earliest baseline and its newest target.
237
+
238
+ - [kc] workflow-pins-changelog-carry-forward: kettle-jem-workflow-pins dropped changelog records for GitHub Actions re-pinned earlier in the same Unreleased cycle that the current run did not touch, for the same reason as kettle-jem-deps-floor. Existing pin ranges are now parsed and carried forward, keeping each action's earliest baseline ref and newest target ref.
239
+
240
+ - [kc] git-add-path-escaping-symlinked-home: kettle-jem-deps-floor and kettle-jem-workflow-pins could pass a parent-directory escaping path to git add -- when the project root and the git worktree root were two valid spellings of the same directory, as on hosts where /home is a symlink to /var/home and TMPDIR reports the symlink spelling while git rev-parse --show-toplevel reports the realpath spelling. Both tools now relativize through Kettle::Jem::RelativePaths, which canonicalizes both sides before comparing and refuses to return an escaping path.
241
+
242
+ - Always load the StructuredMerge sibling closure when `STRUCTUREDMERGE_DEV` is set, not only when `K_JEM_TEMPLATING` is set. StructuredMerge is no longer a templating-only concern: `kettle-changelog` reaches it at runtime through `ast-crispr-markdown-markly` -> `markdown-merge` -> `tree_haver`, so the test, lint, and release bundles resolve the family too. While the family is only partially released, the registry copies pin the last published version with an exact requirement, which cannot be satisfied next to a local lockstep version, and version solving failed for every command in the member bundle rather than only for lint. The generated main `Gemfile` and shim `Gemfile` now evaluate `gemfiles/modular/templating.gemfile` when either flag is set; neither flag set still skips it, so CI and released-mode resolution are unchanged.
243
+
244
+ - The templated `gemfiles/modular/style_local.gemfile` passed `root: ["src", "rubocop-lts"]` to `eval_nomono_gems`, which nomono resolves through `join_home` to `$HOME/src/rubocop-lts`. The rubocop-lts family actually lives at `$HOME/src/my/rubocop-lts`, so `RUBOCOP_LTS_DEV=true` silently selected a stale sibling checkout when one existed and a missing tree otherwise. Now `["src", "my", "rubocop-lts"]`, matching the other sibling templates and nomono's own default root. Already-templated projects keep the stale root until re-templated.
245
+
246
+ - kettle-jem: the packaged current.yml Windows test step hardcoded `bin/turbo_tests2`, correct for exactly one destination (and inconsistent even there, since its exec_cmd is kettle-test). The generated step now invokes the destination's own configured test command, resolved to its binstub from ci.exec_cmd. Also: the kettle-dev family local-gems wiring now includes kettle-rb, so destinations can develop against unreleased kettle-rb with KETTLE_DEV_DEV.
247
+
33
248
  ## [7.1.28] - 2026-09-10
34
249
 
35
250
  - TAG: [v7.1.28][7.1.28t]
@@ -2049,7 +2264,11 @@ Please file a bug if you notice a violation of semantic versioning.
2049
2264
  - Released kettle-jem as part of the initial StructuredMerge Ruby 7.0.0 gem set.
2050
2265
  - Included packaged templates and parser-backed merge support for Ruby gem templating.
2051
2266
 
2052
- [Unreleased]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.28...HEAD
2267
+ [Unreleased]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.30...HEAD
2268
+ [7.1.30]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.29...v7.1.30
2269
+ [7.1.30t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.30
2270
+ [7.1.29]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.28...v7.1.29
2271
+ [7.1.29t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.29
2053
2272
  [7.1.28]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.27...v7.1.28
2054
2273
  [7.1.28t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.28
2055
2274
  [7.1.27]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.26...v7.1.27
data/README.md CHANGED
@@ -95,48 +95,9 @@ Merge analysis must enter parsing through `tree_haver`. Parser-specific gems reg
95
95
  [sm-family-fixtures]: https://github.com/structuredmerge/structuredmerge-fixtures
96
96
  [sm-family-go]: https://github.com/structuredmerge/structuredmerge-go
97
97
  [sm-family-ruby]: https://github.com/structuredmerge/structuredmerge-ruby
98
- [sm-family-rust]: https://github.com/structuredmerge/structuredmerge-rust
98
+ [sm-family-rust]: https://github.com/structuredmerge/structuredmerge
99
99
  [sm-family-typescript]: https://github.com/structuredmerge/structuredmerge-typescript
100
100
 
101
- ### Federated DVCS
102
-
103
- <details markdown="1">
104
- <summary>Find this repo on federated forges (Coming soon!)</summary>
105
-
106
- | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI |
107
- |-------------------------------------------------|-------------------------------------------|-------------------|------------------|-----------------|----------------|
108
- | 🧪 [structuredmerge/structuredmerge-ruby on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix |
109
- | 🧊 [structuredmerge/structuredmerge-ruby on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix |
110
- | 🐙 [structuredmerge/structuredmerge-ruby on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix |
111
-
112
- </details>
113
-
114
- ### Enterprise Support
115
-
116
- [![Tidelift](https://tidelift.com/badges/package/rubygems/kettle-jem)](https://tidelift.com/subscription/pkg/rubygems-kettle-jem?utm_source=rubygems-kettle-jem&utm_medium=referral&utm_campaign=readme)
117
-
118
- Available as part of the Tidelift Subscription.
119
-
120
- <details markdown="1">
121
- <summary>Need enterprise-level guarantees?</summary>
122
-
123
- The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
124
-
125
- [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
126
-
127
- - 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
128
- - 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
129
- - 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
130
-
131
- Alternatively:
132
-
133
- - [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
134
- - [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
135
- - [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork]
136
- - [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]
137
-
138
- </details>
139
-
140
101
  ## ✨ Installation
141
102
 
142
103
  Install the gem and add to the application's Gemfile by executing:
@@ -696,30 +657,6 @@ We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you m
696
657
 
697
658
  See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
698
659
 
699
- ### 🚀 Release Instructions
700
-
701
- See [CONTRIBUTING.md][🤝contributing].
702
-
703
- ### Code Coverage
704
-
705
- <details markdown="1">
706
- <summary>Coverage service badges</summary>
707
-
708
- </details>
709
-
710
- ### 🪇 Code of Conduct
711
-
712
- Everyone interacting with this project's codebases, issue trackers,
713
- chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
714
-
715
- ## 🌈 Contributors
716
-
717
- [![Contributors][🖐contributors-img]][🖐contributors]
718
-
719
- Made with [contributors-img][🖐contrib-rocks].
720
-
721
- Also see GitLab Contributors: [https://gitlab.com/structuredmerge/structuredmerge-ruby/-/graphs/main][🚎contributors-gl]
722
-
723
660
  ## 📌 Versioning
724
661
 
725
662
  This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
@@ -753,41 +690,6 @@ See [LICENSE.md][📄license] for details.
753
690
 
754
691
  If none of the available licenses suit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
755
692
 
756
- ### © Copyright
757
-
758
- See [LICENSE.md][📄license] for the official copyright notice.
759
-
760
- <details markdown="1">
761
- <summary>Copyright holders</summary>
762
-
763
- - Required Notice: Copyright (c) 2026 Peter H. Boling
764
-
765
- </details>
766
-
767
- ## 🤑 A request for help
768
-
769
- Maintainers have teeth and need to pay their dentists.
770
- After getting laid off in an RIF in March, and encountering difficulty finding a new one,
771
- I began spending most of my time building open source tools.
772
- I'm hoping to be able to pay for my kids' health insurance this month,
773
- so if you value the work I am doing, I need your support.
774
- Please consider sponsoring me or the project.
775
-
776
- To join the community or get help, use the RubyForum or Discord.
777
-
778
- [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
779
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
780
-
781
- To say "thanks!" ☝️ Join the community or 👇️ send money.
782
-
783
- [![Sponsor kettle-dev/kettle-jem on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
784
-
785
- ### Please give the project a star ⭐ ♥.
786
-
787
- Many parts of this project are actively managed by a [kettle-jem](https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem) smart template utilizing [StructuredMerge.org](https://structuredmerge.org) merge contracts.
788
-
789
- Thanks for RTFM. ☺️
790
-
791
693
  [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
792
694
  [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
793
695
  [⛳liberapay]: https://liberapay.com/pboling/donate
@@ -822,7 +724,6 @@ Thanks for RTFM. ☺️
822
724
  [✉️ruby-forum-top-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=flat&logo=discourse&label=Ruby%20Users%20Forum
823
725
  [✉️ruby-forum-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=for-the-badge&logo=discourse&label=Ruby%20Users%20Forum
824
726
  [✉️ruby-forum]: https://www.rubyforum.org/tag/structuredmerge
825
-
826
727
  [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
827
728
  [⛳️gem-namespace]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem
828
729
  [⛳️namespace-img]: https://img.shields.io/badge/namespace-Kettle::Jem-3C2D2D.svg?style=square&logo=ruby&logoColor=white
@@ -893,7 +794,6 @@ Thanks for RTFM. ☺️
893
794
  [🖐contrib-rocks]: https://contrib.rocks
894
795
  [🖐contributors]: https://github.com/structuredmerge/structuredmerge-ruby/graphs/contributors
895
796
  [🖐contributors-img]: https://contrib.rocks/image?repo=structuredmerge/structuredmerge-ruby
896
- [🚎contributors-gl]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/graphs/main
897
797
  [🪇conduct]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CODE_OF_CONDUCT.md
898
798
  [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
899
799
  [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
@@ -907,7 +807,7 @@ Thanks for RTFM. ☺️
907
807
  [📌gitmoji]: https://gitmoji.dev
908
808
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
909
809
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
910
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-12.030-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
810
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-12.149-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
911
811
  [🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
912
812
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
913
813
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -916,7 +816,6 @@ Thanks for RTFM. ☺️
916
816
  [📄license-img]: https://img.shields.io/badge/License-AGPL--3.0--only_OR_PolyForm--Small--Business--1.0.0-259D6C.svg
917
817
  [📄license-compat]: https://www.apache.org/legal/resolved.html#category-x
918
818
  [📄license-compat-img]: https://img.shields.io/badge/Apache_Incompatible:_Category_X-%E2%9C%97-C0392B.svg?style=flat&logo=Apache
919
-
920
819
  [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
921
820
  [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
922
821
  [🚎yard-current]: http://rubydoc.info/gems/kettle-jem
@@ -928,14 +827,3 @@ Thanks for RTFM. ☺️
928
827
  [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
929
828
  [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
930
829
  [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
931
-
932
- <!-- kettle-jem:metadata:start -->
933
- | Field | Value |
934
- |---|---|
935
- | Package | kettle-jem |
936
- | Description | 🔮 Kettle::Jem provides gem scaffolding, templating, and setup automation using the *-merge gem family for AST-based file merging and token-resolver for template token resolution. Includes MergerConfig presets, YAML recipes, and a complete gem template scaffold. |
937
- | Homepage | https://github.com/structuredmerge/structuredmerge-ruby |
938
- | Source | https://github.com/structuredmerge/structuredmerge-ruby |
939
- | License | `AGPL-3.0-only` OR `PolyForm-Small-Business-1.0.0` |
940
- | Funding | https://github.com/sponsors/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/kettle-dev, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/kettle-jem, https://www.buymeacoffee.com/pboling |
941
- <!-- kettle-jem:metadata:end -->
@@ -20,7 +20,7 @@ module Kettle
20
20
  path.start_with?("gemfiles/")
21
21
  },
22
22
  documentation: ->(path) { %w[README.md CONTRIBUTING.md SECURITY.md].include?(path) || path.start_with?("docs/") },
23
- configuration: ->(path) { path.start_with?(".structuredmerge/") || path == "mise.toml" || path == ".gitignore" },
23
+ configuration: ->(path) { path.start_with?(".structuredmerge/") || %w[.gitattributes .gitignore mise.toml].include?(path) },
24
24
  code_and_tests: ->(path) { path.start_with?("lib/", "spec/", "test/", "bin/") || path == "Rakefile" }
25
25
  }.freeze
26
26
  TEMPLATE_CHANGELOG_KEYS = {
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Kettle
6
+ module Jem
7
+ # Relativizes a path under a root directory using filesystem identity rather
8
+ # than lexical string comparison.
9
+ #
10
+ # Pathname#relative_path_from is purely lexical, so when the path and the root
11
+ # are two valid spellings of the same directory it walks up past the root and
12
+ # returns a parent-directory escape instead of raising. On hosts where /home
13
+ # is a symlink to /var/home (Fedora Silverblue/Aurora), Dir.mktmpdir and
14
+ # TMPDIR report "/home/..." while `git rev-parse --show-toplevel` reports
15
+ # "/var/home/...". The escape then reaches `git add --`, which fails with
16
+ # "is outside repository at".
17
+ #
18
+ # Both sides are canonicalized before relativizing. File.realpath is safe
19
+ # here, unlike in Kettle::Paths where the result must preserve the caller's
20
+ # spelling because callers stat those paths and spawn processes from that
21
+ # working directory: these values feed only `git add`, and git itself reports
22
+ # the realpath spelling, so the canonical form is the one git expects.
23
+ module RelativePaths
24
+ class << self
25
+ # Returns the path of +path+ relative to +root+, or nil when root is not
26
+ # an ancestor. Never returns an escaping "../../.." path.
27
+ #
28
+ # Falls back to lexical comparison when either side does not exist yet,
29
+ # because generated and planned paths have no filesystem identity.
30
+ #
31
+ # @param path [String, Pathname, nil]
32
+ # @param root [String, Pathname, nil]
33
+ # @return [String, nil]
34
+ def relative_path_under(path, root)
35
+ return nil if blank?(path) || blank?(root)
36
+
37
+ absolute = File.expand_path(path.to_s)
38
+ boundary = File.expand_path(root.to_s)
39
+ relative = relativize(absolute, boundary)
40
+ relative.start_with?("..") ? nil : relative
41
+ rescue ArgumentError
42
+ nil
43
+ end
44
+
45
+ private
46
+
47
+ def relativize(absolute, boundary)
48
+ if File.exist?(absolute) && File.exist?(boundary)
49
+ Pathname(File.realpath(absolute)).relative_path_from(Pathname(File.realpath(boundary))).to_s
50
+ else
51
+ Pathname(absolute).relative_path_from(Pathname(boundary)).to_s
52
+ end
53
+ end
54
+
55
+ def blank?(value)
56
+ value.nil? || value.to_s.strip.empty?
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -28,6 +28,7 @@ module Kettle
28
28
  def run(project_root: Dir.pwd, env: ENV, run_options: {}, command_runner: method(:run_system_command))
29
29
  effective_run_options = install_run_options(env, run_options)
30
30
  config_migration_step = kettle_config_migration_step(project_root)
31
+ effective_run_options = effective_run_options.merge(defer_drift_check: true)
31
32
  report = Kettle::Jem.apply_project(project_root, env: env, run_options: effective_run_options)
32
33
  report = followup_apply_after_config_bootstrap(project_root, env: env, run_options: effective_run_options, report: report)
33
34
  install_steps = []
@@ -68,6 +69,11 @@ module Kettle
68
69
  report: final_report,
69
70
  run_options: effective_run_options
70
71
  )
72
+ final_report = Kettle::Jem.finalize_duplicate_drift(
73
+ project_root: project_root,
74
+ report: final_report,
75
+ run_options: effective_run_options
76
+ )
71
77
  bootstrap_commit = bootstrap_commit_step(project_root, run_options: effective_run_options)
72
78
  install_steps.concat(
73
79
  execute_orchestration_steps(
@@ -954,9 +960,7 @@ module Kettle
954
960
  end
955
961
 
956
962
  def same_path?(left, right)
957
- File.realpath(left.to_s) == File.realpath(right.to_s)
958
- rescue Errno::ENOENT
959
- File.expand_path(left.to_s) == File.expand_path(right.to_s)
963
+ Kettle::Dev::Paths.same?(left.to_s, right.to_s)
960
964
  end
961
965
 
962
966
  def rubocop_lts_local_root(env)
@@ -1025,7 +1029,7 @@ module Kettle
1025
1029
  destination_bin = File.join(project_root.to_s, "bin")
1026
1030
  destination_binstubs = binstub_files(destination_bin)
1027
1031
  parent_root = git_toplevel(project_root)
1028
- parent_binstubs = if parent_root && File.expand_path(parent_root) != File.expand_path(project_root.to_s)
1032
+ parent_binstubs = if parent_root && !same_path?(parent_root, project_root)
1029
1033
  binstub_files(File.join(parent_root, "bin"))
1030
1034
  else
1031
1035
  []
@@ -1788,7 +1792,7 @@ module Kettle
1788
1792
 
1789
1793
  bundle_gemfile = (env || {})["BUNDLE_GEMFILE"].to_s.strip
1790
1794
  project_gemfile = File.expand_path(File.join(project_root.to_s, "Gemfile"))
1791
- if !bundle_gemfile.empty? && File.expand_path(bundle_gemfile) == project_gemfile
1795
+ if !bundle_gemfile.empty? && same_path?(bundle_gemfile, project_gemfile)
1792
1796
  return {
1793
1797
  name: "bundled_handoff",
1794
1798
  status: "already_bundled",
@@ -28,7 +28,8 @@ module Kettle
28
28
  effective_run_options = Kettle::Jem::Tasks::TemplateTask.env_run_options(env || {}).merge(run_options || {})
29
29
  prepare_run_options = effective_run_options.merge(
30
30
  only: PREPARE_ONLY_PATHS,
31
- skip_lock_normalization: true
31
+ skip_lock_normalization: true,
32
+ defer_drift_check: true
32
33
  )
33
34
  events = Kettle::Jem.event_stream_from_options(effective_run_options)
34
35
  report = Kettle::Jem.apply_project(project_root, env: env, run_options: prepare_run_options)
@@ -37,7 +38,7 @@ module Kettle
37
38
  Kettle::Jem.apply_project(project_root, env: env, run_options: prepare_run_options)
38
39
  end
39
40
  report = merge_supplemental_prepare_report(report, supplemental_report)
40
- transition_step = reconcile_template_managed_dependencies_step(project_root, events: events)
41
+ transition_step = reconcile_template_managed_dependencies_step(project_root, env: env, events: events)
41
42
  nomono_bootstrap_step = normalize_existing_local_gemfile_bootstraps_step(project_root, events: events)
42
43
  setup_env = Kettle::Jem::Tasks::InstallTask.setup_command_env(project_root, env)
43
44
  setup_env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
@@ -51,7 +52,7 @@ module Kettle
51
52
  )
52
53
  Kettle::Jem.emit_step_event(events, "command_step", reset_step, phase: "prepare")
53
54
  bootstrap_name = templating_bootstrap_step_name(project_root)
54
- bootstrap_command = templating_bootstrap_command(project_root)
55
+ bootstrap_command = templating_bootstrap_command(project_root, env: setup_env)
55
56
  Kettle::Jem.emit_step_event(
56
57
  events,
57
58
  "command_step",
@@ -98,6 +99,11 @@ module Kettle
98
99
  "updated critical templating gems, and ran bundle install."
99
100
  }]
100
101
  )
102
+ final_report = Kettle::Jem.finalize_duplicate_drift(
103
+ project_root: project_root,
104
+ report: final_report,
105
+ run_options: effective_run_options
106
+ )
101
107
  Kettle::Jem.emit_summary_event(events, final_report)
102
108
  final_report
103
109
  end
@@ -139,7 +145,7 @@ module Kettle
139
145
  step
140
146
  end
141
147
 
142
- def reconcile_template_managed_dependencies_step(project_root, events:)
148
+ def reconcile_template_managed_dependencies_step(project_root, events:, env: ENV)
143
149
  paths = [
144
150
  File.join(project_root.to_s, "Gemfile"),
145
151
  *Dir.glob(File.join(project_root.to_s, "*.gemfile")),
@@ -148,11 +154,12 @@ module Kettle
148
154
  ].select { |path| File.file?(path) }.sort
149
155
  changed_files = paths.filter_map do |path|
150
156
  before = File.read(path)
151
- after = Kettle::Jem.reconcile_template_managed_dependencies(before)
157
+ relative_path = Pathname.new(path).relative_path_from(Pathname.new(project_root.to_s)).to_s
158
+ after = Kettle::Jem.reconcile_template_managed_dependencies(before, env: env)
152
159
  next if after == before
153
160
 
154
161
  File.write(path, after)
155
- Pathname.new(path).relative_path_from(Pathname.new(project_root.to_s)).to_s
162
+ relative_path
156
163
  end
157
164
  step = {
158
165
  name: "reconcile_template_managed_dependencies",
@@ -217,14 +224,26 @@ module Kettle
217
224
  end
218
225
  end
219
226
 
220
- def bundle_update_templating_bootstrap_command(project_root = Dir.pwd)
221
- %w[bundle update] + managed_bootstrap_gems(project_root) + locked_templating_gems(project_root)
227
+ def bundle_update_templating_bootstrap_command(project_root = Dir.pwd, env: ENV)
228
+ # Bundler can retain registry resolutions for sibling gems even when
229
+ # the local path Gemfile is active; refresh the locked family graph.
230
+ local_structuredmerge_gems = if Kettle::Jem.local_structuredmerge_path_mode?(env)
231
+ Kettle::Jem::STRUCTUREDMERGE_GEMS & locked_gem_names(project_root)
232
+ else
233
+ []
234
+ end
235
+ (
236
+ %w[bundle update] +
237
+ managed_bootstrap_gems(project_root) +
238
+ locked_templating_gems(project_root) +
239
+ local_structuredmerge_gems
240
+ ).uniq
222
241
  end
223
242
 
224
- def templating_bootstrap_command(project_root = Dir.pwd)
243
+ def templating_bootstrap_command(project_root = Dir.pwd, env: ENV)
225
244
  return %w[bundle install] unless templating_bootstrap_lockfile_ready?(project_root)
226
245
 
227
- bundle_update_templating_bootstrap_command(project_root)
246
+ bundle_update_templating_bootstrap_command(project_root, env: env)
228
247
  end
229
248
 
230
249
  def templating_bootstrap_step_name(project_root = Dir.pwd)