kettle-jem 7.0.0 → 7.1.3

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 (202) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +1505 -0
  4. data/LICENSE.md +13 -0
  5. data/README.md +794 -0
  6. data/exe/kettle-jem +20 -0
  7. data/lib/kettle/jem/cli.rb +334 -0
  8. data/lib/kettle/jem/license_txt_migrator.rb +33 -0
  9. data/lib/kettle/jem/rakelib/install.rake +10 -0
  10. data/lib/kettle/jem/rakelib/prepare.rake +10 -0
  11. data/lib/kettle/jem/rakelib/selftest.rake +10 -0
  12. data/lib/kettle/jem/rakelib/template.rake +10 -0
  13. data/lib/kettle/jem/tasks/install_task.rb +2178 -0
  14. data/lib/kettle/jem/tasks/prepare_task.rb +274 -0
  15. data/lib/kettle/jem/tasks/self_test_task.rb +256 -0
  16. data/lib/kettle/jem/tasks/template_task.rb +124 -0
  17. data/lib/kettle/jem/tasks.rb +6 -0
  18. data/lib/kettle/jem/templates/.aiignore.example +19 -0
  19. data/lib/kettle/jem/templates/.config/mise/env.sh +26 -0
  20. data/lib/kettle/jem/templates/.devcontainer/apt-install/devcontainer-feature.json.example +9 -0
  21. data/lib/kettle/jem/templates/.devcontainer/apt-install/install.sh.example +19 -0
  22. data/lib/kettle/jem/templates/.devcontainer/devcontainer.json.example +27 -0
  23. data/lib/kettle/jem/templates/.devcontainer/scripts/setup-tree-sitter.sh.example +210 -0
  24. data/lib/kettle/jem/templates/.env.local.example +67 -0
  25. data/lib/kettle/jem/templates/.envrc.example +4 -0
  26. data/lib/kettle/jem/templates/.envrc.no-osc.example +4 -0
  27. data/lib/kettle/jem/templates/.gemrc.example +1 -0
  28. data/lib/kettle/jem/templates/.git-hooks/commit-msg.example +60 -0
  29. data/lib/kettle/jem/templates/.git-hooks/commit-subjects-goalie.txt.example +8 -0
  30. data/lib/kettle/jem/templates/.git-hooks/footer-template.erb.txt.example +16 -0
  31. data/lib/kettle/jem/templates/.git-hooks/prepare-commit-msg.example +9 -0
  32. data/lib/kettle/jem/templates/.github/.codecov.yml.example +14 -0
  33. data/lib/kettle/jem/templates/.github/FUNDING.yml.example +10 -0
  34. data/lib/kettle/jem/templates/.github/FUNDING.yml.no-osc.example +10 -0
  35. data/lib/kettle/jem/templates/.github/copilot_instructions.md.example +1 -0
  36. data/lib/kettle/jem/templates/.github/dependabot.yml.example +13 -0
  37. data/lib/kettle/jem/templates/.github/workflows/auto-assign.yml.example +21 -0
  38. data/lib/kettle/jem/templates/.github/workflows/coverage.yml.example +102 -0
  39. data/lib/kettle/jem/templates/.github/workflows/current.yml.example +86 -0
  40. data/lib/kettle/jem/templates/.github/workflows/dep-heads.yml.example +249 -0
  41. data/lib/kettle/jem/templates/.github/workflows/dependency-review.yml.example +22 -0
  42. data/lib/kettle/jem/templates/.github/workflows/discord-notifier.yml.example +39 -0
  43. data/lib/kettle/jem/templates/.github/workflows/framework-ci.yml.example +81 -0
  44. data/lib/kettle/jem/templates/.github/workflows/heads.yml.example +212 -0
  45. data/lib/kettle/jem/templates/.github/workflows/jruby-10.0.yml.example +91 -0
  46. data/lib/kettle/jem/templates/.github/workflows/jruby-9.1.yml.example +90 -0
  47. data/lib/kettle/jem/templates/.github/workflows/jruby-9.2.yml.example +95 -0
  48. data/lib/kettle/jem/templates/.github/workflows/jruby-9.3.yml.example +95 -0
  49. data/lib/kettle/jem/templates/.github/workflows/jruby-9.4.yml.example +95 -0
  50. data/lib/kettle/jem/templates/.github/workflows/jruby.yml.example +91 -0
  51. data/lib/kettle/jem/templates/.github/workflows/license-eye.yml.example +43 -0
  52. data/lib/kettle/jem/templates/.github/workflows/locked_deps.yml.example +89 -0
  53. data/lib/kettle/jem/templates/.github/workflows/opencollective.yml.example +44 -0
  54. data/lib/kettle/jem/templates/.github/workflows/ruby-2.3.yml.example +80 -0
  55. data/lib/kettle/jem/templates/.github/workflows/ruby-2.4.yml.example +84 -0
  56. data/lib/kettle/jem/templates/.github/workflows/ruby-2.5.yml.example +84 -0
  57. data/lib/kettle/jem/templates/.github/workflows/ruby-2.6.yml.example +84 -0
  58. data/lib/kettle/jem/templates/.github/workflows/ruby-2.7.yml.example +84 -0
  59. data/lib/kettle/jem/templates/.github/workflows/ruby-3.0.yml.example +85 -0
  60. data/lib/kettle/jem/templates/.github/workflows/ruby-3.1.yml.example +83 -0
  61. data/lib/kettle/jem/templates/.github/workflows/ruby-3.2.yml.example +80 -0
  62. data/lib/kettle/jem/templates/.github/workflows/ruby-3.3.yml.example +80 -0
  63. data/lib/kettle/jem/templates/.github/workflows/ruby-3.4.yml.example +80 -0
  64. data/lib/kettle/jem/templates/.github/workflows/style.yml.example +78 -0
  65. data/lib/kettle/jem/templates/.github/workflows/templating.yml.example +86 -0
  66. data/lib/kettle/jem/templates/.github/workflows/truffle.yml.example +91 -0
  67. data/lib/kettle/jem/templates/.github/workflows/truffleruby-22.3.yml.example +92 -0
  68. data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.0.yml.example +92 -0
  69. data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.1.yml.example +78 -0
  70. data/lib/kettle/jem/templates/.github/workflows/truffleruby-24.2.yml.example +91 -0
  71. data/lib/kettle/jem/templates/.github/workflows/truffleruby-25.0.yml.example +96 -0
  72. data/lib/kettle/jem/templates/.github/workflows/truffleruby-33.0.yml.example +91 -0
  73. data/lib/kettle/jem/templates/.github/workflows/unlocked_deps.yml.example +93 -0
  74. data/lib/kettle/jem/templates/.gitignore.example +53 -0
  75. data/lib/kettle/jem/templates/.gitlab-ci.yml.example +134 -0
  76. data/lib/kettle/jem/templates/.idea/.gitignore.example +44 -0
  77. data/lib/kettle/jem/templates/.licenserc.yaml.example +8 -0
  78. data/lib/kettle/jem/templates/.opencollective.yml.example +3 -0
  79. data/lib/kettle/jem/templates/.qlty/qlty.toml.example +79 -0
  80. data/lib/kettle/jem/templates/.rspec.example +10 -0
  81. data/lib/kettle/jem/templates/.rubocop.example +1 -0
  82. data/lib/kettle/jem/templates/.rubocop.yml.example +47 -0
  83. data/lib/kettle/jem/templates/.rubocop_rspec.yml.example +55 -0
  84. data/lib/kettle/jem/templates/.simplecov.example +18 -0
  85. data/lib/kettle/jem/templates/.structuredmerge/git-drivers.toml.example +86 -0
  86. data/lib/kettle/jem/templates/.structuredmerge/kettle-jem.yml.example +519 -0
  87. data/lib/kettle/jem/templates/.yard-lint.yml.example +54 -0
  88. data/lib/kettle/jem/templates/.yardignore.example +13 -0
  89. data/lib/kettle/jem/templates/.yardopts.example +14 -0
  90. data/lib/kettle/jem/templates/AGENTS.md.example +283 -0
  91. data/lib/kettle/jem/templates/AGPL-3.0-only.md.example +235 -0
  92. data/lib/kettle/jem/templates/Appraisal.root.gemfile.example +26 -0
  93. data/lib/kettle/jem/templates/Appraisals.example +150 -0
  94. data/lib/kettle/jem/templates/Big-Time-Public-License.md.example +99 -0
  95. data/lib/kettle/jem/templates/CHANGELOG.md.example +33 -0
  96. data/lib/kettle/jem/templates/CHANGELOG.transfer.md +109 -0
  97. data/lib/kettle/jem/templates/CITATION.cff.example +20 -0
  98. data/lib/kettle/jem/templates/CODE_OF_CONDUCT.md.example +134 -0
  99. data/lib/kettle/jem/templates/CONTRIBUTING.md.example +292 -0
  100. data/lib/kettle/jem/templates/FUNDING.md.example +74 -0
  101. data/lib/kettle/jem/templates/FUNDING.md.no-osc.example +63 -0
  102. data/lib/kettle/jem/templates/Gemfile.example +52 -0
  103. data/lib/kettle/jem/templates/IRP.md.example +34 -0
  104. data/lib/kettle/jem/templates/LICENSE.md.example +3 -0
  105. data/lib/kettle/jem/templates/MIT.md.example +21 -0
  106. data/lib/kettle/jem/templates/PolyForm-Noncommercial-1.0.0.md.example +131 -0
  107. data/lib/kettle/jem/templates/PolyForm-Small-Business-1.0.0.md.example +121 -0
  108. data/lib/kettle/jem/templates/README.md.example +544 -0
  109. data/lib/kettle/jem/templates/REEK +0 -0
  110. data/lib/kettle/jem/templates/RUBOCOP.md.example +78 -0
  111. data/lib/kettle/jem/templates/Rakefile.example +163 -0
  112. data/lib/kettle/jem/templates/SECURITY.md.example +21 -0
  113. data/lib/kettle/jem/templates/bin/setup.example +36 -0
  114. data/lib/kettle/jem/templates/certs/pboling.pem.example +27 -0
  115. data/lib/kettle/jem/templates/gem.gemspec.example +161 -0
  116. data/lib/kettle/jem/templates/gemfiles/modular/benchmark/r4/v0.5.gemfile.example +2 -0
  117. data/lib/kettle/jem/templates/gemfiles/modular/benchmark/vHEAD.gemfile.example +2 -0
  118. data/lib/kettle/jem/templates/gemfiles/modular/cgi/r4/v0.5.gemfile.example +1 -0
  119. data/lib/kettle/jem/templates/gemfiles/modular/cgi/vHEAD.gemfile.example +1 -0
  120. data/lib/kettle/jem/templates/gemfiles/modular/changelog.gemfile.example +6 -0
  121. data/lib/kettle/jem/templates/gemfiles/modular/coverage.gemfile.example +14 -0
  122. data/lib/kettle/jem/templates/gemfiles/modular/coverage_local.gemfile.example +34 -0
  123. data/lib/kettle/jem/templates/gemfiles/modular/debug.gemfile.example +22 -0
  124. data/lib/kettle/jem/templates/gemfiles/modular/documentation.gemfile.example +24 -0
  125. data/lib/kettle/jem/templates/gemfiles/modular/documentation_local.gemfile.example +20 -0
  126. data/lib/kettle/jem/templates/gemfiles/modular/erb/r2/v3.0.gemfile.example +1 -0
  127. data/lib/kettle/jem/templates/gemfiles/modular/erb/r2.3/default.gemfile.example +6 -0
  128. data/lib/kettle/jem/templates/gemfiles/modular/erb/r2.6/v2.2.gemfile.example +3 -0
  129. data/lib/kettle/jem/templates/gemfiles/modular/erb/r3/v5.0.gemfile.example +1 -0
  130. data/lib/kettle/jem/templates/gemfiles/modular/erb/r3.1/v4.0.gemfile.example +2 -0
  131. data/lib/kettle/jem/templates/gemfiles/modular/erb/r4/v5.0.gemfile.example +1 -0
  132. data/lib/kettle/jem/templates/gemfiles/modular/erb/vHEAD.gemfile.example +2 -0
  133. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_22_3.gemfile.example +6 -0
  134. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_0.gemfile.example +6 -0
  135. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_1.gemfile.example +6 -0
  136. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r2/v0.3.gemfile.example +2 -0
  137. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r2.4/v0.1.gemfile.example +3 -0
  138. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r3/v0.3.gemfile.example +2 -0
  139. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r4/v0.3.gemfile.example +2 -0
  140. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/vHEAD.gemfile.example +2 -0
  141. data/lib/kettle/jem/templates/gemfiles/modular/optional.gemfile.example +16 -0
  142. data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.3/recording.gemfile.example +16 -0
  143. data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.4/recording.gemfile.example +18 -0
  144. data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.5/recording.gemfile.example +16 -0
  145. data/lib/kettle/jem/templates/gemfiles/modular/recording/r3/recording.gemfile.example +16 -0
  146. data/lib/kettle/jem/templates/gemfiles/modular/recording/r4/recording.gemfile.example +16 -0
  147. data/lib/kettle/jem/templates/gemfiles/modular/recording/vHEAD.gemfile.example +2 -0
  148. data/lib/kettle/jem/templates/gemfiles/modular/rspec.gemfile.example +4 -0
  149. data/lib/kettle/jem/templates/gemfiles/modular/runtime_heads.gemfile.example +14 -0
  150. data/lib/kettle/jem/templates/gemfiles/modular/shunted.gemfile.example +21 -0
  151. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2/v3.0.gemfile.example +5 -0
  152. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile.example +4 -0
  153. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r3/v3.0.gemfile.example +5 -0
  154. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r4/v3.0.gemfile.example +5 -0
  155. data/lib/kettle/jem/templates/gemfiles/modular/stringio/vHEAD.gemfile.example +2 -0
  156. data/lib/kettle/jem/templates/gemfiles/modular/style.gemfile.example +33 -0
  157. data/lib/kettle/jem/templates/gemfiles/modular/style_local.gemfile.example +26 -0
  158. data/lib/kettle/jem/templates/gemfiles/modular/templating.gemfile.example +34 -0
  159. data/lib/kettle/jem/templates/gemfiles/modular/templating_local.gemfile.example +73 -0
  160. data/lib/kettle/jem/templates/gemfiles/modular/webrick/r4/v1.9.gemfile.example +1 -0
  161. data/lib/kettle/jem/templates/gemfiles/modular/webrick/vHEAD.gemfile.example +1 -0
  162. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2/libs.gemfile.example +3 -0
  163. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.3/libs.gemfile.example +3 -0
  164. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.4/libs.gemfile.example +3 -0
  165. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.6/libs.gemfile.example +3 -0
  166. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r3/libs.gemfile.example +3 -0
  167. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r3.1/libs.gemfile.example +5 -0
  168. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r4/libs.gemfile.example +6 -0
  169. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/vHEAD.gemfile.example +6 -0
  170. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs.gemfile.example +12 -0
  171. data/lib/kettle/jem/templates/lib/gem/version.rb.example +1 -0
  172. data/lib/kettle/jem/templates/lib/gem/version_gem.rb.example +1 -0
  173. data/lib/kettle/jem/templates/mise.toml.example +25 -0
  174. data/lib/kettle/jem/templates/mise.toml.no-osc.example +25 -0
  175. data/lib/kettle/jem/templates/parsers.toml.example +19 -0
  176. data/lib/kettle/jem/templates/readme/partials/basic_usage.md.example +28 -0
  177. data/lib/kettle/jem/templates/readme/partials/configuration.md.example +112 -0
  178. data/lib/kettle/jem/templates/readme/partials/synopsis.md.example +3 -0
  179. data/lib/kettle/jem/templates/shim/.github/workflows/current.yml.example +40 -0
  180. data/lib/kettle/jem/templates/shim/.rspec.example +5 -0
  181. data/lib/kettle/jem/templates/shim/.structuredmerge/kettle-jem.yml.example +28 -0
  182. data/lib/kettle/jem/templates/shim/CHANGELOG.md.example +9 -0
  183. data/lib/kettle/jem/templates/shim/Gemfile.example +14 -0
  184. data/lib/kettle/jem/templates/shim/README.md.example +25 -0
  185. data/lib/kettle/jem/templates/shim/Rakefile.example +8 -0
  186. data/lib/kettle/jem/templates/shim/gem.gemspec.example +45 -0
  187. data/lib/kettle/jem/templates/shim/lib/compat_require.rb.example +3 -0
  188. data/lib/kettle/jem/templates/shim/lib/entrypoint.rb.example +3 -0
  189. data/lib/kettle/jem/templates/shim/lib/version.rb.example +1 -0
  190. data/lib/kettle/jem/templates/shim/spec/shim_spec.rb.example +15 -0
  191. data/lib/kettle/jem/templates/shim/spec/spec_helper.rb.example +23 -0
  192. data/lib/kettle/jem/templates/sig/gem.rbs.example +1 -0
  193. data/lib/kettle/jem/templates/spec/README.md.example +28 -0
  194. data/lib/kettle/jem/templates/spec/spec_helper.rb.example +39 -0
  195. data/lib/kettle/jem/version.rb +5 -3
  196. data/lib/kettle/jem/version_gem.rb +8 -0
  197. data/lib/kettle/jem.rb +18540 -1517
  198. data/lib/kettle-jem.rb +2 -1
  199. data/sig/kettle/jem.rbs +8 -0
  200. data.tar.gz.sig +0 -0
  201. metadata +870 -32
  202. metadata.gz.sig +0 -0
