kettle-jem 7.1.39 → 7.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +117 -1
- data/README.md +43 -44
- data/lib/kettle/jem/templates/.duel.rb.example +25 -0
- data/lib/kettle/jem/templates/.github/FUNDING.yml.example +0 -1
- data/lib/kettle/jem/templates/.github/FUNDING.yml.no-osc.example +0 -1
- data/lib/kettle/jem/templates/.github/workflows/auto-assign.yml.example +1 -1
- data/lib/kettle/jem/templates/.github/workflows/coverage.yml.example +1 -0
- data/lib/kettle/jem/templates/.github/workflows/perf.yml.example +186 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-22.3.yml.example +3 -19
- data/lib/kettle/jem/templates/.structuredmerge/kettle-jem.yml.example +33 -2
- data/lib/kettle/jem/templates/AGENTS.md.example +1 -0
- data/lib/kettle/jem/templates/CHANGELOG.transfer.md +6 -1
- data/lib/kettle/jem/templates/CONTRIBUTING.md.example +1 -0
- data/lib/kettle/jem/templates/README.md.example +17 -40
- data/lib/kettle/jem/templates/Rakefile.example +1 -0
- data/lib/kettle/jem/templates/gem.gemspec.example +10 -6
- data/lib/kettle/jem/templates/gemfiles/modular/cgi/vHEAD.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/coverage.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/debug.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/documentation.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/duel.gemfile.example +18 -0
- data/lib/kettle/jem/templates/gemfiles/modular/optional.gemfile.example +2 -2
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r3/recording.gemfile.example +2 -2
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r4/recording.gemfile.example +2 -2
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2/v3.0.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r3/v3.0.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r4/v3.0.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/style.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/templating.gemfile.example +1 -1
- data/lib/kettle/jem/templates/gemfiles/modular/webrick/vHEAD.gemfile.example +1 -1
- data/lib/kettle/jem/templates/mise.toml.example +1 -0
- data/lib/kettle/jem/templates/mise.toml.no-osc.example +1 -0
- data/lib/kettle/jem/templates/shim/Gemfile.example +1 -1
- data/lib/kettle/jem/templates/shim/gem.gemspec.example +2 -2
- data/lib/kettle/jem/version.rb +1 -1
- data/lib/kettle/jem.rb +527 -140
- data.tar.gz.sig +0 -0
- metadata +40 -16
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f44a87b5964c590bf6eb74a9da0ae7900cac81230aea488043d27668c4339cee
|
|
4
|
+
data.tar.gz: 350675e9ec0c38f3104318288598c7bfc814c1a36c290ed1cb31ace8fd55093f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b30a4785a25f30cff74549eb28b0c1ccc716d1f549572a385935f06d3191cd109df50589e48c50a401e47dcf4f4dec2715fd1b4ccfac58941ae71cf89f6ce40
|
|
7
|
+
data.tar.gz: 1c5335c6579f99a7a993d463287b3a7ffee8bff522dc58151ac35e3ed8c8e3fa37457117adb1b8f261449edd3179efd47e4fdd53a471f165e37fe986aba1e578
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,22 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
22
22
|
|
|
23
23
|
### Changed
|
|
24
24
|
|
|
25
|
+
- [kc] kettle-jem/prepare: updated 24 project files:
|
|
26
|
+
- configuration (1)
|
|
27
|
+
- dependencies (23)
|
|
28
|
+
|
|
29
|
+
- [kc] kettle-jem/template: updated 5 project files:
|
|
30
|
+
- code and tests (1)
|
|
31
|
+
- dependencies (2)
|
|
32
|
+
- documentation (1)
|
|
33
|
+
- other (1)
|
|
34
|
+
|
|
35
|
+
- [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
|
|
36
|
+
- addressable (>= 2.9.0 -> >= 2.9.1)
|
|
37
|
+
|
|
38
|
+
- [kc] kettle-jem-workflow-pins: Update pinned GitHub Actions in kettle-jem templates:
|
|
39
|
+
- ruby/setup-ruby v1.335.0 (7f797815d8adceb1c412e7abefdd1ab4abc47a5f) -> v1.336.0 (de492cf36cbab201753d720ce4f4e336cd688c94)
|
|
40
|
+
|
|
25
41
|
### Deprecated
|
|
26
42
|
|
|
27
43
|
### Removed
|
|
@@ -30,6 +46,102 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
46
|
|
|
31
47
|
### Security
|
|
32
48
|
|
|
49
|
+
## [7.1.41] - 2026-10-29
|
|
50
|
+
|
|
51
|
+
- TAG: [v7.1.41][7.1.41t]
|
|
52
|
+
- COVERAGE: 93.52% -- 11582/12384 lines in 17 files
|
|
53
|
+
- BRANCH COVERAGE: 77.20% -- 4998/6474 branches in 17 files
|
|
54
|
+
- 20.48% documented
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
|
|
59
|
+
- anonymous_loader (>= 0.1.3 -> >= 0.1.4)
|
|
60
|
+
- kettle-dev (>= 3.1.22 -> >= 3.1.24)
|
|
61
|
+
- kettle-family (>= 1.3.29 -> >= 1.3.32)
|
|
62
|
+
- version_gem (>= 1.1.15 -> >= 1.1.16)
|
|
63
|
+
|
|
64
|
+
- [kc] kettle-jem-workflow-pins: Update pinned GitHub Actions in kettle-jem templates:
|
|
65
|
+
- qltysh/qlty-action/coverage v2.4.0 (c9b09987143d1e4ac955f4803c7bea742102feb5) -> v2.5.0 (ebcd284bf32e36a030afe15f1e136c7be08b9506)
|
|
66
|
+
- ruby/setup-ruby v1.333.0 (e1bd6146574da5e0795075b5319b300edb30c5b0) -> v1.334.0 (26e5b7d197ebf60545ee9667bcbace0be11ccaec)
|
|
67
|
+
|
|
68
|
+
- [kc] kettle-jem/prepare: updated 13 project files:
|
|
69
|
+
- dependencies (13)
|
|
70
|
+
- [kc] kettle-jem/template: updated 10 project files:
|
|
71
|
+
- code and tests (2)
|
|
72
|
+
- dependencies (2)
|
|
73
|
+
- documentation (1)
|
|
74
|
+
- other (3)
|
|
75
|
+
- workflows (2)
|
|
76
|
+
- kettle-jem: cold-start bootstrap runs now materialize the first `.github/FUNDING.yml` structurally from discovered funding facts (GitHub org, Open Collective org, configured platform tokens) instead of relying on the removed unconditional `tidelift:` key; configured runs continue to receive FUNDING.yml from packaged template application
|
|
77
|
+
|
|
78
|
+
- [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
|
|
79
|
+
- kettle-family (>= 1.3.34 -> >= 1.3.35)
|
|
80
|
+
- duel (>= 0.1.0 -> >= 0.1.1)
|
|
81
|
+
|
|
82
|
+
- [kc] kettle-deps-floor-extraction: kettle-jem-deps-floor now delegates shared discovery, planning, AST edits, and CLI reporting to the kettle-deps-floor runtime gem while retaining Kettle Jem source defaults and changelog identity. Template sources with {KJ|...} placeholder tokens stay floor-managed through kettle-deps-floor's placeholder_pattern parsing.
|
|
83
|
+
|
|
84
|
+
- [kc] kettle-jem-workflow-pins: Update pinned GitHub Actions in kettle-jem templates:
|
|
85
|
+
- ruby/setup-ruby v1.334.0 (26e5b7d197ebf60545ee9667bcbace0be11ccaec) -> v1.335.0 (7f797815d8adceb1c412e7abefdd1ab4abc47a5f)
|
|
86
|
+
|
|
87
|
+
- [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
|
|
88
|
+
- nomono (>= 1.1.16 -> >= 1.1.17)
|
|
89
|
+
- kettle-dev (>= 3.1.25 -> >= 3.1.26)
|
|
90
|
+
- kettle-changelog (>= 1.0.14 -> >= 1.0.15)
|
|
91
|
+
- kettle-drift (>= 1.0.18 -> >= 1.0.19)
|
|
92
|
+
- kettle-family (>= 1.3.35 -> >= 1.3.36)
|
|
93
|
+
- kettle-test (>= 2.0.28 -> >= 2.0.29)
|
|
94
|
+
- kettle-soup-cover (>= 3.0.15 -> >= 3.0.16)
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- Gemspec template: the legacy (Ruby < 3.1) gem_version loader no longer require_relative's version.rb, which defined the gem's top-level namespace whenever the gemspec was evaluated (e.g. by bundler/setup), polluting the load path on old Rubies and breaking isolated-loading specs. It now loads version.rb anonymously via Module#module_eval (available on Ruby 2.2+); the Ruby < 2.2 load-path fallback is unchanged.
|
|
99
|
+
|
|
100
|
+
- Skip the generated coverage comment step for fork-originated pull requests, where GitHub's read-only token cannot create comments.
|
|
101
|
+
|
|
102
|
+
- Remove a stale `runtime_heads.gemfile` evaluation from existing `head` appraisals when the separate `dep-heads` appraisal already tests runtime dependency HEADs, avoiding conflicting duplicate requirements for extracted standard libraries.
|
|
103
|
+
|
|
104
|
+
- Curated x_std_lib modular Gemfiles by the shared Ruby-series bucket ranges; obsolete matrix files are removed when the destination raises its minimum Ruby.
|
|
105
|
+
|
|
106
|
+
- Cap the Ruby 2.x StringIO templates at the last compatible release lines, so dependency-floor updates cannot select incompatible releases.
|
|
107
|
+
- Limit automatic template dependency-floor updates to ordinary modular Gemfiles, their `_local` counterparts, and `vHEAD` lanes; Ruby-version compatibility matrix files remain curated.
|
|
108
|
+
|
|
109
|
+
- Legacy kettle-jem config handling: templating now fails closed with a clear error when both .structuredmerge/kettle-jem.yml and a legacy .kettle-jem.yml exist (the legacy file was previously ignored silently, dropping any settings it alone held, e.g. readme.corporate_sponsors), and a legacy-only .kettle-jem.yml is now moved to the canonical .structuredmerge/kettle-jem.yml path on apply (including prepare flows), with the legacy file removed.
|
|
110
|
+
|
|
111
|
+
- kettle-jem: Nomono bundler bootstrap payload now derives its activation floor from TEMPLATE_MANAGED_DEPENDENCIES (was a stale literal >= 1.1.16 while the managed floor is >= 1.1.17), so generated *_local.gemfile bootstraps can no longer drift from the managed nomono floor on floor bumps
|
|
112
|
+
|
|
113
|
+
### Added
|
|
114
|
+
|
|
115
|
+
- kettle-jem: corporate sponsor entries now accept optional `since`/`until` ISO 8601 dates defining the sponsorship window; entries render only while active, so lapsed or future sponsors can be listed in static config without manual README edits
|
|
116
|
+
|
|
117
|
+
- kettle-jem: corporate sponsor entries now support umbrella sponsors via `members` (e.g. Alpha-Omega backed by AWS, Google, Microsoft): the umbrella renders in the top logo row and members render as linked names in a `backed by` caption in the new `{KJ|README:CORPORATE_SPONSORS_DETAIL}` FLOSS Funding block; members support the same `since`/`until` window and cannot nest further
|
|
118
|
+
|
|
119
|
+
- kettle-jem: corporate sponsor configuration now accepts the explicit target_href and image_src keys alongside legacy url and img_src, preserving existing configs while supporting direct, date-bounded static sponsors.
|
|
120
|
+
- kettle-jem: opt-in Duel performance regression guards (https://github.com/sferik/duel-ruby), off by default. With `workflows.duel: true`, the full template generates the Perf workflow (`.github/workflows/perf.yml`), `gemfiles/modular/duel.gemfile` (Duel and StackProf on Ruby 3.4 or later only), a `perf` appraisal, the root Gemfile wiring, a `rake duel` task with a stub for older Rubies, the RuboCop include for `.duel.rb`, and the Duel README badge. It creates `.duel.rb` only when it is missing. While Duel is off, a project's own `.duel.rb`, `perf.yml`, and `duel.gemfile` are neither written nor removed.
|
|
121
|
+
- kettle-jem: `actions/download-artifact` joins the template workflow pins, for the Perf workflow's Comment job.
|
|
122
|
+
- kettle-jem: the Duel template follows Duel 0.1.1, which it now requires. The Perf workflow's Comment job posts a new comment only when Duel says to (by default when a benchmark fails, as `c.comment` in `.duel.rb` chooses), and never edits one, so the pull request keeps every report. `.duel.rb` adds only the Perf workflow to what Duel watches, as Duel 0.1.1 watches the benchmarks, `.duel.rb`, and the version files itself.
|
|
123
|
+
|
|
124
|
+
### Removed
|
|
125
|
+
|
|
126
|
+
- kettle-jem: Tidelift mentions removed from the generated README (subscription URLs are dead post-Sonar acquisition) and from the generated `.github/FUNDING.yml`, including stripping lingering `tidelift:` keys on retemplate; the README Enterprise Support section keeps only the `Lifted` package badge with the caption `Lifted through Tidelift, part of Sonar`; SECURITY.md keeps Tidelift as the security-report channel; the FLOSS Funding section is now split into Corporate Sponsorship (sponsor detail block + company pitch) and Individual Support subsections
|
|
127
|
+
|
|
128
|
+
## [7.1.40] - 2026-10-23
|
|
129
|
+
|
|
130
|
+
- TAG: [v7.1.40][7.1.40t]
|
|
131
|
+
- COVERAGE: 93.45% -- 11397/12196 lines in 17 files
|
|
132
|
+
- BRANCH COVERAGE: 77.11% -- 4911/6369 branches in 17 files
|
|
133
|
+
- 19.89% documented
|
|
134
|
+
|
|
135
|
+
### Changed
|
|
136
|
+
|
|
137
|
+
- [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
|
|
138
|
+
- kettle-dev (>= 3.1.21 -> >= 3.1.22)
|
|
139
|
+
- rubocop-lts-rspec (>= 1.0.7 -> >= 1.0.8)
|
|
140
|
+
|
|
141
|
+
- [kc] kettle-jem-workflow-pins: Update pinned GitHub Actions in kettle-jem templates:
|
|
142
|
+
- pozil/auto-assign-issue v4.0.2 (bec9ebf443592887b8f9bed643afafdfd3f62f1d) -> v4.1.0 (d6e511896e5f1d2408e74db74416647d8ff80c8c)
|
|
143
|
+
- ruby/setup-ruby v1.332.0 (b1da4809b005e0178fe08a1d57317bffc00b7b4c) -> v1.333.0 (e1bd6146574da5e0795075b5319b300edb30c5b0)
|
|
144
|
+
|
|
33
145
|
## [7.1.39] - 2026-10-21
|
|
34
146
|
|
|
35
147
|
- TAG: [v7.1.39][7.1.39t]
|
|
@@ -2450,7 +2562,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2450
2562
|
- Released kettle-jem as part of the initial StructuredMerge Ruby 7.0.0 gem set.
|
|
2451
2563
|
- Included packaged templates and parser-backed merge support for Ruby gem templating.
|
|
2452
2564
|
|
|
2453
|
-
[Unreleased]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.
|
|
2565
|
+
[Unreleased]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.41...HEAD
|
|
2566
|
+
[7.1.41]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.40...v7.1.41
|
|
2567
|
+
[7.1.41t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.41
|
|
2568
|
+
[7.1.40]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.39...v7.1.40
|
|
2569
|
+
[7.1.40t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.40
|
|
2454
2570
|
[7.1.39]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.38...v7.1.39
|
|
2455
2571
|
[7.1.39t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.39
|
|
2456
2572
|
[7.1.38]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.37...v7.1.38
|
data/README.md
CHANGED
|
@@ -57,11 +57,12 @@ to build `kettle-jem` extension gems against the supported plugin seam.
|
|
|
57
57
|
|
|
58
58
|
### Compatibility
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
The gem's declared runtime compatibility floor is MRI Ruby 4.0.0+, with concordant releases of JRuby and TruffleRuby.
|
|
61
61
|
CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
The current MRI CI lane runs on Linux and macOS.
|
|
63
|
+
The test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml`.
|
|
64
|
+
When it is higher than the runtime floor, older runtime-compatible Rubies are
|
|
65
|
+
not covered by the generated CI or Appraisal matrix.
|
|
65
66
|
|
|
66
67
|
<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
|
|
|
@@ -98,22 +99,6 @@ Merge analysis must enter parsing through `tree_haver`. Parser-specific gems reg
|
|
|
98
99
|
[sm-family-rust]: https://github.com/structuredmerge/structuredmerge
|
|
99
100
|
[sm-family-typescript]: https://github.com/structuredmerge/structuredmerge-typescript
|
|
100
101
|
|
|
101
|
-
### Runtime and Platform Support
|
|
102
|
-
|
|
103
|
-
Support tiers describe what this project's CI actually exercises, not what may
|
|
104
|
-
work in an untested environment:
|
|
105
|
-
|
|
106
|
-
- **Parallel support**: CI runs the test suite with multiple workers available.
|
|
107
|
-
- **Unsupported**: the engine/platform combination is not covered by CI and is
|
|
108
|
-
not a project compatibility claim.
|
|
109
|
-
|
|
110
|
-
| Ruby engine/version | Linux | macOS | Windows | Notes |
|
|
111
|
-
|---------------------|-------|-------|---------|-------|
|
|
112
|
-
| MRI 4.0-4.0 | Parallel support | Unsupported | Unsupported | Versioned Ruby workflows run on Linux. |
|
|
113
|
-
| MRI current | Parallel support | Parallel support | Unsupported | Current Ruby workflow covers the declared OS platforms. |
|
|
114
|
-
| MRI head | Parallel support | Unsupported | Unsupported | CI is allowed to fail while Ruby is in development. |
|
|
115
|
-
| JRuby (all CI lanes) | Parallel support | Unsupported | Unsupported | Engine workflows run on Linux; macOS and Windows are not exercised by CI. |
|
|
116
|
-
|
|
117
102
|
### Federated DVCS
|
|
118
103
|
|
|
119
104
|
<details markdown="1">
|
|
@@ -129,29 +114,12 @@ work in an untested environment:
|
|
|
129
114
|
|
|
130
115
|
### Enterprise Support
|
|
131
116
|
|
|
132
|
-
[
|
|
133
|
-
|
|
134
|
-
Available as part of the Tidelift Subscription.
|
|
117
|
+
For enterprise support from the maintainer, use [Upwork][👨🏼🏫expsup-upwork] or [CodeMentor][👨🏼🏫expsup-codementor].
|
|
135
118
|
|
|
136
|
-
|
|
137
|
-
<summary>Need enterprise-level guarantees?</summary>
|
|
138
|
-
|
|
139
|
-
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.
|
|
140
|
-
|
|
141
|
-
[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
|
|
142
|
-
|
|
143
|
-
- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
|
|
144
|
-
- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
|
|
145
|
-
- 💡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
|
|
146
|
-
|
|
147
|
-
Alternatively:
|
|
119
|
+
Other support channels:
|
|
148
120
|
|
|
149
121
|
- [![Ruby Users Forum][✉️ruby-forum-img]][✉️ruby-forum]
|
|
150
122
|
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
|
|
151
|
-
- [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork]
|
|
152
|
-
- [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor]
|
|
153
|
-
|
|
154
|
-
</details>
|
|
155
123
|
|
|
156
124
|
## ✨ Installation
|
|
157
125
|
|
|
@@ -379,6 +347,39 @@ workflows:
|
|
|
379
347
|
Do not use freeze markers for any of these declarations. The configuration is
|
|
380
348
|
the source of truth, and templating keeps the root bundle synchronized with it.
|
|
381
349
|
|
|
350
|
+
### Duel Performance Regression Guards
|
|
351
|
+
|
|
352
|
+
[Duel](https://github.com/sferik/duel-ruby) runs a gem's benchmarks on the
|
|
353
|
+
baseline and on the change side by side, and fails if the change is slower.
|
|
354
|
+
It is off by default. To opt in, set:
|
|
355
|
+
|
|
356
|
+
```yaml
|
|
357
|
+
workflows:
|
|
358
|
+
duel: true
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
With Duel on, the full template generates:
|
|
362
|
+
|
|
363
|
+
| File | What it holds |
|
|
364
|
+
|------|---------------|
|
|
365
|
+
| `.github/workflows/perf.yml` | A read-only Duel job that compares each pull request with the main branch, each push to `main` with the commit before it, and, on a schedule, the last release. A separate Comment job, with only `pull-requests: write` and none of the branch's code, posts the report on the pull request as a new comment, when a benchmark fails by default (`c.comment` in `.duel.rb` chooses when), and again when a later run's outcome differs from its last comment's. When the baseline's version file names another Ruby, the workflow installs that Ruby too. |
|
|
366
|
+
| `gemfiles/modular/duel.gemfile` | Duel and StackProf, on Ruby 3.4 or later only, so older Rubies install neither. |
|
|
367
|
+
| `Appraisals` | A `perf` appraisal, whose generated `gemfiles/perf.gemfile` the Perf workflow bundles. |
|
|
368
|
+
| `Gemfile` | `eval_gemfile "gemfiles/modular/duel.gemfile"`, for `rake duel` in local development. |
|
|
369
|
+
| `Rakefile` | `rake duel`, with a stub task that only warns where Duel isn't installed. |
|
|
370
|
+
| `.rubocop.yml` | `.duel.rb` in `AllCops/Include` (RuboCop skips dotfiles otherwise), merged with RuboCop's own patterns. |
|
|
371
|
+
| `README.md` | The Duel badge, at the end of the badge row. |
|
|
372
|
+
| `.duel.rb` | Duel's configuration, created only when it is missing. It runs each version on the Ruby that its own version file names, and watches the Perf workflow on top of what Duel watches already: `lib/`, `ext/`, the benchmarks, `.duel.rb`, and the version files. After that it is the project's own. |
|
|
373
|
+
|
|
374
|
+
The benchmarks in `duel/` are the project's to write: see
|
|
375
|
+
[Write a benchmark](https://github.com/sferik/duel-ruby#write-a-benchmark).
|
|
376
|
+
The template has already done what `duel init` would, so there is no need to
|
|
377
|
+
run it.
|
|
378
|
+
|
|
379
|
+
While Duel is off, kettle-jem neither writes nor removes `.duel.rb`,
|
|
380
|
+
`.github/workflows/perf.yml`, or `gemfiles/modular/duel.gemfile`, so a
|
|
381
|
+
project that has its own keeps them as they are.
|
|
382
|
+
|
|
382
383
|
### Strategies
|
|
383
384
|
|
|
384
385
|
| Strategy | Behavior |
|
|
@@ -776,6 +777,7 @@ See [LICENSE.md][📄license] for the official copyright notice.
|
|
|
776
777
|
<details markdown="1">
|
|
777
778
|
<summary>Copyright holders</summary>
|
|
778
779
|
|
|
780
|
+
- Required Notice: Copyright (c) 2026 Erik Berlin
|
|
779
781
|
- Required Notice: Copyright (c) 2026 Peter H. Boling
|
|
780
782
|
|
|
781
783
|
</details>
|
|
@@ -874,9 +876,6 @@ Thanks for RTFM. ☺️
|
|
|
874
876
|
[👨🏼🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
|
|
875
877
|
[👨🏼🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
|
|
876
878
|
[👨🏼🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
|
|
877
|
-
[🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-kettle-jem?utm_source=rubygems-kettle-jem&utm_medium=referral&utm_campaign=readme
|
|
878
|
-
[🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
|
|
879
|
-
[🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
|
|
880
879
|
[💁🏼♂️peterboling]: http://www.peterboling.com
|
|
881
880
|
[🚂railsbling]: http://www.railsbling.com
|
|
882
881
|
[📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
|
|
@@ -923,7 +922,7 @@ Thanks for RTFM. ☺️
|
|
|
923
922
|
[📌gitmoji]: https://gitmoji.dev
|
|
924
923
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
925
924
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
926
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-12.
|
|
925
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-12.384-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
927
926
|
[🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
|
|
928
927
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
929
928
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -953,5 +952,5 @@ Thanks for RTFM. ☺️
|
|
|
953
952
|
| Homepage | https://github.com/structuredmerge/structuredmerge-ruby |
|
|
954
953
|
| Source | https://github.com/structuredmerge/structuredmerge-ruby |
|
|
955
954
|
| License | `AGPL-3.0-only` OR `PolyForm-Small-Business-1.0.0` |
|
|
956
|
-
| 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://
|
|
955
|
+
| 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://www.buymeacoffee.com/pboling |
|
|
957
956
|
<!-- kettle-jem:metadata:end -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Duel's configuration: https://github.com/sferik/duel-ruby/blob/main/docs/configuration.md
|
|
4
|
+
#
|
|
5
|
+
# Duel follows conventions, so set only what differs: benchmarks are in
|
|
6
|
+
# duel/**/*_duel.rb, each version's lib/ is first on the load path, a
|
|
7
|
+
# change is compared when it touches lib/ or ext/ (or the benchmarks, this
|
|
8
|
+
# file, or the version files), and releases are tagged v*.
|
|
9
|
+
#
|
|
10
|
+
# kettle-jem creates this file when it is missing, and leaves it alone
|
|
11
|
+
# after that, so it is this project's to change.
|
|
12
|
+
Duel.configure do |c|
|
|
13
|
+
# Each version runs on the Ruby that its own version file names, so a
|
|
14
|
+
# branch that upgrades Ruby is compared on each version's own Ruby.
|
|
15
|
+
c.ruby = :version_file
|
|
16
|
+
# Compare a change to the Perf workflow too, which decides how the code
|
|
17
|
+
# is measured. (Duel already watches lib/, ext/, the benchmarks, this
|
|
18
|
+
# file, and the version files.)
|
|
19
|
+
c.watch += %w[.github/workflows/perf.yml]
|
|
20
|
+
|
|
21
|
+
# When the workflow comments on a pull request with the report: :always,
|
|
22
|
+
# :results (passed or failed), :failures (failed, or couldn't compare),
|
|
23
|
+
# or :never.
|
|
24
|
+
# c.comment = :failures
|
|
25
|
+
end
|
|
@@ -7,4 +7,3 @@ ko_fi: "{KJ|FUNDING:KOFI}" # Replace with a single Ko-fi username
|
|
|
7
7
|
liberapay: "{KJ|FUNDING:LIBERAPAY}" # Replace with a single Liberapay username
|
|
8
8
|
open_collective: # Replace with a single Open Collective slug e.g., orgs/cloud-foundry
|
|
9
9
|
thanks_dev: u/gh/{KJ|GH:USER}
|
|
10
|
-
tidelift: rubygems/{KJ|GEM_NAME}
|
|
@@ -12,7 +12,7 @@ jobs:
|
|
|
12
12
|
pull-requests: write
|
|
13
13
|
steps:
|
|
14
14
|
- name: 'Auto-assign issue'
|
|
15
|
-
uses: pozil/auto-assign-issue@
|
|
15
|
+
uses: pozil/auto-assign-issue@d6e511896e5f1d2408e74db74416647d8ff80c8c # v4.1.0
|
|
16
16
|
with:
|
|
17
17
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
18
18
|
assignees: "{KJ|GH:USER}"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
name: Perf
|
|
2
|
+
|
|
3
|
+
# Performance regression guards: Duel runs the benchmarks in duel/ on the
|
|
4
|
+
# baseline and on this code side by side, and fails if this code is slower.
|
|
5
|
+
# See .duel.rb and https://github.com/sferik/duel-ruby
|
|
6
|
+
|
|
7
|
+
# The Duel job runs this branch's code, so it only reads. Commenting is a
|
|
8
|
+
# job of its own, which runs none of it.
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
pull_request:
|
|
14
|
+
push:
|
|
15
|
+
branches:
|
|
16
|
+
- 'main'
|
|
17
|
+
schedule:
|
|
18
|
+
- cron: "0 4 * * *"
|
|
19
|
+
# Allow manually triggering the workflow.
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
|
|
22
|
+
# Cancels a pull request's previous runs that have not yet completed. Every
|
|
23
|
+
# other run is a group of its own, so none is cancelled: each push to main
|
|
24
|
+
# is compared with the commit before it, and a cancelled run would skip
|
|
25
|
+
# that comparison.
|
|
26
|
+
concurrency:
|
|
27
|
+
group: "${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}"
|
|
28
|
+
cancel-in-progress: true
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
duel:
|
|
32
|
+
if: "github.event_name != 'push' || (!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]'))"
|
|
33
|
+
name: Duel
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
# Whether the Comment job comments (post, change or none), and the
|
|
36
|
+
# run's status (passed, failed or neutral), from Duel.
|
|
37
|
+
outputs:
|
|
38
|
+
comment: ${{ steps.compare.outputs.comment }}
|
|
39
|
+
status: ${{ steps.compare.outputs.status }}
|
|
40
|
+
env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps
|
|
41
|
+
# The perf appraisal: the gemspec's dependencies and Duel (Ruby 3.4 or later)
|
|
42
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/perf.gemfile
|
|
43
|
+
steps:
|
|
44
|
+
- name: Checkout
|
|
45
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
46
|
+
with:
|
|
47
|
+
fetch-depth: 0 # the baseline comes from history, tags included
|
|
48
|
+
persist-credentials: false
|
|
49
|
+
|
|
50
|
+
# .duel.rb sets c.ruby = :version_file, so each version runs on the Ruby
|
|
51
|
+
# that its own version file names (.ruby-version, .tool-versions, or
|
|
52
|
+
# mise.toml, the first that names one, as Duel reads them). When the
|
|
53
|
+
# baseline's names another Ruby than this one's, install that Ruby too,
|
|
54
|
+
# and link it where Duel finds it. The baseline is the one the Compare
|
|
55
|
+
# step uses: the main branch for a pull request, the commit before a
|
|
56
|
+
# push, the last release on a schedule (the newest v* tag before HEAD,
|
|
57
|
+
# as Duel finds it), and where HEAD left the default branch when run by
|
|
58
|
+
# hand.
|
|
59
|
+
- name: Find the baseline's Ruby
|
|
60
|
+
id: baseline
|
|
61
|
+
env:
|
|
62
|
+
BEFORE: ${{ github.event.before }}
|
|
63
|
+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
|
64
|
+
run: |
|
|
65
|
+
case "$GITHUB_EVENT_NAME" in
|
|
66
|
+
pull_request) base="origin/$GITHUB_BASE_REF" ;;
|
|
67
|
+
push) base="$BEFORE" ;;
|
|
68
|
+
schedule) base=$(git describe --tags --abbrev=0 --match 'v*' HEAD^ 2>/dev/null) || base= ;;
|
|
69
|
+
*) base=$(git merge-base HEAD "origin/$DEFAULT_BRANCH" 2>/dev/null) || base= ;;
|
|
70
|
+
esac
|
|
71
|
+
ruby_of() {
|
|
72
|
+
for file in .ruby-version .tool-versions mise.toml .mise.toml; do
|
|
73
|
+
text=$(git show "$1:$file" 2>/dev/null) || continue
|
|
74
|
+
case "$file" in
|
|
75
|
+
.ruby-version) version=$(printf '%s\n' "$text" | awk 'NF { print $1; exit }') ;;
|
|
76
|
+
.tool-versions) version=$(printf '%s\n' "$text" | awk '$1 == "ruby" { print $2; exit }') ;;
|
|
77
|
+
*) version=$(printf '%s\n' "$text" | awk '/^\[/ { tools = ($0 ~ /^\[tools\]/) } tools && match($0, /^[ \t]*"?ruby"?[ \t]*=[ \t]*[[{]?[ \t]*(version[ \t]*=[ \t]*)?"[^"]+"/) { s = substr($0, RSTART, RLENGTH); sub(/"$/, "", s); sub(/.*"/, "", s); print s; exit }') ;;
|
|
78
|
+
esac
|
|
79
|
+
if [ -n "$version" ]; then
|
|
80
|
+
echo "$version"
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
done
|
|
84
|
+
}
|
|
85
|
+
ruby=
|
|
86
|
+
if [ -n "$base" ] && git cat-file -e "$base^{commit}" 2>/dev/null; then
|
|
87
|
+
ruby=$(ruby_of "$base")
|
|
88
|
+
[ "$ruby" = "$(ruby_of HEAD)" ] && ruby=
|
|
89
|
+
fi
|
|
90
|
+
echo "ruby=$ruby" >> "$GITHUB_OUTPUT"
|
|
91
|
+
|
|
92
|
+
- name: Setup the baseline's Ruby
|
|
93
|
+
id: baseline-ruby
|
|
94
|
+
if: steps.baseline.outputs.ruby != ''
|
|
95
|
+
uses: ruby/setup-ruby@de492cf36cbab201753d720ce4f4e336cd688c94 # v1.336.0
|
|
96
|
+
with:
|
|
97
|
+
ruby-version: ${{ steps.baseline.outputs.ruby }}
|
|
98
|
+
# The bundle for this Ruby too, which the baseline's workers load.
|
|
99
|
+
# (Gems installed for one Ruby version don't load on another.)
|
|
100
|
+
bundler-cache: true
|
|
101
|
+
|
|
102
|
+
- name: Link the baseline's Ruby where Duel finds it
|
|
103
|
+
if: steps.baseline.outputs.ruby != ''
|
|
104
|
+
env:
|
|
105
|
+
PREFIX: ${{ steps.baseline-ruby.outputs.ruby-prefix }}
|
|
106
|
+
run: |
|
|
107
|
+
mkdir -p ~/.rubies
|
|
108
|
+
ln -sfn "$PREFIX" ~/.rubies/ruby-"$("$PREFIX/bin/ruby" -e 'print RUBY_VERSION')"
|
|
109
|
+
|
|
110
|
+
# The candidate's Ruby, from its version file (the same one Duel reads)
|
|
111
|
+
- name: Setup Ruby & RubyGems
|
|
112
|
+
uses: ruby/setup-ruby@de492cf36cbab201753d720ce4f4e336cd688c94 # v1.336.0
|
|
113
|
+
with:
|
|
114
|
+
bundler-cache: true
|
|
115
|
+
|
|
116
|
+
# Against the main branch for a pull request, what was there before
|
|
117
|
+
# for a push, and the last release on a schedule (slow creep). Duel
|
|
118
|
+
# adds its report to the job's summary, writes it to duel.md for the
|
|
119
|
+
# comment, and sets the step's outputs to say whether to comment, as
|
|
120
|
+
# c.comment in .duel.rb says, whatever the outcome.
|
|
121
|
+
- name: Compare
|
|
122
|
+
id: compare
|
|
123
|
+
env:
|
|
124
|
+
DUEL_BASE: ${{ github.event_name == 'schedule' && 'last-release' || github.event_name == 'push' && github.event.before || '' }}
|
|
125
|
+
# (Run by hand, it compares anyway: with nothing changed, the code
|
|
126
|
+
# with itself, which checks the setup.)
|
|
127
|
+
DUEL_FORCE: ${{ github.event_name == 'workflow_dispatch' && '1' || '' }}
|
|
128
|
+
run: |
|
|
129
|
+
# (A branch's first push, or one that rewrote history, has no
|
|
130
|
+
# commit before it here to compare against.)
|
|
131
|
+
if [ "$GITHUB_EVENT_NAME" = push ] && ! git cat-file -e "$DUEL_BASE^{commit}" 2>/dev/null; then
|
|
132
|
+
echo "::notice::No commit before this push to compare against"
|
|
133
|
+
exit 0
|
|
134
|
+
fi
|
|
135
|
+
bundle exec duel --markdown "$RUNNER_TEMP/duel.md"
|
|
136
|
+
|
|
137
|
+
# The report, for the Comment job
|
|
138
|
+
- name: Keep the report
|
|
139
|
+
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
|
|
140
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
141
|
+
with:
|
|
142
|
+
name: duel-report
|
|
143
|
+
path: ${{ runner.temp }}/duel.md
|
|
144
|
+
if-no-files-found: ignore # none when the comparison didn't run
|
|
145
|
+
retention-days: 1
|
|
146
|
+
|
|
147
|
+
# The report, as a new comment on the pull request, when Duel says to:
|
|
148
|
+
# always (post), or only when its last comment's status differs from
|
|
149
|
+
# this run's (change), so the latest comment is never stale. It never
|
|
150
|
+
# edits or deletes a comment. This job checks nothing out and runs none
|
|
151
|
+
# of the branch's code, so it alone can write. (A pull request from a
|
|
152
|
+
# fork gets no comment, as its token can't write one: the Duel job's
|
|
153
|
+
# summary has the report.)
|
|
154
|
+
comment:
|
|
155
|
+
name: Comment
|
|
156
|
+
needs: duel
|
|
157
|
+
if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && (needs.duel.outputs.comment == 'post' || needs.duel.outputs.comment == 'change') }}
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
permissions:
|
|
160
|
+
pull-requests: write # to comment on the pull request with the report
|
|
161
|
+
steps:
|
|
162
|
+
- name: Get the report
|
|
163
|
+
id: report
|
|
164
|
+
continue-on-error: true # the Duel job keeps no report when it fails before comparing
|
|
165
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
166
|
+
with:
|
|
167
|
+
name: duel-report
|
|
168
|
+
path: ${{ runner.temp }}/report
|
|
169
|
+
|
|
170
|
+
- name: Comment on the pull request
|
|
171
|
+
if: steps.report.outcome == 'success'
|
|
172
|
+
env:
|
|
173
|
+
GH_TOKEN: ${{ github.token }}
|
|
174
|
+
PR: ${{ github.event.pull_request.number }}
|
|
175
|
+
COMMENT: ${{ needs.duel.outputs.comment }}
|
|
176
|
+
STATUS: ${{ needs.duel.outputs.status }}
|
|
177
|
+
run: |
|
|
178
|
+
marker="<!-- duel"
|
|
179
|
+
# The first line of the last comment Duel posted here: its marker,
|
|
180
|
+
# with its status (none in Duel 0.1.0's, which counts as another).
|
|
181
|
+
last=$(gh api --paginate "repos/$GITHUB_REPOSITORY/issues/$PR/comments" \
|
|
182
|
+
--jq ".[] | select(.user.login == \"github-actions[bot]\") | .body | split(\"\n\")[0] | rtrimstr(\"\r\") | select(startswith(\"$marker status=\") or . == \"$marker -->\")" | tail -n 1)
|
|
183
|
+
if [ "$COMMENT" = post ] || { [ -n "$last" ] && [ "$last" != "$marker status=$STATUS -->" ]; }; then
|
|
184
|
+
{ echo "$marker status=$STATUS -->"; cat "$RUNNER_TEMP/report/duel.md"; } > "$RUNNER_TEMP/comment.md"
|
|
185
|
+
gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR/comments" -F body=@"$RUNNER_TEMP/comment.md" > /dev/null
|
|
186
|
+
fi
|
|
@@ -40,7 +40,8 @@ jobs:
|
|
|
40
40
|
timeout-minutes: 30
|
|
41
41
|
continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }}
|
|
42
42
|
env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps
|
|
43
|
-
|
|
43
|
+
# Use the selected appraisal during setup so engine-specific dependency pins apply immediately.
|
|
44
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/ruby_3_0.gemfile
|
|
44
45
|
strategy:
|
|
45
46
|
matrix:
|
|
46
47
|
include:
|
|
@@ -71,23 +72,6 @@ jobs:
|
|
|
71
72
|
manual-compatibility-bundle: true
|
|
72
73
|
gem-install-retries: 7
|
|
73
74
|
|
|
74
|
-
- name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}"
|
|
75
|
-
id: bundleAppraisalAttempt1
|
|
76
|
-
if: ${{ !env.ACT }}
|
|
77
|
-
run: bundle exec appraisal ${{ matrix.appraisal }} install
|
|
78
|
-
# Continue to the next step on failure
|
|
79
|
-
continue-on-error: true
|
|
80
|
-
|
|
81
|
-
# Effectively an automatic retry of the previous step.
|
|
82
|
-
- name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}"
|
|
83
|
-
id: bundleAppraisalAttempt2
|
|
84
|
-
# If bundleAppraisalAttempt1 failed, try again here; Otherwise skip.
|
|
85
|
-
if: ${{ steps.bundleAppraisalAttempt1.outcome == 'failure' && !env.ACT }}
|
|
86
|
-
run: |
|
|
87
|
-
rm -f gemfiles/${{ matrix.appraisal }}.gemfile.lock
|
|
88
|
-
rm -rf vendor/bundle "$HOME/.bundle/cache"
|
|
89
|
-
bundle exec appraisal ${{ matrix.appraisal }} install
|
|
90
|
-
|
|
91
75
|
- name: Restore RSpec status log
|
|
92
76
|
if: "${{!env.ACT}}"
|
|
93
77
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
@@ -100,4 +84,4 @@ jobs:
|
|
|
100
84
|
|
|
101
85
|
- name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }}
|
|
102
86
|
if: ${{ !env.ACT }}
|
|
103
|
-
run: bundle exec
|
|
87
|
+
run: bundle exec ${{ matrix.exec_cmd }}
|
|
@@ -265,6 +265,17 @@ workflows:
|
|
|
265
265
|
# Include VCR/WebMock recording modular gemfiles in generated Appraisals.
|
|
266
266
|
# Enable only for projects that actually require recording support.
|
|
267
267
|
recording: false
|
|
268
|
+
# Duel performance regression guards (https://github.com/sferik/duel-ruby).
|
|
269
|
+
# Off by default. Set `duel: true` to generate the Perf workflow
|
|
270
|
+
# (.github/workflows/perf.yml), gemfiles/modular/duel.gemfile (loaded by
|
|
271
|
+
# the root Gemfile and a `perf` appraisal), the `rake duel` task, the
|
|
272
|
+
# RuboCop include for .duel.rb, and the Duel README badge. .duel.rb is
|
|
273
|
+
# created only when it is missing, and the benchmarks in duel/ are the
|
|
274
|
+
# project's own to write. Duel requires Ruby 3.4 or later: on older
|
|
275
|
+
# Rubies it is not installed, and `rake duel` only warns. While Duel is
|
|
276
|
+
# off, kettle-jem neither writes nor removes .duel.rb, perf.yml, or
|
|
277
|
+
# duel.gemfile, so a project's own copies stay as they are.
|
|
278
|
+
# duel: true
|
|
268
279
|
# preset: framework
|
|
269
280
|
# framework_matrix:
|
|
270
281
|
# dimension: activerecord
|
|
@@ -346,8 +357,28 @@ readme:
|
|
|
346
357
|
#
|
|
347
358
|
# corporate_sponsors:
|
|
348
359
|
# - name: Example Corp
|
|
349
|
-
#
|
|
350
|
-
#
|
|
360
|
+
# target_href: https://example.com
|
|
361
|
+
# image_src: https://example.com/logo.svg
|
|
362
|
+
# # Legacy aliases remain accepted: url and img_src.
|
|
363
|
+
# # Optional sponsorship window (ISO 8601 dates).
|
|
364
|
+
# # `since` is the first day shown; `until` is the last day shown.
|
|
365
|
+
# # Omit either bound for an open-ended period.
|
|
366
|
+
# # since: 2026-01-01
|
|
367
|
+
# # until: 2026-12-31
|
|
368
|
+
# An umbrella sponsor (an organization that itself is funded by companies,
|
|
369
|
+
# e.g. Alpha-Omega) may list its backers as members. The umbrella renders in
|
|
370
|
+
# the top logo row; members render as linked names in a "backed by" caption
|
|
371
|
+
# in the FLOSS Funding detail block. Members require name and target_href
|
|
372
|
+
# (legacy url is accepted), accept the same optional since/until window,
|
|
373
|
+
# and cannot themselves have members.
|
|
374
|
+
#
|
|
375
|
+
# corporate_sponsors:
|
|
376
|
+
# - name: Alpha-Omega
|
|
377
|
+
# target_href: https://alpha-omega.dev
|
|
378
|
+
# image_src: https://alpha-omega.dev/logo.svg
|
|
379
|
+
# members:
|
|
380
|
+
# - name: Example Corp
|
|
381
|
+
# target_href: https://example.com
|
|
351
382
|
# Optional package-family README block.
|
|
352
383
|
# Supported values: structuredmerge
|
|
353
384
|
# Leave unset for standalone kettle-rb gems and most projects.
|
|
@@ -207,6 +207,7 @@ mise exec -C /path/to/project -- bin/kettle-soup-cover -d
|
|
|
207
207
|
- `K_SOUP_COV_MIN_LINE` – Line coverage threshold
|
|
208
208
|
- `K_SOUP_COV_MIN_BRANCH` – Branch coverage threshold
|
|
209
209
|
- `K_SOUP_COV_MIN_HARD=true` – Fail if thresholds not met
|
|
210
|
+
- `K_SOUP_COV_MIN_STRICT=true` – Require 100% line, branch, and method coverage
|
|
210
211
|
|
|
211
212
|
### Code Quality
|
|
212
213
|
|