kettle-family 1.1.9 → 1.2.0
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 +94 -1
- data/README.md +158 -17
- data/exe/kettle-family +2 -1
- data/lib/kettle/family/cli.rb +46 -4
- data/lib/kettle/family/command_runner.rb +29 -3
- data/lib/kettle/family/config.rb +18 -18
- data/lib/kettle/family/discovery.rb +46 -8
- data/lib/kettle/family/report.rb +23 -4
- data/lib/kettle/family/secrets.rb +149 -0
- data/lib/kettle/family/unreleased_gem_cleanup.rb +109 -0
- data/lib/kettle/family/version.rb +4 -1
- data/lib/kettle/family/workflow.rb +371 -22
- data/lib/kettle/family/workflow_progress.rb +68 -12
- data/lib/kettle/family.rb +2 -0
- data.tar.gz.sig +0 -0
- metadata +12 -10
- 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: aefb679d0cc2bdabed07ed294dfe6a270a2c8739bcb11b80c1eb1a367a8c72c9
|
|
4
|
+
data.tar.gz: c77c800317e98d86444d28ef032956d57dcfa187cb79a39b622ef266502a5a34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77f54999801132c906f45e29b377c088e6459fa4103649a06f4e1679fee57ffa99f43c38d5af5af9dae12c9352ef027e896775c897ba077a28057122f700aa5f
|
|
7
|
+
data.tar.gz: a213bee6bf3d05686cfe38957c47a8554190283b944037532426097a1d23285c5132a9aef20f71637305aba02421d9761ac5ae0605f8fcd59d43418d673150e8
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,97 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [1.2.0] - 2026-07-27
|
|
34
|
+
|
|
35
|
+
- TAG: [v1.2.0][1.2.0t]
|
|
36
|
+
- COVERAGE: 93.85% -- 3557/3790 lines in 27 files
|
|
37
|
+
- BRANCH COVERAGE: 76.15% -- 1338/1757 branches in 27 files
|
|
38
|
+
- 28.20% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `kettle-family reset Gemfile.lock` now resets selected member lockfiles with
|
|
43
|
+
local sibling paths disabled and repairs missing checksum entries by updating
|
|
44
|
+
the affected released gems.
|
|
45
|
+
|
|
46
|
+
- `kettle-family release` can now use an opt-in `1password` release secrets
|
|
47
|
+
provider to load the gem signing passphrase and RubyGems MFA OTP from the
|
|
48
|
+
local `op` CLI during executed publish flows.
|
|
49
|
+
|
|
50
|
+
- `kettle-family clean-unreleased` now scans installed versions of selected
|
|
51
|
+
family gems and can uninstall local versions newer than each gem's latest
|
|
52
|
+
released version, helping recover from failed local release attempts that
|
|
53
|
+
leave unpublished gems installed.
|
|
54
|
+
|
|
55
|
+
- kettle-jem-template-20260726-001 - Projects now include YARD lint
|
|
56
|
+
configuration and documentation dependencies so documentation issues fail
|
|
57
|
+
before generated docs are refreshed.
|
|
58
|
+
|
|
59
|
+
- kettle-jem-template-20260727-001 - Spec harness documentation now lists the
|
|
60
|
+
RSpec helpers provided by `kettle-test`.
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- Templating and release progress rows now show a scheduled family-step counter
|
|
65
|
+
and per-member elapsed timer, making parallel family runs easier to scan.
|
|
66
|
+
|
|
67
|
+
- `kettle-family reset Gemfile.lock` now delegates to each member's
|
|
68
|
+
`kettle-reset release-lockfiles`, keeping `Gemfile.lock` and
|
|
69
|
+
`Appraisal.root.gemfile.lock` reset semantics aligned with `kettle-release`.
|
|
70
|
+
|
|
71
|
+
- Explicit `members.roots` lists now remain open to member discovery by default:
|
|
72
|
+
newly discovered gems are included in family operations and reported as
|
|
73
|
+
unlisted until the config is updated. Set `members.discover: false` to operate
|
|
74
|
+
only on configured members.
|
|
75
|
+
- The `kettle-family` executable startup header is now shown only when
|
|
76
|
+
`--verbose` is passed; `-v` and `--version` still print just the executable
|
|
77
|
+
version and exit.
|
|
78
|
+
- README release guidance now documents release secrets provider overrides,
|
|
79
|
+
release-state columns, default `--only` filters, JSON reports, and
|
|
80
|
+
unreleased-gem cleanup recovery.
|
|
81
|
+
- Release secrets now use the shared `kettle-dev` provider implementation;
|
|
82
|
+
family publish runs cache the signing passphrase once and let child
|
|
83
|
+
`kettle-release` processes fetch RubyGems OTP values directly.
|
|
84
|
+
- Family publish runs now pass `--yes` to child `kettle-release` commands by
|
|
85
|
+
default, so release-owned confirmation prompts are approved explicitly instead
|
|
86
|
+
of being answered by terminal prompt detection.
|
|
87
|
+
|
|
88
|
+
- GitHub workflows now use the setup-ruby-flash revision that supports
|
|
89
|
+
appraisal-only setup without installing the main Gemfile bundle.
|
|
90
|
+
|
|
91
|
+
- kettle-jem-template-20260728-001 - Generated Ruby workflows now use clearer
|
|
92
|
+
setup-ruby-flash planning and can prepare appraisal-only jobs without
|
|
93
|
+
installing the main Gemfile bundle.
|
|
94
|
+
|
|
95
|
+
### Fixed
|
|
96
|
+
|
|
97
|
+
- RuboCop Gradual now ignores gems installed under `gemfiles/vendor/bundle`,
|
|
98
|
+
preventing vendored dependency source from being treated as project lint debt.
|
|
99
|
+
|
|
100
|
+
- Family releases now pass configured 1Password CLI paths through to child
|
|
101
|
+
`kettle-release` processes and derive direct secret handoff from normalized
|
|
102
|
+
provider config instead of parsing equivalent command-line spellings.
|
|
103
|
+
|
|
104
|
+
- Family release secrets now preserve `Kettle::Family::Error` failures when the
|
|
105
|
+
shared `kettle-dev` provider reports 1Password lookup errors.
|
|
106
|
+
|
|
107
|
+
- Template summaries now count unique changed files from kettle-jem event
|
|
108
|
+
streams instead of adding duplicate per-phase summary counts.
|
|
109
|
+
- Family release lockfile refreshes now disable the generated local-path
|
|
110
|
+
toggles used by member Gemfiles, reject lockfile refreshes that still write
|
|
111
|
+
path sources, and re-normalize lockfiles before family-managed release pushes.
|
|
112
|
+
|
|
113
|
+
- kettle-jem-template-20260726-002 - Generated version files now document their
|
|
114
|
+
version namespace and constants, reducing warning-only YARD lint output.
|
|
115
|
+
|
|
116
|
+
- kettle-jem-template-20260726-003 - Coverage upload steps now treat Coveralls,
|
|
117
|
+
QLTY, and Codecov as optional, so provider outages do not fail CI when local
|
|
118
|
+
coverage thresholds still pass.
|
|
119
|
+
|
|
120
|
+
- kettle-jem-template-20260728-002 - Generated RuboCop configs now ignore the
|
|
121
|
+
same `gemfiles/vendor/bundle` tree as `.gitignore`, so vendored dependency
|
|
122
|
+
installs are not reported as project lint debt.
|
|
123
|
+
|
|
33
124
|
## [1.1.9] - 2026-07-25
|
|
34
125
|
|
|
35
126
|
- TAG: [v1.1.9][1.1.9t]
|
|
@@ -1072,7 +1163,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
1072
1163
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
1073
1164
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
1074
1165
|
|
|
1075
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.
|
|
1166
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.0...HEAD
|
|
1167
|
+
[1.2.0]: https://github.com/kettle-dev/kettle-family/compare/v1.1.9...v1.2.0
|
|
1168
|
+
[1.2.0t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.0
|
|
1076
1169
|
[1.1.9]: https://github.com/kettle-dev/kettle-family/compare/v1.1.8...v1.1.9
|
|
1077
1170
|
[1.1.9t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.1.9
|
|
1078
1171
|
[1.1.8]: https://github.com/kettle-dev/kettle-family/compare/v1.1.7...v1.1.8
|
data/README.md
CHANGED
|
@@ -38,7 +38,8 @@ is, avoiding duplicate release-prep commits after a failure/fix/retry cycle.
|
|
|
38
38
|
`kettle-release` runs its own `kettle-pre-release` gate for full publish flows;
|
|
39
39
|
options such as `start_step=N` and `--local-ci` pass through, CI failures still
|
|
40
40
|
abort by default, and gem signing passphrases are cached once per family run
|
|
41
|
-
while RubyGems MFA prompts remain interactive
|
|
41
|
+
while RubyGems MFA prompts remain interactive unless an opt-in release secrets
|
|
42
|
+
provider supplies them.
|
|
42
43
|
|
|
43
44
|
## 💡 Info you can shake a stick at
|
|
44
45
|
|
|
@@ -139,17 +140,34 @@ gem install kettle-family
|
|
|
139
140
|
`kettle-family` reads `.kettle-family.yml` from the family root by default.
|
|
140
141
|
Use `--config PATH` to load a different file.
|
|
141
142
|
|
|
142
|
-
Member discovery is recursive under the
|
|
143
|
-
gemspecs ignored by git and any configured `members.exclude` glob patterns
|
|
144
|
-
loading gemspec metadata, so fixture or temporary gemspecs do not create
|
|
145
|
-
members. Exclude patterns are matched relative to the family root and
|
|
146
|
-
member root.
|
|
143
|
+
Member discovery is recursive under the family member search roots. Discovery
|
|
144
|
+
skips gemspecs ignored by git and any configured `members.exclude` glob patterns
|
|
145
|
+
before loading gemspec metadata, so fixture or temporary gemspecs do not create
|
|
146
|
+
duplicate members. Exclude patterns are matched relative to the family root and
|
|
147
|
+
to each member root.
|
|
148
|
+
|
|
149
|
+
Use `members.roots` as the normal configured member list. Each root is a member
|
|
150
|
+
directory with one primary gemspec. When `members.roots` is present,
|
|
151
|
+
`kettle-family` still discovers additional matching gems by default, includes
|
|
152
|
+
them in the operative member set, and reports warnings so the config can be
|
|
153
|
+
updated. Set `members.discover: false` when a family must operate only on the
|
|
154
|
+
configured roots.
|
|
155
|
+
|
|
156
|
+
Use `members.explicit` only when the member directory and gemspec cannot be
|
|
157
|
+
resolved by the one-gemspec rule. For example, use it when a member directory has
|
|
158
|
+
multiple gemspecs or the intended gemspec filename does not match the directory.
|
|
147
159
|
|
|
148
160
|
```yaml
|
|
149
161
|
family:
|
|
150
162
|
members_root: gems
|
|
151
163
|
|
|
152
164
|
members:
|
|
165
|
+
roots:
|
|
166
|
+
- gems/alpha
|
|
167
|
+
- gems/beta
|
|
168
|
+
explicit:
|
|
169
|
+
- root: gems/compat
|
|
170
|
+
gemspec: compat-runtime.gemspec
|
|
153
171
|
exclude:
|
|
154
172
|
- "**/tmp/**"
|
|
155
173
|
- "**/vendor/**"
|
|
@@ -191,6 +209,10 @@ Monorepo families whose member gems share release metadata from the repository
|
|
|
191
209
|
root can configure readiness and changelog ownership explicitly:
|
|
192
210
|
|
|
193
211
|
```yaml
|
|
212
|
+
family:
|
|
213
|
+
name: structuredmerge-ruby
|
|
214
|
+
local_path_env: STRUCTUREDMERGE_DEV
|
|
215
|
+
|
|
194
216
|
check:
|
|
195
217
|
required_files:
|
|
196
218
|
- Gemfile
|
|
@@ -212,11 +234,23 @@ changelog:
|
|
|
212
234
|
release:
|
|
213
235
|
env:
|
|
214
236
|
KETTLE_DEV_DEV: false
|
|
237
|
+
disable_local_path_env:
|
|
238
|
+
- TSLP_DEV
|
|
215
239
|
family_changelog:
|
|
216
240
|
enabled: true
|
|
217
241
|
command: bundle exec kettle-changelog
|
|
218
242
|
```
|
|
219
243
|
|
|
244
|
+
`family.local_path_env` names the environment variable that activates local
|
|
245
|
+
path dependencies for this family. It is the family identity used by other
|
|
246
|
+
families when they scan lockfile `PATH` remotes and need to disable local
|
|
247
|
+
resolution before writing CI-facing release lockfiles. Configure it when the
|
|
248
|
+
env name is not the default derived from `family.name`.
|
|
249
|
+
|
|
250
|
+
`release.disable_local_path_env` is different: it is an extra release-lockfile
|
|
251
|
+
normalization shutoff list for path-injecting env vars that are not themselves
|
|
252
|
+
discoverable as family roots, such as vendored parser/tooling paths.
|
|
253
|
+
|
|
220
254
|
For `changelog.mode: root`, release commands pass `K_CHANGELOG_GEM_NAME` as the
|
|
221
255
|
configured family name and `K_CHANGELOG_VERSION_FILE` when `version_file` is
|
|
222
256
|
configured, so `kettle-changelog` can operate from a repository root without a
|
|
@@ -257,6 +291,30 @@ Executed publish runs skip versions that are already published. CI failures
|
|
|
257
291
|
abort by default; pass `--continue-ci-failures` to set
|
|
258
292
|
`K_RELEASE_CI_CONTINUE=true` for the underlying `kettle-release` process.
|
|
259
293
|
|
|
294
|
+
Executed publish runs can also use a release secrets provider. The default
|
|
295
|
+
provider is `interactive`. The `1password` provider is supplied by
|
|
296
|
+
`kettle-dev`, uses the local `op` CLI, and expects that `op` is installed,
|
|
297
|
+
unlocked, and signed in before the release starts.
|
|
298
|
+
|
|
299
|
+
```yaml
|
|
300
|
+
release:
|
|
301
|
+
secrets:
|
|
302
|
+
provider: 1password
|
|
303
|
+
item: Rubygems
|
|
304
|
+
gem_signing_passphrase_field: GEM-SIGN-PASSPHRASE
|
|
305
|
+
rubygems_otp_field: one-time password
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Use `--secrets-provider 1password` to opt in explicitly for a release run, or
|
|
309
|
+
`--secrets-provider interactive` to force prompts even when the family config
|
|
310
|
+
has a provider configured. Family publish runs pass `--yes` to child
|
|
311
|
+
`kettle-release` commands by default, making release confirmation approval
|
|
312
|
+
explicit instead of relying on terminal prompt detection. Pass `--no-accept` to
|
|
313
|
+
leave child release confirmation prompts interactive. When publishing through
|
|
314
|
+
`kettle-release`, `kettle-family` loads the signing passphrase once and passes
|
|
315
|
+
the cached value to each child process; `kettle-release` still fetches RubyGems
|
|
316
|
+
OTP values directly from 1Password at prompt time.
|
|
317
|
+
|
|
260
318
|
## 🔧 Basic Usage
|
|
261
319
|
|
|
262
320
|
Inspect discovery and release plans before executing them:
|
|
@@ -270,12 +328,17 @@ Audit changelog release state across the selected family members:
|
|
|
270
328
|
|
|
271
329
|
```console
|
|
272
330
|
kettle-family release-state
|
|
331
|
+
kettle-family state
|
|
273
332
|
```
|
|
274
333
|
|
|
275
|
-
The release-state report lists each
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
334
|
+
`state` is an alias for `release-state`. The release-state report lists each
|
|
335
|
+
gem's current `version.rb`, latest versioned `CHANGELOG.md` section, latest
|
|
336
|
+
published RubyGems release, latest GitHub release tag, transfer-changelog lag,
|
|
337
|
+
whether pending changelog work exists in either `Unreleased` or an unpublished
|
|
338
|
+
prepared release section, and how many commits the current branch is ahead of or
|
|
339
|
+
behind the latest release tag. The version columns are ordered as `V.rb`,
|
|
340
|
+
`V.ch.md`, `V.rel`, and `GH.rel`; `GH.rel` is prefixed with a red circle when it
|
|
341
|
+
does not match `V.rel`.
|
|
279
342
|
|
|
280
343
|
Use release-state tokens with `--only` to select gems by state instead of by
|
|
281
344
|
name. Multiple tokens are combined with logical AND, so this selects only gems
|
|
@@ -295,6 +358,14 @@ The supported release-state tokens are:
|
|
|
295
358
|
| `unreleased` | The current `version.rb` is not the latest published gem version. |
|
|
296
359
|
| `prepared` | The changelog already has a versioned section for the current `version.rb`. |
|
|
297
360
|
| `pending` | The member has unreleased changelog work or an unpublished prepared release. |
|
|
361
|
+
| `bump` | The member has unreleased changelog work and still needs a version bump. |
|
|
362
|
+
|
|
363
|
+
When `--only` is omitted, `kettle-family bump` defaults to `--only bump` and
|
|
364
|
+
`kettle-family release` defaults to `--only pending`.
|
|
365
|
+
|
|
366
|
+
Most commands accept `--json` to print a machine-readable report instead of the
|
|
367
|
+
text report, and `--report PATH` to write that JSON report while still printing
|
|
368
|
+
the normal text report.
|
|
298
369
|
|
|
299
370
|
Plan or update GitHub Actions workflow SHA pins across the selected family
|
|
300
371
|
members:
|
|
@@ -322,12 +393,78 @@ kettle-family release --execute --publish --skip-remotes cb,mirror2
|
|
|
322
393
|
|
|
323
394
|
Publish through `kettle-release`. A full publish first runs the
|
|
324
395
|
`kettle-pre-release` gate, then prompts once for the gem signing key password
|
|
325
|
-
and leaves RubyGems MFA prompts interactive:
|
|
396
|
+
and leaves RubyGems MFA prompts interactive by default:
|
|
326
397
|
|
|
327
398
|
```console
|
|
328
399
|
kettle-family release --publish --execute
|
|
329
400
|
```
|
|
330
401
|
|
|
402
|
+
By default, family publish runs pass `--yes` to child `kettle-release`
|
|
403
|
+
commands. Use `--no-accept` when you want each child release command to ask its
|
|
404
|
+
own confirmation prompts.
|
|
405
|
+
|
|
406
|
+
Executed publish runs can opt in to the local 1Password CLI for unattended
|
|
407
|
+
release credentials. The gem signing passphrase is loaded once and cached only
|
|
408
|
+
in memory for the current `kettle-family` process. When the publish command is
|
|
409
|
+
`kettle-release`, that cached value is passed through an internal environment
|
|
410
|
+
integration so child releases do not query 1Password repeatedly for the same
|
|
411
|
+
passphrase. RubyGems OTP values are loaded by `kettle-release` when each MFA
|
|
412
|
+
prompt arrives, so long releases do not reuse a stale TOTP from release startup.
|
|
413
|
+
|
|
414
|
+
```yaml
|
|
415
|
+
release:
|
|
416
|
+
secrets:
|
|
417
|
+
provider: 1password
|
|
418
|
+
item: Rubygems
|
|
419
|
+
gem_signing_passphrase_field: GEM-SIGN-PASSPHRASE
|
|
420
|
+
rubygems_otp_field: one-time password
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
```console
|
|
424
|
+
kettle-family release --publish --execute --secrets-provider 1password
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
For vault-specific or field-specific references, configure explicit 1Password
|
|
428
|
+
secret references. `rubygems_otp_reference` is read with `op read`; otherwise
|
|
429
|
+
the OTP path uses `op item get ITEM --otp`.
|
|
430
|
+
|
|
431
|
+
```yaml
|
|
432
|
+
release:
|
|
433
|
+
secrets:
|
|
434
|
+
provider: 1password
|
|
435
|
+
account: my.1password.com
|
|
436
|
+
gem_signing_passphrase_reference: op://Private/Rubygems/GEM-SIGN-PASSPHRASE
|
|
437
|
+
rubygems_otp_reference: op://Private/Rubygems/one-time password?attribute=otp
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
If an old failed release left local unpublished versions installed and Bundler
|
|
441
|
+
keeps selecting them, clean selected family members before retrying:
|
|
442
|
+
|
|
443
|
+
```console
|
|
444
|
+
kettle-family clean-unreleased --execute
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
The cleanup command compares installed local gem versions with each member's
|
|
448
|
+
latest released version and uninstalls only versions newer than the release
|
|
449
|
+
state reports.
|
|
450
|
+
|
|
451
|
+
If a member lockfile has captured local sibling paths or checksum-less gem
|
|
452
|
+
versions from local development, reset the selected members' `Gemfile.lock`
|
|
453
|
+
files:
|
|
454
|
+
|
|
455
|
+
```console
|
|
456
|
+
kettle-family reset Gemfile.lock
|
|
457
|
+
kettle-family reset Gemfile.lock --execute
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
`reset Gemfile.lock` runs each member's `bundle exec kettle-reset release-lockfiles`
|
|
461
|
+
with configured, inferred, and path-like local `*_DEV`/`*_LOCAL` environment
|
|
462
|
+
variables disabled. The member-level reset rewrites local sibling paths and
|
|
463
|
+
checksum-less local gem versions back to released registry versions with
|
|
464
|
+
`sha256=` checksums in `Gemfile.lock` and `Appraisal.root.gemfile.lock`.
|
|
465
|
+
Executed family resets fail if the resulting lockfiles still contain sibling
|
|
466
|
+
path remotes or checksum gaps.
|
|
467
|
+
|
|
331
468
|
Resume a failed family publish after fixing the failure. Already published
|
|
332
469
|
versions are skipped automatically; `start_step` is passed to `kettle-release`
|
|
333
470
|
for unreleased members that still need work. Use `--start-step` only after the
|
|
@@ -371,7 +508,9 @@ kettle-family template --execute --only kettle-family
|
|
|
371
508
|
|
|
372
509
|
Templating uses `kettle-jem --events` as the default child interface, so
|
|
373
510
|
executed runs stream member-prefixed progress while still keeping the final
|
|
374
|
-
family report.
|
|
511
|
+
family report. Each member line includes a scheduled family-step counter and a
|
|
512
|
+
per-member elapsed timer. Child `kettle-jem` events can still add progress marks,
|
|
513
|
+
but only completed family steps advance the counter. Progress marks include:
|
|
375
514
|
|
|
376
515
|
| Mark | Meaning |
|
|
377
516
|
|------|---------|
|
|
@@ -386,10 +525,12 @@ Example progress:
|
|
|
386
525
|
|
|
387
526
|
```text
|
|
388
527
|
templating 1 member with 1 job:
|
|
389
|
-
[kettle-family] >
|
|
390
|
-
[kettle-family]
|
|
391
|
-
[kettle-family]
|
|
392
|
-
[kettle-family]
|
|
528
|
+
[kettle-family] (0/4) 00:00 > prepare_lockfiles
|
|
529
|
+
[kettle-family] (1/4) 00:01 . prepare_lockfiles
|
|
530
|
+
[kettle-family] (2/4) 00:05 . prepare_template_dependencies
|
|
531
|
+
[kettle-family] (3/4) 00:12 . template
|
|
532
|
+
[kettle-family] (4/4) 00:13 . normalize_lockfiles
|
|
533
|
+
[kettle-family] (4/4) 00:13 done 2 files changed
|
|
393
534
|
.
|
|
394
535
|
template summary: 1/1 members ok, 2 files changed
|
|
395
536
|
```
|
|
@@ -669,7 +810,7 @@ Thanks for RTFM. ☺️
|
|
|
669
810
|
[📌gitmoji]: https://gitmoji.dev
|
|
670
811
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
671
812
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
672
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.
|
|
813
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.790-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
673
814
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
674
815
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
675
816
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/exe/kettle-family
CHANGED
|
@@ -13,6 +13,7 @@ end
|
|
|
13
13
|
|
|
14
14
|
require "kettle/family"
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
verbose_header = ARGV.delete("--verbose")
|
|
17
|
+
puts "== #{script_basename} v#{Kettle::Family::Version::VERSION} ==" if verbose_header && !ARGV.include?("--json")
|
|
17
18
|
|
|
18
19
|
exit(Kettle::Family::CLI.call(ARGV))
|
data/lib/kettle/family/cli.rb
CHANGED
|
@@ -10,8 +10,8 @@ module Kettle
|
|
|
10
10
|
class CLI < CommandKit::Command
|
|
11
11
|
include CommandKit::Commands
|
|
12
12
|
|
|
13
|
-
COMMANDS = %w[discover plan report metadata check test lint docs template gha-sha-pins bup bupb bex install bump bump-version add-changelog release push pull sync up branch-lanes release-state state].freeze
|
|
14
|
-
WORKFLOW_COMMANDS = %w[check test lint docs template gha-sha-pins bup bupb bex release push pull sync up].freeze
|
|
13
|
+
COMMANDS = %w[discover plan report metadata check clean-unreleased reset test lint docs template gha-sha-pins bup bupb bex install bump bump-version add-changelog release push pull sync up branch-lanes release-state state].freeze
|
|
14
|
+
WORKFLOW_COMMANDS = %w[check reset test lint docs template gha-sha-pins bup bupb bex release push pull sync up].freeze
|
|
15
15
|
|
|
16
16
|
command_name "kettle-family"
|
|
17
17
|
usage "[options] COMMAND [ARGS...]"
|
|
@@ -151,12 +151,14 @@ module Kettle
|
|
|
151
151
|
release_ci_workflows: nil,
|
|
152
152
|
release_skip_bundle_audit: false,
|
|
153
153
|
release_skip_remotes: nil,
|
|
154
|
+
release_secrets_provider: nil,
|
|
154
155
|
accept: true,
|
|
155
156
|
tag: false,
|
|
156
157
|
push: false,
|
|
157
158
|
commit: !options.key?(:commit) || options[:commit],
|
|
158
159
|
allow_dirty: truthy_option?(:allow_dirty),
|
|
159
160
|
target_version: nil,
|
|
161
|
+
reset_target: nil,
|
|
160
162
|
bup_args: [],
|
|
161
163
|
bex_args: []
|
|
162
164
|
}.merge(overrides)
|
|
@@ -281,6 +283,25 @@ module Kettle
|
|
|
281
283
|
description "Run internal read-only readiness checks."
|
|
282
284
|
end
|
|
283
285
|
|
|
286
|
+
class CleanUnreleased < WorkflowCommand
|
|
287
|
+
command_name "clean-unreleased"
|
|
288
|
+
usage "[options]"
|
|
289
|
+
description "Uninstall locally installed family gem versions newer than the latest released version."
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
class Reset < WorkflowCommand
|
|
293
|
+
command_name "reset"
|
|
294
|
+
usage "[options] TARGET"
|
|
295
|
+
description "Reset a managed family artifact. Initially supports Gemfile.lock."
|
|
296
|
+
argument :target, required: true, usage: "TARGET", desc: "Artifact to reset"
|
|
297
|
+
|
|
298
|
+
def run(target = nil)
|
|
299
|
+
raise Error, "reset requires TARGET" if target.to_s.empty?
|
|
300
|
+
|
|
301
|
+
run_family("reset", reset_target: target)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
284
305
|
class Test < WorkflowCommand
|
|
285
306
|
command_name "test"
|
|
286
307
|
usage "[options]"
|
|
@@ -443,6 +464,7 @@ module Kettle
|
|
|
443
464
|
option :ci_workflows, long: "--ci-workflows", value: {type: String, usage: "LIST"}, desc: "Pass a comma-separated CI workflow monitor subset through to kettle-release commands"
|
|
444
465
|
option :skip_bundle_audit, long: "--skip-bundle-audit", desc: "Skip bundle:audit/update during release rake checks"
|
|
445
466
|
option :skip_remotes, long: "--skip-remotes", value: {type: String, usage: "LIST"}, desc: "Pass a comma-separated git remote skip list through to kettle-release commands"
|
|
467
|
+
option :secrets_provider, long: "--secrets-provider", value: {type: String, usage: "NAME"}, desc: "Release secrets provider: interactive, 1password"
|
|
446
468
|
option :no_auto_floors, long: "--no-auto-floors", desc: "Do not raise family dependency floors between member releases" do
|
|
447
469
|
options[:no_auto_floors] = true
|
|
448
470
|
end
|
|
@@ -465,6 +487,7 @@ module Kettle
|
|
|
465
487
|
release_ci_workflows: options[:ci_workflows],
|
|
466
488
|
release_skip_bundle_audit: truthy_option?(:skip_bundle_audit),
|
|
467
489
|
release_skip_remotes: options[:skip_remotes],
|
|
490
|
+
release_secrets_provider: options[:secrets_provider],
|
|
468
491
|
release_auto_dependency_floors: !truthy_option?(:no_auto_floors),
|
|
469
492
|
accept: !options.key?(:accept) || options[:accept],
|
|
470
493
|
tag: truthy_option?(:tag),
|
|
@@ -502,6 +525,8 @@ module Kettle
|
|
|
502
525
|
command "report", ReportCommand
|
|
503
526
|
command Metadata
|
|
504
527
|
command Check
|
|
528
|
+
command CleanUnreleased
|
|
529
|
+
command Reset
|
|
505
530
|
command Test
|
|
506
531
|
command Lint
|
|
507
532
|
command Docs
|
|
@@ -552,7 +577,8 @@ module Kettle
|
|
|
552
577
|
def build_report(command, options)
|
|
553
578
|
config = Config.load(root: options[:root], path: options[:config])
|
|
554
579
|
start_at = parse_start_at(options[:start_at])
|
|
555
|
-
|
|
580
|
+
discovery = Discovery.new(config: config)
|
|
581
|
+
members = discovery.members
|
|
556
582
|
ordered = if command == "install"
|
|
557
583
|
install_order(members, config)
|
|
558
584
|
elsif %w[metadata release-state].include?(command)
|
|
@@ -577,7 +603,8 @@ module Kettle
|
|
|
577
603
|
member_release_target_branches: member_release_target_branches(command: command, members: selected, config: config, start_at: start_at),
|
|
578
604
|
release_mode: release_mode(command: command, options: options),
|
|
579
605
|
command: command,
|
|
580
|
-
results: results
|
|
606
|
+
results: results,
|
|
607
|
+
warnings: discovery.warnings
|
|
581
608
|
)
|
|
582
609
|
end
|
|
583
610
|
|
|
@@ -607,6 +634,7 @@ module Kettle
|
|
|
607
634
|
def command_results_for_current_branch(command:, config:, members:, options:, start_at: StartAt.new(nil, nil))
|
|
608
635
|
return bump_version_results(members: members, options: options, phase: command) if %w[bump bump-version].include?(command)
|
|
609
636
|
return add_changelog_results(members: members, options: options) if command == "add-changelog"
|
|
637
|
+
return clean_unreleased_results(config: config, members: members, options: options) if command == "clean-unreleased"
|
|
610
638
|
return branch_lane_results(config: config, members: members) if command == "branch-lanes"
|
|
611
639
|
return release_state_results(config: config, members: members) if command == "release-state"
|
|
612
640
|
return install_results(config: config, members: members, options: options) if command == "install"
|
|
@@ -630,6 +658,7 @@ module Kettle
|
|
|
630
658
|
ci_workflows: options[:release_ci_workflows],
|
|
631
659
|
skip_bundle_audit: options[:release_skip_bundle_audit],
|
|
632
660
|
skip_remotes: options[:release_skip_remotes],
|
|
661
|
+
secrets_provider: release_secrets_provider(command: command, config: config, options: options),
|
|
633
662
|
auto_dependency_floors: options[:release_auto_dependency_floors],
|
|
634
663
|
gha_sha_pins_upgrade: options[:gha_sha_pins_upgrade],
|
|
635
664
|
gha_sha_pins_check: options[:check],
|
|
@@ -638,6 +667,7 @@ module Kettle
|
|
|
638
667
|
verbose: options[:verbose],
|
|
639
668
|
jobs: options[:jobs],
|
|
640
669
|
progress_io: progress_io(command, options),
|
|
670
|
+
reset_target: options[:reset_target],
|
|
641
671
|
bup_args: options[:bup_args],
|
|
642
672
|
bex_args: options[:bex_args],
|
|
643
673
|
start_member: start_at.member,
|
|
@@ -653,6 +683,12 @@ module Kettle
|
|
|
653
683
|
stdout
|
|
654
684
|
end
|
|
655
685
|
|
|
686
|
+
def release_secrets_provider(command:, config:, options:)
|
|
687
|
+
return nil unless command == "release"
|
|
688
|
+
|
|
689
|
+
Secrets::Factory.build(config: config, override_provider: options[:release_secrets_provider])
|
|
690
|
+
end
|
|
691
|
+
|
|
656
692
|
def branch_target_command?(command, config)
|
|
657
693
|
return false if config.release_target_branches.empty?
|
|
658
694
|
return false if command == "release-state"
|
|
@@ -727,6 +763,8 @@ module Kettle
|
|
|
727
763
|
discovered = Discovery.new(config: config).members
|
|
728
764
|
ordered = (command == "install") ? install_order(discovered, config) : Orderer.new(members: discovered, mode: config.order_mode, hints: config.order_hints).ordered
|
|
729
765
|
ordered.select { |member| selected_names.include?(member.name) }
|
|
766
|
+
rescue Error
|
|
767
|
+
[]
|
|
730
768
|
end
|
|
731
769
|
|
|
732
770
|
def family_member(config)
|
|
@@ -836,6 +874,10 @@ module Kettle
|
|
|
836
874
|
BranchLaneAudit.new(config: config, members: members).results
|
|
837
875
|
end
|
|
838
876
|
|
|
877
|
+
def clean_unreleased_results(config:, members:, options:)
|
|
878
|
+
UnreleasedGemCleanup.new(config: config, members: members, execute: options[:execute]).results
|
|
879
|
+
end
|
|
880
|
+
|
|
839
881
|
def install_results(config:, members:, options:)
|
|
840
882
|
LocalInstall.new(config: config, members: members, execute: options[:execute], jobs: options[:jobs]).results
|
|
841
883
|
end
|
|
@@ -6,10 +6,15 @@ require "io/console"
|
|
|
6
6
|
module Kettle
|
|
7
7
|
module Family
|
|
8
8
|
class CommandRunner
|
|
9
|
+
SENSITIVE_ENV_KEYS = [
|
|
10
|
+
"KETTLE_RELEASE_GEM_SIGNING_PASSPHRASE"
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
9
13
|
class OtpCoordinator
|
|
10
|
-
def initialize(input: $stdin, output: $stdout, queue_total: nil)
|
|
14
|
+
def initialize(input: $stdin, output: $stdout, queue_total: nil, secrets_provider: nil)
|
|
11
15
|
@input = input
|
|
12
16
|
@output = output
|
|
17
|
+
@secrets_provider = secrets_provider
|
|
13
18
|
@mutex = Mutex.new
|
|
14
19
|
@condition = ConditionVariable.new
|
|
15
20
|
@prompting = false
|
|
@@ -61,6 +66,8 @@ module Kettle
|
|
|
61
66
|
|
|
62
67
|
private
|
|
63
68
|
|
|
69
|
+
attr_reader :secrets_provider
|
|
70
|
+
|
|
64
71
|
def wait_for_response(generation)
|
|
65
72
|
@condition.wait(@mutex) while @prompting
|
|
66
73
|
return @response if @completed_generation == generation
|
|
@@ -88,6 +95,12 @@ module Kettle
|
|
|
88
95
|
end
|
|
89
96
|
|
|
90
97
|
def read_response(chunk:)
|
|
98
|
+
provided = secrets_provider&.rubygems_otp.to_s
|
|
99
|
+
unless provided.empty?
|
|
100
|
+
@output.puts("RubyGems MFA code loaded from configured secrets provider.")
|
|
101
|
+
return provided
|
|
102
|
+
end
|
|
103
|
+
|
|
91
104
|
@output.print("#{otp_prompt_label(chunk)} ")
|
|
92
105
|
@output.flush if @output.respond_to?(:flush)
|
|
93
106
|
if @input.respond_to?(:noecho) && @input.tty?
|
|
@@ -406,7 +419,8 @@ module Kettle
|
|
|
406
419
|
argv = normalize_command(command)
|
|
407
420
|
return argv unless mise_configured?(member)
|
|
408
421
|
|
|
409
|
-
|
|
422
|
+
command_env = nonsensitive_env(env)
|
|
423
|
+
unset_env, set_env = command_env.partition { |_key, value| value.nil? }
|
|
410
424
|
injected_env = [
|
|
411
425
|
*unset_env.flat_map { |key, _value| ["-u", key.to_s] },
|
|
412
426
|
*set_env.map { |key, value| "#{key}=#{value}" }
|
|
@@ -421,7 +435,19 @@ module Kettle
|
|
|
421
435
|
base_env = unbundled_process_env
|
|
422
436
|
return base_env.merge(env) unless mise_configured?(member)
|
|
423
437
|
|
|
424
|
-
base_env
|
|
438
|
+
base_env.merge(sensitive_env(env))
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def nonsensitive_env(env)
|
|
442
|
+
env.reject { |key, _value| sensitive_env_key?(key) }
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def sensitive_env(env)
|
|
446
|
+
env.select { |key, value| sensitive_env_key?(key) && !value.nil? }
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def sensitive_env_key?(key)
|
|
450
|
+
SENSITIVE_ENV_KEYS.include?(key.to_s)
|
|
425
451
|
end
|
|
426
452
|
|
|
427
453
|
def unbundled_process_env
|