data/README.md ADDED
@@ -0,0 +1,794 @@
1
+ <a href="https://github.com/structuredmerge"><img alt="structuredmerge Logo by GitHub" src="https://github.com/structuredmerge.png?size=192" width="12%" align="right"/></a> <a href="https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem"><img alt="kettle-jem Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/structuredmerge/structuredmerge-ruby/kettle-jem/avatar-128px.svg" width="12%" align="right"/></a>
2
+
3
+ # 🔮 Kettle::Jem
4
+
5
+ [![Version][👽versioni]][👽version] [![Ruby Users Forum][✉️ruby-forum-top-img]][✉️ruby-forum] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0][📄license-img]][📄license] [![Total downloads][👽dl-ranki]][👽dl-rank] [![CI Current][🚎11-c-wfi]][🚎11-c-wf]
6
+
7
+ `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know on Discord][✉️discord-invite] or [RubyForum][✉️ruby-forum], as I may have missed the notification.
8
+
9
+ ---
10
+
11
+ `if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
12
+
13
+ [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
14
+
15
+ <details markdown="1">
16
+ <summary>👣 How will this project approach the September 2025 hostile takeover of RubyGems? 🚑️</summary>
17
+
18
+ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo).
19
+
20
+ </details>
21
+
22
+ ## 🌻 Synopsis <a href="https://discord.gg/3qme4XHNKN"><img alt="Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px.svg" width="8%" align="right"/></a> <a href="https://ruby-toolbox.com"><img alt="ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5" src="https://logos.galtzo.com/assets/images/ruby-lang/avatar-128px.svg" width="8%" align="right"/></a>
23
+
24
+ Kettle::Jem is an AST-aware gem templating system that keeps hundreds of Ruby gems
25
+ in sync with a shared template while preserving each project's customizations.
26
+ Unlike line-based copy/merge tools, Kettle::Jem understands the *structure* of
27
+ every file it touches — Ruby via Prism, YAML via Psych, Markdown via Markly,
28
+ TOML via tree-sitter, and more — so template updates land precisely where they
29
+ belong, and project-specific additions are never clobbered.
30
+
31
+ Plugin authors can now use the dedicated [plugin authoring guide](KETTLE_JEM_PLUGINS.md)
32
+ to build `kettle-jem` extension gems against the supported plugin seam.
33
+
34
+ ### Key Features
35
+
36
+ - **AST-aware merging** — 10 format-specific merge engines (prism, psych, markly, toml, json, jsonc, json5, bash, dotenv, rbs, text)
37
+ - **Token substitution** — `{KJ|TOKEN}` patterns resolved from config, ENV, or auto-derived from gemspec
38
+ - **Freeze blocks** — protect any section from template overwrites with `# kettle-jem:freeze` / `# kettle-jem:unfreeze`
39
+ - **Per-file strategies** — `merge`, `accept_template`, `keep_destination`, or `raw_copy`
40
+ - **Multi-phase pipeline** — 11 ordered phases (service_actor-based) from config sync through duplicate checking
41
+ - **SHA-pinned GitHub Actions** — template `uses:` always wins, propagating immutable SHAs
42
+ - **Convergence in one pass** — a single `kettle-jem install` applies all changes; a second run produces zero diff
43
+ - **Selftest divergence check** — CI verifies that project drift stays within a configurable threshold
44
+
45
+ ## 💡 Info you can shake a stick at
46
+
47
+ | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
48
+ |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
49
+ | Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf]|
50
+ | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Get help from RubyForum][✉️ruby-forum-img]][✉️ruby-forum] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
51
+ | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on GitHub.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
52
+ | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
53
+ | Compliance | [![License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0][📄license-img]][📄license] [![Apache license compatibility: Category X][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
54
+ | Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
55
+ | Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] |
56
+ | `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
57
+
58
+ ### Compatibility
59
+
60
+ Compatible with MRI Ruby 4.0.0+, and JRuby.
61
+ CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
62
+ This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
63
+ may be higher than the gem's runtime compatibility floor when legacy Rubies are
64
+ not practical for the current toolchain.
65
+
66
+ <a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/kettle-dev/avatar-128px.svg" width="14%" align="right"/></a>
67
+
68
+ The _amazing_ test matrix is powered by the kettle-dev stack.
69
+
70
+ <details markdown="1">
71
+ <summary>How kettle-dev manages complexity in tests</summary>
72
+
73
+ | Gem | Source | Role | Total downloads |
74
+ |-----|--------|------|---------------------|
75
+ | [appraisal2](https://clickgems.clickhouse.com/dashboard/appraisal2) | [GitHub](https://github.com/appraisal-rb/appraisal2) | multi-dependency Appraisal matrix generation | [![Total downloads for appraisal2](https://img.shields.io/gem/dt/appraisal2.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/appraisal2) |
76
+ | [appraisal2-rubocop](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) | [GitHub](https://github.com/appraisal-rb/appraisal2-rubocop) | RuboCop Appraisal generator integration | [![Total downloads for appraisal2-rubocop](https://img.shields.io/gem/dt/appraisal2-rubocop.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) |
77
+ | [kettle-dev](https://clickgems.clickhouse.com/dashboard/kettle-dev) | [GitHub](https://github.com/kettle-dev/kettle-dev) | development, release, and CI workflow tooling | [![Total downloads for kettle-dev](https://img.shields.io/gem/dt/kettle-dev.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-dev) |
78
+ | [kettle-soup-cover](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) | [GitHub](https://github.com/kettle-dev/kettle-soup-cover) | SimpleCov coverage policy and reporting | [![Total downloads for kettle-soup-cover](https://img.shields.io/gem/dt/kettle-soup-cover.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) |
79
+ | [kettle-test](https://clickgems.clickhouse.com/dashboard/kettle-test) | [GitHub](https://github.com/kettle-dev/kettle-test) | standard test runner and coverage harness | [![Total downloads for kettle-test](https://img.shields.io/gem/dt/kettle-test.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-test) |
80
+ | [rubocop-lts](https://clickgems.clickhouse.com/dashboard/rubocop-lts) | [GitHub](https://github.com/rubocop-lts/rubocop-lts) | Ruby-version-aware linting | [![Total downloads for rubocop-lts](https://img.shields.io/gem/dt/rubocop-lts.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/rubocop-lts) |
81
+ | [turbo_tests2](https://clickgems.clickhouse.com/dashboard/turbo_tests2) | [GitHub](https://github.com/galtzo-floss/turbo_tests2) | parallel test execution | [![Total downloads for turbo_tests2](https://img.shields.io/gem/dt/turbo_tests2.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/turbo_tests2) |
82
+
83
+ </details>
84
+
85
+ <details markdown="1">
86
+ <summary>StructuredMerge package family</summary>
87
+
88
+ This gem is part of the StructuredMerge Ruby package family. The implementation inventory, layering model, and backend notes live in the [root package-family guide][sm-family-guide]. Shared behavior is defined by the [StructuredMerge fixtures][sm-family-fixtures] and implemented by the [Go][sm-family-go], [Ruby][sm-family-ruby], [Rust][sm-family-rust], and [TypeScript][sm-family-typescript] repositories.
89
+
90
+ Merge analysis must enter parsing through `tree_haver`. Parser-specific gems register concrete TreeHaver backends; substrate gems register grammar mappings and keep shared format or language merge behavior in one place. Missing backends fail closed instead of falling back to direct parser-library calls.
91
+
92
+ </details>
93
+
94
+ [sm-family-guide]: https://github.com/structuredmerge/structuredmerge-ruby#package-family
95
+ [sm-family-fixtures]: https://github.com/structuredmerge/structuredmerge-fixtures
96
+ [sm-family-go]: https://github.com/structuredmerge/structuredmerge-go
97
+ [sm-family-ruby]: https://github.com/structuredmerge/structuredmerge-ruby
98
+ [sm-family-rust]: https://github.com/structuredmerge/structuredmerge-rust
99
+ [sm-family-typescript]: https://github.com/structuredmerge/structuredmerge-typescript
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 [![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)
115
+
116
+ Available as part of the Tidelift Subscription.
117
+
118
+ <details markdown="1">
119
+ <summary>Need enterprise-level guarantees?</summary>
120
+
121
+ 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.
122
+
123
+ [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
124
+
125
+ - 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
126
+ - 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
127
+ - 💡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
128
+
129
+ Alternatively:
130
+
131
+ - [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
132
+ - [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
133
+ - [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork]
134
+ - [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]
135
+
136
+ </details>
137
+
138
+ ## ✨ Installation
139
+
140
+ Install the gem and add to the application's Gemfile by executing:
141
+
142
+ ```console
143
+ bundle add kettle-jem
144
+ ```
145
+
146
+ If bundler is not being used to manage dependencies, install the gem by executing:
147
+
148
+ ```console
149
+ gem install kettle-jem
150
+ ```
151
+
152
+ ## ⚙️ Configuration
153
+
154
+ Each gem that uses Kettle::Jem has a `.kettle-jem.yml` file at its root. This file controls
155
+ every aspect of how the template is applied.
156
+
157
+ ### Minimal Configuration
158
+
159
+ ```yaml
160
+ project_emoji: "🔮"
161
+ engines:
162
+ - ruby
163
+ licenses:
164
+ - MIT
165
+ tokens:
166
+ forge:
167
+ gh_user: "your-username"
168
+ author:
169
+ name: "Your Name"
170
+ email: "you@example.com"
171
+ ```
172
+
173
+ ### Full Configuration Reference
174
+
175
+ ```yaml
176
+ # REQUIRED — unique emoji used in badges and gemspec summary
177
+ project_emoji: "🔮" # ENV override: KJ_PROJECT_EMOJI
178
+
179
+ # Ruby engines to include in CI matrix (remove to skip)
180
+ engines:
181
+ - ruby
182
+ - jruby
183
+ - truffleruby
184
+
185
+ # The generated direct dependency-heads workflow derives its Bundler lockfile
186
+ # platform from `engines`. JRuby gets an isolated temporary lockfile with the
187
+ # `universal-java` platform; MRI and TruffleRuby use the checked-in lock
188
+ # context. This avoids making one shared lockfile satisfy incompatible engine
189
+ # platforms.
190
+
191
+ # SPDX license identifiers
192
+ licenses:
193
+ - MIT
194
+
195
+ # Logo layout in README header: org | project | org_and_project
196
+ readme:
197
+ top_logo_mode: org
198
+
199
+ # Bot accounts to exclude from contributor lists
200
+ machine_users:
201
+ - dependabot
202
+
203
+ # Maximum allowed divergence (%) for selftest CI check
204
+ min_divergence_threshold: 5 # ENV override: KJ_MIN_DIVERGENCE_THRESHOLD
205
+
206
+ # Default merge behavior applied to all files
207
+ defaults:
208
+ preference: "template" # template | destination
209
+ add_template_only_nodes: true # add nodes that only exist in template
210
+ freeze_token: "kettle-jem" # marker for frozen sections
211
+
212
+ # Token values for {KJ|TOKEN} substitution
213
+ tokens:
214
+ forge:
215
+ gh_user: "github-username" # ENV override: KJ_GH_USER
216
+ gl_user: "gitlab-username" # ENV override: KJ_GL_USER
217
+ cb_user: "codeberg-username" # ENV override: KJ_CB_USER
218
+ sh_user: "sourcehut-user" # ENV override: KJ_SH_USER
219
+ author:
220
+ name: "Full Name" # ENV override: KJ_AUTHOR_NAME
221
+ given_names: "Full" # ENV override: KJ_AUTHOR_GIVEN_NAMES
222
+ family_names: "Name" # ENV override: KJ_AUTHOR_FAMILY_NAMES
223
+ email: "you@example.com" # ENV override: KJ_AUTHOR_EMAIL
224
+ domain: "example.com" # ENV override: KJ_AUTHOR_DOMAIN
225
+ orcid: "0000-0000-0000-0000" # ENV override: KJ_AUTHOR_ORCID
226
+ funding:
227
+ kofi: "username" # ENV override: KJ_FUNDING_KOFI
228
+ paypal: "username" # ENV override: KJ_FUNDING_PAYPAL
229
+ buymeacoffee: "username" # ENV override: KJ_FUNDING_BUYMEACOFFEE
230
+ liberapay: "username" # ENV override: KJ_FUNDING_LIBERAPAY
231
+ social:
232
+ mastodon: "username" # ENV override: KJ_SOCIAL_MASTODON
233
+ bluesky: "user.bsky.social" # ENV override: KJ_SOCIAL_BLUESKY
234
+ linktree: "username" # ENV override: KJ_SOCIAL_LINKTREE
235
+ devto: "username" # ENV override: KJ_SOCIAL_DEVTO
236
+
237
+ # Glob-based overrides (first match wins)
238
+ patterns:
239
+ - path: "certs/**"
240
+ strategy: raw_copy
241
+
242
+ # Per-file overrides
243
+ files:
244
+ Rakefile:
245
+ strategy: merge
246
+ preference: destination # preserve local tasks
247
+ AGENTS.md:
248
+ strategy: accept_template # always use template version
249
+ ```
250
+
251
+ ### Framework Matrix vs. Appraisals
252
+
253
+ `workflows.preset: framework` and `workflows.framework_matrix` are meant for a
254
+ simple 2D matrix: **Ruby versions × one framework gem/version axis**. This is a
255
+ good fit when you want kettle-jem to generate framework-version modular gemfiles,
256
+ `Appraisals` entries, and CI matrix entries for a single framework dependency.
257
+
258
+ If you need a deeper or more complex matrix, prefer
259
+ **`kettle-jem-appraisals`**, which generates `Appraisals` entries and is the
260
+ better fit for Appraisals-style combinations.
261
+
262
+ ### Strategies
263
+
264
+ | Strategy | Behavior |
265
+ |--------------------|-----------------------------------------------------------------------|
266
+ | `merge` | Resolve tokens, then AST-merge template + destination (default) |
267
+ | `accept_template` | Resolve tokens, overwrite destination with template result |
268
+ | `keep_destination` | Skip entirely — no merge, no creation |
269
+ | `raw_copy` | Copy bytes as-is — no token resolution, no merge (for binary assets) |
270
+
271
+ `raw_copy` exists for bootstrap files that may be needed before the full
272
+ templating stack is available. Because it bypasses normal template processing,
273
+ do not use it for templates that contain `{KJ|...}` tokens or require
274
+ StructuredMerge normalization.
275
+
276
+ ### Token Substitution
277
+
278
+ Tokens use `{KJ|TOKEN}` syntax and are resolved in priority order:
279
+
280
+ 1. **ENV variables** (highest) — e.g., `KJ_AUTHOR_NAME`
281
+ 2. **`.kettle-jem.yml` `tokens:` section** — explicit values
282
+ 3. **Auto-derived from gemspec** (lowest) — author name, email, domain
283
+
284
+ Common tokens:
285
+
286
+ | Token | Source |
287
+ |------------------------|-----------------------------------|
288
+ | `{KJ\|GEM_NAME}` | Gem name from gemspec |
289
+ | `{KJ\|NAMESPACE}` | Ruby module namespace |
290
+ | `{KJ\|AUTHOR:NAME}` | Author full name |
291
+ | `{KJ\|AUTHOR:EMAIL}` | Author email |
292
+ | `{KJ\|GH:USER}` | GitHub username |
293
+ | `{KJ\|PROJECT_EMOJI}` | Project emoji from config |
294
+ | `{KJ\|MIN_RUBY}` | Minimum Ruby version |
295
+ | `{KJ\|FREEZE_TOKEN}` | Freeze marker name |
296
+
297
+ ### Freeze Blocks
298
+
299
+ Protect sections in any file from template overwrites:
300
+
301
+ ```ruby
302
+ # kettle-jem:freeze
303
+ gem "my-local-fork", path: "../custom"
304
+ # kettle-jem:unfreeze
305
+ ```
306
+
307
+ Content between freeze/unfreeze markers is always preserved from the destination,
308
+ regardless of what the template contains. Works in all supported formats (Ruby, YAML,
309
+ Markdown, TOML, JSON, Bash, etc.).
310
+
311
+ ### Merge Engine Selection
312
+
313
+ Kettle::Jem selects the merge engine by file type:
314
+
315
+ | File Pattern | Merge Engine | Key Behaviors |
316
+ |----------------------------------------------------------|---------------|--------------------------------------------|
317
+ | `*.rb`, `Gemfile`, `*.gemspec`, `Rakefile`, `Appraisals` | Prism::Merge | Three-phase matching, gemspec var renaming |
318
+ | `*.yml`, `*.yaml` | Psych::Merge | SHA-pinned `uses:`, per-key preferences |
319
+ | `*.md`, `*.markdown` | Markly::Merge | Heading/list matching, inner list merge |
320
+ | `*.toml` | Toml::Merge | Sort keys, table matching |
321
+ | `*.json` | Json::Merge | Key-based matching |
322
+ | `*.jsonc` | Json::Merge | With comment preservation |
323
+ | `*.json5` | Json::Merge | JSON5 keys, strings, comments, and trailing commas |
324
+ | `*.sh`, `*.bash`, `.envrc` | Bash::Merge | Block matching |
325
+ | `.env*` | Dotenv::Merge | KEY=value matching |
326
+ | `*.rbs` | RBS::Merge | Type signature matching |
327
+ | `.gitignore` | Text::Merge | Intentional line-based merge |
328
+
329
+ > **No silent fallback:** If a tree-sitter grammar is unavailable for a file
330
+ > type that requires AST merging, kettle-jem will **fail** (default) or
331
+ > **skip** the file — never silently degrade to text-based merging.
332
+ > See `PARSE_ERROR_MODE` below.
333
+
334
+ ## 🔧 Basic Usage
335
+
336
+ ### Initial Setup
337
+
338
+ ```bash
339
+ gem install kettle-jem
340
+ cd my-gem
341
+ kettle-jem
342
+ ```
343
+
344
+ The setup CLI runs a two-phase bootstrap:
345
+
346
+ 1. **Bootstrap** — creates `.kettle-jem.yml`, installs modular gemfiles, ensures dev dependencies
347
+ 2. **Bundled** — loads the full runtime and runs `kettle-jem install`
348
+
349
+ ### Applying Template Updates
350
+
351
+ After initial setup, re-run the template process to pull in updates:
352
+
353
+ ```bash
354
+ K_JEM_TEMPLATING=true bundle exec kettle-jem install
355
+ ```
356
+
357
+ This applies all template phases, then runs the local finishing steps such as
358
+ `bin/setup`, curated binstub generation, hooks, and lockfile normalization.
359
+
360
+ | Phase | Description | Files Affected |
361
+ |-------|--------------------------------------|---------------------------------------|
362
+ | 0 | Config sync | `.kettle-jem.yml` |
363
+ | 1 | Dev container | `.devcontainer/` |
364
+ | 2 | GitHub workflows | `.github/workflows/`, `FUNDING.yml` |
365
+ | 3 | Quality config | `.qlty/qlty.toml` |
366
+ | 4 | Modular gemfiles | `gemfiles/modular/` |
367
+ | 5 | Spec helper | `spec/spec_helper.rb` |
368
+ | 6 | Environment templates | `.env.local.example` |
369
+ | 7 | Remaining files | gemspec, README, LICENSE, Rakefile, … |
370
+ | 8 | Git hooks | `.git-hooks/` |
371
+ | 9 | License files | `LICENSE*` |
372
+ | 10 | Duplicate check | _(validation only)_ |
373
+
374
+ Each phase is implemented as a composable [service_actor](https://github.com/sunny/actor)
375
+ actor, enabling per-phase statistics (📄 templates, 🆕 created, 📋 pre-existing,
376
+ 🟰 identical, ✏️ changed) and future slice-based workflows.
377
+
378
+ ### Checking Divergence
379
+
380
+ CI can verify that a project hasn't drifted too far from the template:
381
+
382
+ ```bash
383
+ bundle exec rake kettle:jem:selftest
384
+ ```
385
+
386
+ This re-applies the template in a temporary checkout and measures the diff.
387
+ Output is condensed to two summary lines after the template run:
388
+
389
+ ```
390
+ [selftest] 📄 Report - tmp/template_test/report/summary.md
391
+ [selftest] ✅ Score: 100.0% · Divergence: 0.0% · Threshold: fail when divergence reaches 5.0%
392
+ ```
393
+
394
+ If divergence exceeds `min_divergence_threshold` (default 5%), the check fails.
395
+
396
+ ### Workflow-Specific Options
397
+
398
+ For GitHub Actions workflows, the template always wins for `uses:` lines
399
+ (SHA-pinned action references) while destination wins for job configuration:
400
+
401
+ ```yaml
402
+ # Template updates this SHA automatically:
403
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
404
+
405
+ # Your matrix customizations are preserved:
406
+ matrix:
407
+ ruby: ["3.2", "3.3", "3.4"]
408
+ ```
409
+
410
+ ### Per-File Overrides
411
+
412
+ Override merge behavior for specific files in `.kettle-jem.yml`:
413
+
414
+ ```yaml
415
+ files:
416
+ Rakefile:
417
+ strategy: merge
418
+ preference: destination # keep your custom tasks
419
+ certs/my.pem:
420
+ strategy: raw_copy # binary file, no merging
421
+ generated/report.md:
422
+ strategy: keep_destination # never touch this file
423
+ ```
424
+
425
+ ### Environment Variables & CLI Options
426
+
427
+ Kettle::Jem behavior is controlled via environment variables (which double as
428
+ Rake task arguments) and CLI flags passed to `kettle-jem setup`.
429
+
430
+ #### Merge & Error Handling
431
+
432
+ | Variable | CLI Flag | Default | Description |
433
+ |----------|----------|---------|-------------|
434
+ | `FAILURE_MODE` | `--failure-mode=VAL` | `error` | How general merge failures are handled. `error` raises and halts; `rescue` logs a warning and uses unmerged content. |
435
+ | `PARSE_ERROR_MODE` | — | `fail` | How AST parser unavailability is handled. `fail` raises immediately (recommended); `skip` warns and preserves the destination file unchanged. **There is no text-merge fallback** — AST merge or nothing. |
436
+
437
+ #### Task Control
438
+
439
+ | Variable | CLI Flag | Default | Description |
440
+ |----------|----------|---------|-------------|
441
+ | `allowed` | `--allowed=VAL` | `true` | Set to `false`/`0`/`no` to require manual review of env file changes before continuing. |
442
+ | — | `--interactive` | _(off)_ | Enable interactive prompts (opt-in). Overrides the default non-interactive behavior. |
443
+ | `KETTLE_JEM_VERBOSE` | `--verbose` | `false` | Show detailed output including per-file messages and setup progress. Overrides the default quiet behavior. |
444
+ | `only` | `--only=VAL` | _(all)_ | Comma-separated glob patterns — only template files matching at least one pattern are processed. |
445
+ | `include` | `--include=VAL` | _(all)_ | Comma-separated glob patterns — additional files to include beyond the default set. |
446
+ | `hook_templates` | `--hook_templates=VAL` | _(prompt)_ | Git hook install location: `l`/`local`, `g`/`global`, or `n`/`none`. Also via `KETTLE_DEV_HOOK_TEMPLATES`. |
447
+ | `KETTLE_JEM_CHECKSUMS` | `--checksums=VAL` | `template,ignore-dest` | Checksum skip mode. See "Checksum Skip Modes" below. |
448
+ | — | `--ignore-checksums` | _(off)_ | Alias for `--checksums=off`; disables checksum-based skipping. |
449
+
450
+ #### Checksum Skip Modes
451
+
452
+ Kettle::Jem writes managed template state to
453
+ `.structuredmerge/kettle-jem.lock`. The legacy whole-template checksum
454
+ inventory now lives under `template_state.checksums`.
455
+ That inventory is useful for coarse template drift state, but it is not used to
456
+ skip individual files. File skipping uses `files.*.input_fingerprint`, a
457
+ per-destination digest that includes the selected template source SHA, recipe
458
+ identity/version, resolved token digest, and kettle-jem implementation SHA.
459
+ Token values are hashed into the fingerprint and are not written to the lockfile.
460
+
461
+ `--checksums` accepts comma-separated modes:
462
+
463
+ | Mode | Behavior |
464
+ |------|----------|
465
+ | `template` | Skip a destination file only when its `input_fingerprint` matches the lock. This is the default template-input check. |
466
+ | `ignore-dest` | Do not compare the destination file checksum. This is the default destination behavior. |
467
+ | `dest` | Also require the destination file checksum to match the lock before skipping. `dest` implies `template` unless `ignore-template` is present. |
468
+ | `ignore-template` | Do not compare `input_fingerprint`; useful only with `dest` when local destination state should be the only skip gate. |
469
+ | `off` | Disable checksum-based skipping. This cannot be combined with other modes. |
470
+
471
+ Common combinations:
472
+
473
+ | Value | Result |
474
+ |-------|--------|
475
+ | `template,ignore-dest` | Default. Retemplate when relevant template input changes; ignore local destination edits. |
476
+ | `dest,template` | Retemplate when relevant template input changes or the destination differs from the prior templated result. |
477
+ | `dest,ignore-template` | Skip only when the destination checksum matches; ignore template input changes. |
478
+ | `off` | Run normal templating without checksum skip shortcuts. |
479
+
480
+ #### Config & Identity (KJ_ prefix)
481
+
482
+ These map directly to `.kettle-jem.yml` keys, seed freshly created configs,
483
+ fill missing keys during config sync, and act as runtime overrides.
484
+
485
+ | Variable | Description |
486
+ |----------|-------------|
487
+ | `KJ_PROJECT_EMOJI` | Project identifying emoji (e.g. `🪙`). Required in config. |
488
+ | `KJ_MIN_DIVERGENCE_THRESHOLD` | Selftest divergence threshold for `min_divergence_threshold`. |
489
+ | `KJ_AUTHOR_NAME` | Gem author full name |
490
+ | `KJ_AUTHOR_EMAIL` | Gem author email |
491
+ | `KJ_AUTHOR_DOMAIN` | Author website domain (derived from email if unset) |
492
+ | `KJ_AUTHOR_GIVEN_NAMES` | First/given names |
493
+ | `KJ_AUTHOR_FAMILY_NAMES` | Last/family names |
494
+ | `KJ_AUTHOR_ORCID` | ORCID identifier |
495
+ | `FORGE_ORG` | Preferred forge organization or owner used for scaffolded repository URLs when no forge remote is available |
496
+ | `KJ_GH_ORG` | GitHub-specific organization or owner fallback for scaffolded repository URLs |
497
+ | `KJ_GH_USER` | GitHub username used for maintainer profile and sponsor links |
498
+ | `KJ_GL_USER` | GitLab username |
499
+ | `KJ_CB_USER` | Codeberg username |
500
+ | `KJ_SH_USER` | SourceHut username |
501
+
502
+ #### Workspace & Funding
503
+
504
+ | Variable | Description |
505
+ |----------|-------------|
506
+ | `KETTLE_DEV_DEV` | Workspace root for local sibling gems. `true` = `~/src/my`; a path = that path; unset/`false` = released gems. |
507
+ | `KETTLE_DEV_DEBUG` | Set to `true` for verbose debug output. |
508
+ | `FUNDING_ORG` | OpenCollective organization handle for FUNDING.yml. Auto-derived from git remote if unset. |
509
+ | `OPENCOLLECTIVE_HANDLE` | Alternative to `FUNDING_ORG` for personal OpenCollective pages. |
510
+ | `KJ_FUNDING_KOFI` | Ko-fi handle for FUNDING.yml |
511
+ | `KJ_FUNDING_PAYPAL` | PayPal handle for FUNDING.yml |
512
+ | `KJ_FUNDING_BUYMEACOFFEE` | Buy Me a Coffee handle for funding links |
513
+ | `KJ_FUNDING_LIBERAPAY` | Liberapay handle for funding links |
514
+ | `KJ_SOCIAL_MASTODON` | Mastodon handle for social/profile links |
515
+ | `KJ_SOCIAL_BLUESKY` | Bluesky handle for social/profile links |
516
+ | `KJ_SOCIAL_LINKTREE` | Linktree handle for social/profile links |
517
+ | `KJ_SOCIAL_DEVTO` | DEV Community handle for social/profile links |
518
+
519
+ #### Templating Examples
520
+
521
+ ```bash
522
+ # Standard template update (quiet, non-interactive — the default)
523
+ K_JEM_TEMPLATING=true bundle exec kettle-jem install
524
+
525
+ # Verbose output
526
+ K_JEM_TEMPLATING=true KETTLE_JEM_VERBOSE=true bundle exec kettle-jem install
527
+
528
+ # Interactive mode (prompts before each change)
529
+ K_JEM_TEMPLATING=true bundle exec kettle-jem install --interactive
530
+
531
+ # Force re-evaluation of every selected template file
532
+ K_JEM_TEMPLATING=true bundle exec kettle-jem install --ignore-checksums
533
+
534
+ # Retemplate when either template inputs or destination checksums changed
535
+ K_JEM_TEMPLATING=true bundle exec kettle-jem install --checksums=dest,template
536
+
537
+ # Only workflow files, skip unparseable. Scoped template runs skip install
538
+ # finishing steps and are intended for surgical file updates.
539
+ K_JEM_TEMPLATING=true PARSE_ERROR_MODE=skip bundle exec kettle-jem template --only=".github/**"
540
+
541
+ # Rescue on merge failure (don't halt)
542
+ K_JEM_TEMPLATING=true FAILURE_MODE=rescue bundle exec kettle-jem install
543
+ ```
544
+
545
+ The `kettle:jem:*` rake tasks are internal targets used by the executable after
546
+ it prepares the templating environment; call `kettle-jem` directly for normal
547
+ templating work.
548
+
549
+ ## 🔐 Security
550
+
551
+ See [SECURITY.md][🔐security].
552
+
553
+ ## 🤝 Contributing
554
+
555
+ If you need some ideas of where to help, you could work on adding more code coverage,
556
+ or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
557
+ or use the gem and think about how it could be better.
558
+
559
+ We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
560
+
561
+ See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
562
+
563
+ ### 🚀 Release Instructions
564
+
565
+ See [CONTRIBUTING.md][🤝contributing].
566
+
567
+ ### Code Coverage
568
+
569
+ <details markdown="1">
570
+ <summary>Coverage service badges</summary>
571
+
572
+ </details>
573
+
574
+ ### 🪇 Code of Conduct
575
+
576
+ Everyone interacting with this project's codebases, issue trackers,
577
+ chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
578
+
579
+ ## 🌈 Contributors
580
+
581
+ [![Contributors][🖐contributors-img]][🖐contributors]
582
+
583
+ Made with [contributors-img][🖐contrib-rocks].
584
+
585
+ Also see GitLab Contributors: [https://gitlab.com/structuredmerge/structuredmerge-ruby/-/graphs/main][🚎contributors-gl]
586
+
587
+ ## 📌 Versioning
588
+
589
+ This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
590
+ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
591
+
592
+ For example:
593
+
594
+ ```ruby
595
+ spec.add_dependency("kettle-jem", "~> 7.0")
596
+ ```
597
+
598
+ <details markdown="1">
599
+ <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
600
+
601
+ Dropping support for a platform can be a breaking change for affected users.
602
+ If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
603
+
604
+ To get a better understanding of how SemVer is intended to work over a project's lifetime,
605
+ read this article from the creator of SemVer:
606
+
607
+ - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
608
+
609
+ </details>
610
+
611
+ See [CHANGELOG.md][📌changelog] for a list of releases.
612
+
613
+ ## 📄 License
614
+
615
+ The gem is available under the following licenses: [AGPL-3.0-only](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/AGPL-3.0-only.md), [PolyForm-Small-Business-1.0.0](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/PolyForm-Small-Business-1.0.0.md).
616
+ See [LICENSE.md][📄license] for details.
617
+
618
+ If none of the available licenses suit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
619
+
620
+ ### © Copyright
621
+
622
+ See [LICENSE.md][📄license] for the official copyright notice.
623
+
624
+ <details markdown="1">
625
+ <summary>Copyright holders</summary>
626
+
627
+ - Required Notice: Copyright (c) 2026 Peter H. Boling
628
+
629
+ </details>
630
+
631
+ ## 🤑 A request for help
632
+
633
+ Maintainers have teeth and need to pay their dentists.
634
+ After getting laid off in an RIF in March, and encountering difficulty finding a new one,
635
+ I began spending most of my time building open source tools.
636
+ I'm hoping to be able to pay for my kids' health insurance this month,
637
+ so if you value the work I am doing, I need your support.
638
+ Please consider sponsoring me or the project.
639
+
640
+ To join the community or get help, use the RubyForum or Discord.
641
+
642
+ [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
643
+ [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
644
+
645
+ To say "thanks!" ☝️ Join the community or 👇️ send money.
646
+
647
+ [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
648
+
649
+ ### Please give the project a star ⭐ ♥.
650
+
651
+ 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.
652
+
653
+ Thanks for RTFM. ☺️
654
+
655
+ [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
656
+ [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
657
+ [⛳liberapay]: https://liberapay.com/pboling/donate
658
+ [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
659
+ [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
660
+ [🖇sponsor]: https://github.com/sponsors/pboling
661
+ [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
662
+ [🖇kofi]: https://ko-fi.com/pboling
663
+ [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
664
+ [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
665
+ [🖇buyme]: https://www.buymeacoffee.com/pboling
666
+ [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
667
+ [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
668
+ [🖇paypal]: https://www.paypal.com/paypalme/peterboling
669
+ [🖇floss-funding.dev]: https://floss-funding.dev
670
+ [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
671
+ [✉️discord-invite]: https://discord.gg/3qme4XHNKN
672
+ [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
673
+ [✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
674
+ [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
675
+ [✉️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
676
+ [✉️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
677
+ [✉️ruby-forum]: https://www.rubyforum.org/tag/structuredmerge
678
+
679
+ [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
680
+ [⛳️gem-namespace]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem
681
+ [⛳️namespace-img]: https://img.shields.io/badge/namespace-Kettle::Jem-3C2D2D.svg?style=square&logo=ruby&logoColor=white
682
+ [⛳️gem-name]: https://clickgems.clickhouse.com/dashboard/kettle-jem
683
+ [⛳️name-img]: https://img.shields.io/badge/name-kettle--jem-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
684
+ [⛳️tag-img]: https://img.shields.io/github/tag/structuredmerge/structuredmerge-ruby.svg
685
+ [⛳️tag]: https://github.com/structuredmerge/structuredmerge-ruby/releases
686
+ [🚂maint-blog]: http://www.railsbling.com/tags/kettle-jem
687
+ [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
688
+ [🚂maint-contact]: http://www.railsbling.com/contact
689
+ [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
690
+ [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
691
+ [💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
692
+ [💖✌️wellfound]: https://wellfound.com/u/peter-boling
693
+ [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
694
+ [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
695
+ [💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
696
+ [💖🐘ruby-mast]: https://ruby.social/@galtzo
697
+ [💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo
698
+ [💖🦋bluesky]: https://bsky.app/profile/galtzo.com
699
+ [💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white
700
+ [💖🌳linktree]: https://linktr.ee/galtzo
701
+ [💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree
702
+ [💖💁🏼‍♂️devto]: https://dev.to/galtzo
703
+ [💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white
704
+ [💖💁🏼‍♂️aboutme]: https://about.me/peter.boling
705
+ [💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white
706
+ [💖🧊berg]: https://codeberg.org/pboling
707
+ [💖🐙hub]: https://github.org/pboling
708
+ [💖🛖hut]: https://sr.ht/~galtzo/
709
+ [💖🧪lab]: https://gitlab.com/pboling
710
+ [👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share
711
+ [👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
712
+ [👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
713
+ [👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
714
+ [🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-kettle-jem?utm_source=rubygems-kettle-jem&utm_medium=referral&utm_campaign=readme
715
+ [🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
716
+ [🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
717
+ [💁🏼‍♂️peterboling]: http://www.peterboling.com
718
+ [🚂railsbling]: http://www.railsbling.com
719
+ [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
720
+ [📜src-gl]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/gems/kettle-jem
721
+ [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
722
+ [📜src-cb]: https://codeberg.org/structuredmerge/structuredmerge-ruby/src/branch/main/gems/kettle-jem
723
+ [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=GitHub&logoColor=green
724
+ [📜src-gh]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem
725
+ [📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
726
+ [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
727
+ [📜gl-wiki]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/wikis/home
728
+ [📜gh-wiki]: https://github.com/structuredmerge/structuredmerge-ruby/wiki
729
+ [📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
730
+ [📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
731
+ [👽dl-rank]: https://clickgems.clickhouse.com/dashboard/kettle-jem
732
+ [👽dl-ranki]: https://img.shields.io/gem/dt/kettle-jem.svg
733
+ [👽version]: https://clickgems.clickhouse.com/dashboard/kettle-jem
734
+ [👽versioni]: https://img.shields.io/gem/v/kettle-jem.svg
735
+ [🚎11-c-wf]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml
736
+ [🚎11-c-wfi]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml/badge.svg
737
+ [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
738
+ [🤝gh-issues]: https://github.com/structuredmerge/structuredmerge-ruby/issues
739
+ [🤝gh-pulls]: https://github.com/structuredmerge/structuredmerge-ruby/pulls
740
+ [🤝gl-issues]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/issues
741
+ [🤝gl-pulls]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/merge_requests
742
+ [🤝cb-issues]: https://codeberg.org/structuredmerge/structuredmerge-ruby/issues
743
+ [🤝cb-pulls]: https://codeberg.org/structuredmerge/structuredmerge-ruby/pulls
744
+ [🤝cb-donate]: https://donate.codeberg.org/
745
+ [🤝contributing]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CONTRIBUTING.md
746
+ [🖐contrib-rocks]: https://contrib.rocks
747
+ [🖐contributors]: https://github.com/structuredmerge/structuredmerge-ruby/graphs/contributors
748
+ [🖐contributors-img]: https://contrib.rocks/image?repo=structuredmerge/structuredmerge-ruby
749
+ [🚎contributors-gl]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/graphs/main
750
+ [🪇conduct]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CODE_OF_CONDUCT.md
751
+ [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
752
+ [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
753
+ [📌semver]: https://semver.org/spec/v2.0.0.html
754
+ [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
755
+ [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
756
+ [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
757
+ [📌changelog]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CHANGELOG.md
758
+ [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
759
+ [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
760
+ [📌gitmoji]: https://gitmoji.dev
761
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
762
+ [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
763
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-11.445-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
764
+ [🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
765
+ [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
766
+ [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
767
+ [📄license]: LICENSE.md
768
+ [📄license-ref]: LICENSE.md
769
+ [📄license-img]: https://img.shields.io/badge/License-AGPL--3.0--only_OR_PolyForm--Small--Business--1.0.0-259D6C.svg
770
+ [📄license-compat]: https://www.apache.org/legal/resolved.html#category-x
771
+ [📄license-compat-img]: https://img.shields.io/badge/Apache_Incompatible:_Category_X-%E2%9C%97-C0392B.svg?style=flat&logo=Apache
772
+
773
+ [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
774
+ [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
775
+ [🚎yard-current]: http://rubydoc.info/gems/kettle-jem
776
+ [🚎yard-head]: https://kettle-jem.galtzo.com
777
+ [💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
778
+ [💎SHA_checksums]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/checksums
779
+ [💎rlts]: https://github.com/rubocop-lts/rubocop-lts
780
+ [💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
781
+ [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
782
+ [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
783
+ [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
784
+
785
+ <!-- kettle-jem:metadata:start -->
786
+ | Field | Value |
787
+ |---|---|
788
+ | Package | kettle-jem |
789
+ | 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. |
790
+ | Homepage | https://github.com/structuredmerge/structuredmerge-ruby |
791
+ | Source | https://github.com/structuredmerge/structuredmerge-ruby |
792
+ | License | `AGPL-3.0-only` OR `PolyForm-Small-Business-1.0.0` |
793
+ | Funding | https://github.com/sponsors/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/kettle-jem, https://www.buymeacoffee.com/pboling |
794
+ <!-- kettle-jem:metadata:end -->