kettle-family 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bca5a6ab33a2d894ba06b7b75827b8849476922494bbf02df50efb0d9a0edac
4
- data.tar.gz: 9dc491d15e3f89e737961bf87f103a982bddafc6183dc26f8d2287b84dce7d12
3
+ metadata.gz: 19486d91816150dae28ac5f1e8b31ef3f43b0f4f7004337681f08819fb41b27d
4
+ data.tar.gz: e2bf61a97f739f211accb4fc5cbfe91acc4121077c5f620786d30d217b4cc803
5
5
  SHA512:
6
- metadata.gz: 9ebfabcd00bbb7631fe7ef8f3aae71e283b2bfda98a7c60e8b28551e7e3401f219916408c0fafa7321a62dc3ff44fd02a9a44a7461684399a672df9af89d0adf
7
- data.tar.gz: 9d72e678c1f451b101b3dc297da6b320321a2fec0b1c723a6c174a5e2a5f5f1d0175ef120c276ca1f52df115a70f87a8d6f76a4a7e30eba7fb2b137eade11b29
6
+ metadata.gz: e8826e083336b3de83e313fc3207c60461e494fc8c1c4ad22b8148b861024ca78adcefa1b5c357d7ed90af79dddb3cda59dc4900434e862739d8d2f930e8e0c3
7
+ data.tar.gz: 1d735e6c84877eab971381d22226c96871e2fa0198bab1d9895bf771abdbbe2d5bf2c69a0d16bd04c0a3cdfdc830c7e7354f526313a5f0b8dbeec2a96df7d908
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,93 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.0.2] - 2026-07-21
34
+
35
+ - TAG: [v1.0.2][1.0.2t]
36
+ - COVERAGE: 95.51% -- 2637/2761 lines in 23 files
37
+ - BRANCH COVERAGE: 77.98% -- 928/1190 branches in 23 files
38
+ - 28.63% documented
39
+
40
+ ### Added
41
+
42
+ - `kettle-family template` now passes family-level `readme.corporate_sponsors`
43
+ config into member `kettle-jem` runs for template-managed README sponsor logos.
44
+ - `kettle-family state` is now an alias for `kettle-family release-state`.
45
+ - `kettle-family sync` now fetches the remote default branch, rebases the local
46
+ default branch onto it, then rebases the original checked-out branch onto the
47
+ updated local default branch.
48
+
49
+ ### Changed
50
+
51
+ - `kettle-family release-state` now reports the checked-out branch and displays
52
+ release distance as local/remote ahead and behind counts.
53
+ - kettle-jem-template-20260720-001 - Generated READMEs can now render
54
+ template-managed corporate sponsor logos from project or family config.
55
+ - kettle-jem-template-20260720-002 - Generated development Gemfiles now use the
56
+ released `tree_sitter_language_pack` gem 1.13.3 or newer by default.
57
+ - kettle-jem-template-20260720-003 - Generated StructuredMerge Git diff driver
58
+ config now uses the installed `smorg-rb` Ruby driver name.
59
+ - kettle-jem-template-20260720-004 - Generated multi-engine workflow files now
60
+ omit JRuby and TruffleRuby jobs when project config declares MRI-only engines.
61
+ - kettle-jem-template-20260720-005 - Generated README Support & Community rows
62
+ now include a RubyForum help badge.
63
+
64
+ ### Fixed
65
+
66
+ - Default member discovery now excludes benchmark fixture gemspecs so benchmark
67
+ projects are not treated as install/template/release family members.
68
+ - `kettle-family template` now keeps non-verbose `kettle-jem --events` progress
69
+ compact and omits raw NDJSON event streams from failed text reports.
70
+ - Generated HEAD and runtime dependency HEAD CI workflows no longer include
71
+ JRuby or TruffleRuby jobs for this MRI-only gem.
72
+
73
+ ## [1.0.1] - 2026-07-20
74
+
75
+ - TAG: [v1.0.1][1.0.1t]
76
+ - COVERAGE: 95.44% -- 2534/2655 lines in 23 files
77
+ - BRANCH COVERAGE: 77.89% -- 877/1126 branches in 23 files
78
+ - 29.10% documented
79
+
80
+ ### Added
81
+
82
+ - `kettle-family release-state` now includes an `ahead` column counting commits
83
+ from the latest release tag to the local default branch when available.
84
+ - `kettle-family release` now accepts `--skip-remotes` and forwards the
85
+ comma-separated remote skip list to member `kettle-release` runs.
86
+ - `kettle-family --only` now accepts release-state tokens `unreleased`,
87
+ `prepared`, and `pending`; multiple tokens are combined with logical AND.
88
+ - `kettle-family template` now accepts `--verbose` and forwards verbose mode to
89
+ member `kettle-jem` runs instead of forcing quiet JSON output.
90
+
91
+ - `kettle-family template` now runs `kettle-jem prepare` before full templating
92
+ for Kettle Jem-powered members so templating-only dependency bootstraps, such
93
+ as parser packages, are available before the full template command loads.
94
+
95
+ - `kettle-family template` now uses `kettle-jem --events` as its default
96
+ templating interface, including verbose and single-job runs, and streams
97
+ newline-delimited JSON template phase, recipe, post-apply, command-step,
98
+ diagnostic, and summary events as member-prefixed progress lines.
99
+
100
+ ### Deprecated
101
+
102
+ - `kettle-family bump-version` is now deprecated in favor of `kettle-family bump`.
103
+
104
+ ### Fixed
105
+
106
+ - The templating prepare phase now disables only the implicit family local path
107
+ environment variable unless it was explicitly provided, avoiding stale local
108
+ Gemfile activation failures before the prepare payload can refresh generated
109
+ modular Gemfiles.
110
+ - The templating prepare phase now runs outside each member's Bundler context so
111
+ it can repair stale generated Gemfiles before Bundler evaluates them.
112
+
113
+ - `kettle-family` template preparation now handles custom non-`kettle-jem`
114
+ template commands without treating no-op dependency preparation as failure.
115
+ - `kettle-family release-state` now counts `ahead` from the release tag to the
116
+ checked-out member `HEAD`.
117
+ - Generated docs now retain the YARD `_index.html` content wrapper after
118
+ regeneration with the shared YARD postprocessing stack.
119
+
33
120
  ## [1.0.0] - 2026-07-17
34
121
 
35
122
  - TAG: [v1.0.0][1.0.0t]
@@ -736,7 +823,11 @@ Please file a bug if you notice a violation of semantic versioning.
736
823
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
737
824
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
738
825
 
739
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.0.0...HEAD
826
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.0.2...HEAD
827
+ [1.0.2]: https://github.com/kettle-dev/kettle-family/compare/v1.0.1...v1.0.2
828
+ [1.0.2t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.0.2
829
+ [1.0.1]: https://github.com/kettle-dev/kettle-family/compare/v1.0.0...v1.0.1
830
+ [1.0.1t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.0.1
740
831
  [1.0.0]: https://github.com/kettle-dev/kettle-family/compare/v0.2.7...v1.0.0
741
832
  [1.0.0t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.0.0
742
833
  [0.2.7]: https://github.com/kettle-dev/kettle-family/compare/v0.2.6...v0.2.7
data/README.md CHANGED
@@ -45,7 +45,7 @@ while RubyGems MFA prompts remain interactive.
45
45
  | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
46
46
  |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
47
47
  | Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]|
48
- | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![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] |
48
+ | 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] |
49
49
  | 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] |
50
50
  | 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] |
51
51
  | Compliance | [![License: AGPL-3.0-only][📄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] |
@@ -211,7 +211,7 @@ changelog:
211
211
 
212
212
  release:
213
213
  env:
214
- KETTLE_RB_DEV: false
214
+ KETTLE_DEV_DEV: false
215
215
  family_changelog:
216
216
  enabled: true
217
217
  command: bundle exec kettle-changelog
@@ -273,8 +273,28 @@ kettle-family release-state
273
273
  ```
274
274
 
275
275
  The release-state report lists each gem's current `version.rb`, latest published
276
- release, latest versioned `CHANGELOG.md` section, and whether pending changelog
277
- work exists in either `Unreleased` or an unpublished prepared release section.
276
+ release, latest versioned `CHANGELOG.md` section, whether pending changelog work
277
+ exists in either `Unreleased` or an unpublished prepared release section, and
278
+ how many commits the current branch is ahead of the latest release tag.
279
+
280
+ Use release-state tokens with `--only` to select gems by state instead of by
281
+ name. Multiple tokens are combined with logical AND, so this selects only gems
282
+ that are both unreleased and pending. Status tokens cannot be mixed with member
283
+ names in the same `--only` value.
284
+
285
+ ```console
286
+ kettle-family release-state --only pending
287
+ kettle-family release-state --only unreleased,prepared
288
+ kettle-family release --execute --only unreleased,pending
289
+ ```
290
+
291
+ The supported release-state tokens are:
292
+
293
+ | Token | Meaning |
294
+ |-------|---------|
295
+ | `unreleased` | The current `version.rb` is not the latest published gem version. |
296
+ | `prepared` | The changelog already has a versioned section for the current `version.rb`. |
297
+ | `pending` | The member has unreleased changelog work or an unpublished prepared release. |
278
298
 
279
299
  Plan or update GitHub Actions workflow SHA pins across the selected family
280
300
  members:
@@ -291,6 +311,15 @@ Run release prep/build phases without publishing:
291
311
  kettle-family release --execute
292
312
  ```
293
313
 
314
+ When a remote mirror is temporarily unavailable, skip it for the underlying
315
+ `kettle-release` fetch/parity gate instead of removing the remote from each
316
+ member repository:
317
+
318
+ ```console
319
+ kettle-family release --execute --publish --skip-remotes cb
320
+ kettle-family release --execute --publish --skip-remotes cb,mirror2
321
+ ```
322
+
294
323
  Publish through `kettle-release`. A full publish first runs the
295
324
  `kettle-pre-release` gate, then prompts once for the gem signing key password
296
325
  and leaves RubyGems MFA prompts interactive:
@@ -321,6 +350,61 @@ If you intentionally need to continue after CI failures, opt in explicitly:
321
350
  kettle-family release --publish --execute --continue-ci-failures
322
351
  ```
323
352
 
353
+ Bump aligned member versions with `bump`. The older `bump-version` command still
354
+ works as a deprecated alias.
355
+
356
+ ```console
357
+ kettle-family bump --execute --only pending patch
358
+ kettle-family bump --execute --only unreleased minor
359
+ ```
360
+
361
+ Run `kettle-jem` templating across the selected family members. Planning shows
362
+ the commands that would run; `--execute` runs them. Executed templating defaults
363
+ to a parallel job count based on CPU cores, capped for readability, and can be
364
+ overridden with `--jobs`.
365
+
366
+ ```console
367
+ kettle-family template
368
+ kettle-family template --execute --jobs 4
369
+ kettle-family template --execute --only kettle-family
370
+ ```
371
+
372
+ Templating uses `kettle-jem --events` as the default child interface, so
373
+ executed runs stream member-prefixed progress while still keeping the final
374
+ family report. Progress marks include:
375
+
376
+ | Mark | Meaning |
377
+ |------|---------|
378
+ | `>` | A phase or command step started. |
379
+ | `.` | A phase, recipe, or step completed without changing files. |
380
+ | `*` | A recipe or step changed files. |
381
+ | `!` | A diagnostic was emitted. |
382
+ | `F` | A phase or step failed. |
383
+ | `done` | The member summary was emitted. |
384
+
385
+ Example progress:
386
+
387
+ ```text
388
+ templating 1 member with 1 job:
389
+ [kettle-family] > recipes
390
+ [kettle-family] * Gemfile
391
+ [kettle-family] * post_apply:git_hooks_executable
392
+ [kettle-family] done 2 files changed
393
+ .
394
+ template summary: 1/1 members ok, 2 files changed
395
+ ```
396
+
397
+ Use `--verbose` when you need `kettle-jem` verbose diagnostics layered on top of
398
+ the event stream. Environment overrides pass through to every member workflow
399
+ command, which is useful when templating against local unreleased dependencies.
400
+
401
+ ```console
402
+ kettle-family template --execute --verbose --jobs 1
403
+ kettle-family template --execute \
404
+ --env K_JEM_TEMPLATING=true \
405
+ --env STRUCTUREDMERGE_DEV=/home/me/src/structuredmerge/ruby/gems
406
+ ```
407
+
324
408
  ## 🔐 Security
325
409
 
326
410
  See [SECURITY.md][🔐security].
@@ -386,7 +470,7 @@ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with
386
470
  For example:
387
471
 
388
472
  ```ruby
389
- spec.add_dependency("kettle-family", "~> 0.0")
473
+ spec.add_dependency("kettle-family", "~> 1.0")
390
474
  ```
391
475
 
392
476
  <details markdown="1">
@@ -476,6 +560,8 @@ Thanks for RTFM. ☺️
476
560
  [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
477
561
  [✉️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
478
562
  [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
563
+ [✉️ruby-forum-img]: https://img.shields.io/badge/RubyForum-Help-CC342D?style=for-the-badge&logo=ruby&logoColor=white
564
+ [✉️ruby-forum]: https://www.rubyforum.org/c/help/8
479
565
 
480
566
  [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
481
567
  [⛳️gem-namespace]: https://github.com/kettle-dev/kettle-family
@@ -583,7 +669,7 @@ Thanks for RTFM. ☺️
583
669
  [📌gitmoji]: https://gitmoji.dev
584
670
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
585
671
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
586
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.489-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
672
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-2.761-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
587
673
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
588
674
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
589
675
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -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-version add-changelog release push pull up branch-lanes release-state].freeze
14
- WORKFLOW_COMMANDS = %w[check test lint docs template gha-sha-pins bup bupb bex release push pull up].freeze
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
15
15
 
16
16
  command_name "kettle-family"
17
17
  usage "[options] COMMAND [ARGS...]"
@@ -37,7 +37,7 @@ module Kettle
37
37
 
38
38
  module SelectionOptions
39
39
  def self.included(base)
40
- base.option :only, value: {type: String, usage: "MEMBERS"}, desc: "Select comma-separated members"
40
+ base.option :only, value: {type: String, usage: "MEMBERS"}, desc: "Select comma-separated members, or release-state tokens: unreleased, prepared, pending"
41
41
  base.option :exclude, value: {type: String, usage: "MEMBERS"}, desc: "Exclude comma-separated members"
42
42
  base.option :start_at, long: "--start-at", value: {type: String, usage: "MEMBER[@BRANCH]"}, desc: "Select from member through the end of order"
43
43
  end
@@ -65,6 +65,7 @@ module Kettle
65
65
  module WorkflowOptions
66
66
  def self.included(base)
67
67
  base.option :debug, desc: "Preserve debug environment for workflow commands"
68
+ base.option :verbose, desc: "Pass verbose mode through to supported workflow commands"
68
69
  base.option :jobs, value: {type: Integer, usage: "N"}, desc: "Parallel jobs for supported executed workflows"
69
70
  base.option :env, value: {type: String, usage: "KEY=VALUE"}, desc: "Override an environment variable for each member workflow command" do |value|
70
71
  parse_env_override(value, workflow_env)
@@ -134,6 +135,7 @@ module Kettle
134
135
  report: options[:report],
135
136
  execute: truthy_option?(:execute),
136
137
  debug: truthy_option?(:debug),
138
+ verbose: truthy_option?(:verbose),
137
139
  jobs: options[:jobs],
138
140
  workflow_env: workflow_env,
139
141
  changelog_section: nil,
@@ -148,6 +150,7 @@ module Kettle
148
150
  release_continue_ci_failures: false,
149
151
  release_ci_workflows: nil,
150
152
  release_skip_bundle_audit: false,
153
+ release_skip_remotes: nil,
151
154
  accept: true,
152
155
  tag: false,
153
156
  push: false,
@@ -251,6 +254,16 @@ module Kettle
251
254
  end
252
255
  end
253
256
 
257
+ class State < ReleaseState
258
+ command_name "state"
259
+ description "Alias for release-state."
260
+
261
+ def run(*args)
262
+ unexpected_arguments!(args)
263
+ run_family("release-state")
264
+ end
265
+ end
266
+
254
267
  class WorkflowCommand < BaseCommand
255
268
  include ExecutionOptions
256
269
  include WorkflowOptions
@@ -353,11 +366,11 @@ module Kettle
353
366
  end
354
367
  end
355
368
 
356
- class BumpVersion < BaseCommand
369
+ class Bump < BaseCommand
357
370
  include ExecutionOptions
358
371
  include CommitOptions
359
372
 
360
- command_name "bump-version"
373
+ command_name "bump"
361
374
  usage "[options] VERSION|major|minor|patch|pre"
362
375
  description "Check, plan, or execute family version alignment."
363
376
  argument :target_version, required: false, usage: "VERSION|major|minor|patch|pre", desc: "Version or bump target"
@@ -366,9 +379,31 @@ module Kettle
366
379
  option :from, value: {type: String, usage: "VERSION"}, desc: "Require selected members to currently match VERSION"
367
380
 
368
381
  def run(target_version = nil)
369
- raise Error, "bump-version requires VERSION, major, minor, patch, or pre" unless target_version
382
+ raise Error, "#{workflow_command_name} requires VERSION, major, minor, patch, or pre" unless target_version
383
+
384
+ run_family(workflow_command_name, target_version: target_version, from_version: options[:from])
385
+ end
386
+
387
+ private
388
+
389
+ def workflow_command_name
390
+ "bump"
391
+ end
392
+ end
393
+
394
+ class BumpVersion < Bump
395
+ command_name "bump-version"
396
+ description "Deprecated alias for bump."
397
+
398
+ def run(target_version = nil)
399
+ stderr.puts("kettle-family: bump-version is deprecated; use bump instead.")
400
+ super
401
+ end
402
+
403
+ private
370
404
 
371
- run_family("bump-version", target_version: target_version, from_version: options[:from])
405
+ def workflow_command_name
406
+ "bump-version"
372
407
  end
373
408
  end
374
409
 
@@ -407,6 +442,7 @@ module Kettle
407
442
  option :continue_ci_failures, long: "--continue-ci-failures", desc: "Set K_RELEASE_CI_CONTINUE=true for release commands"
408
443
  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"
409
444
  option :skip_bundle_audit, long: "--skip-bundle-audit", desc: "Skip bundle:audit/update during release rake checks"
445
+ 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"
410
446
  option :no_auto_floors, long: "--no-auto-floors", desc: "Do not raise family dependency floors between member releases" do
411
447
  options[:no_auto_floors] = true
412
448
  end
@@ -428,6 +464,7 @@ module Kettle
428
464
  release_continue_ci_failures: truthy_option?(:continue_ci_failures),
429
465
  release_ci_workflows: options[:ci_workflows],
430
466
  release_skip_bundle_audit: truthy_option?(:skip_bundle_audit),
467
+ release_skip_remotes: options[:skip_remotes],
431
468
  release_auto_dependency_floors: !truthy_option?(:no_auto_floors),
432
469
  accept: !options.key?(:accept) || options[:accept],
433
470
  tag: truthy_option?(:tag),
@@ -448,6 +485,12 @@ module Kettle
448
485
  description "Plan or execute git pull --rebase per member."
449
486
  end
450
487
 
488
+ class Sync < WorkflowCommand
489
+ command_name "sync"
490
+ usage "[options]"
491
+ description "Plan or execute default-branch fetch/rebase and branch rebase per member."
492
+ end
493
+
451
494
  class Up < WorkflowCommand
452
495
  command_name "up"
453
496
  usage "[options]"
@@ -468,14 +511,17 @@ module Kettle
468
511
  command Bupb
469
512
  command Bex
470
513
  command Install
514
+ command Bump
471
515
  command BumpVersion
472
516
  command AddChangelog
473
517
  command Release
474
518
  command Push
475
519
  command Pull
520
+ command Sync
476
521
  command Up
477
522
  command BranchLanes
478
523
  command ReleaseState
524
+ command State
479
525
 
480
526
  prepend ReturningMain
481
527
 
@@ -514,7 +560,8 @@ module Kettle
514
560
  else
515
561
  Orderer.new(members: members, mode: config.order_mode, hints: config.order_hints).ordered
516
562
  end
517
- selected = Selection.new(members: ordered).apply(only: options[:only], exclude: options[:exclude], start_at: start_at.member)
563
+ release_state_results = release_state_results_for_selection(config: config, members: ordered, only: options[:only])
564
+ selected = Selection.new(members: ordered, release_state_results: release_state_results).apply(only: options[:only], exclude: options[:exclude], start_at: start_at.member)
518
565
  result_members = selected
519
566
  results = command_results(command: command, config: config, members: result_members, options: options, start_at: start_at)
520
567
  Report.new(
@@ -542,8 +589,14 @@ module Kettle
542
589
  command_results_for_current_branch(command: command, config: config, members: members, options: options, start_at: start_at)
543
590
  end
544
591
 
592
+ def release_state_results_for_selection(config:, members:, only:)
593
+ return nil unless only.to_s.split(",").map(&:strip).any? { |token| Selection.status_token?(token) }
594
+
595
+ ReleaseStateCheck.new(config: config, members: members).results
596
+ end
597
+
545
598
  def command_results_for_current_branch(command:, config:, members:, options:, start_at: StartAt.new(nil, nil))
546
- return bump_version_results(members: members, options: options) if command == "bump-version"
599
+ return bump_version_results(members: members, options: options, phase: command) if %w[bump bump-version].include?(command)
547
600
  return add_changelog_results(members: members, options: options) if command == "add-changelog"
548
601
  return branch_lane_results(config: config, members: members) if command == "branch-lanes"
549
602
  return release_state_results(config: config, members: members) if command == "release-state"
@@ -567,11 +620,13 @@ module Kettle
567
620
  continue_ci_failures: options[:release_continue_ci_failures],
568
621
  ci_workflows: options[:release_ci_workflows],
569
622
  skip_bundle_audit: options[:release_skip_bundle_audit],
623
+ skip_remotes: options[:release_skip_remotes],
570
624
  auto_dependency_floors: options[:release_auto_dependency_floors],
571
625
  gha_sha_pins_upgrade: options[:gha_sha_pins_upgrade],
572
626
  gha_sha_pins_check: options[:check],
573
627
  env_overrides: options[:workflow_env],
574
628
  debug: options[:debug],
629
+ verbose: options[:verbose],
575
630
  jobs: options[:jobs],
576
631
  progress_io: progress_io(command, options),
577
632
  bup_args: options[:bup_args],
@@ -593,14 +648,14 @@ module Kettle
593
648
  return false if config.release_target_branches.empty?
594
649
  return false if command == "release-state"
595
650
  return false if command == "branch-lanes"
596
- return false unless WORKFLOW_COMMANDS.include?(command) || %w[bump-version install add-changelog].include?(command)
651
+ return false unless WORKFLOW_COMMANDS.include?(command) || %w[bump bump-version install add-changelog].include?(command)
597
652
 
598
653
  !WORKFLOW_COMMANDS.include?(command)
599
654
  end
600
655
 
601
656
  def member_local_branch_target_command?(command, config, members)
602
657
  return false if !config.release_target_branches.empty?
603
- return false unless %w[bump-version install add-changelog].include?(command)
658
+ return false unless %w[bump bump-version install add-changelog].include?(command)
604
659
 
605
660
  members.any? { |member| member_release_config(member: member, config: config) }
606
661
  end
@@ -686,14 +741,15 @@ module Kettle
686
741
  StartAt.new(member, branch)
687
742
  end
688
743
 
689
- def bump_version_results(members:, options:)
744
+ def bump_version_results(members:, options:, phase:)
690
745
  require_relative "version_bump"
691
746
 
692
747
  results = VersionBump.new(
693
748
  members: members,
694
749
  target_version: options[:target_version],
695
750
  from_version: options[:from_version],
696
- mode: bump_version_mode(options)
751
+ mode: bump_version_mode(options),
752
+ phase: phase
697
753
  ).results
698
754
  return results if options[:check] || !options[:commit]
699
755
  return results unless results.all?(&:ok?)
@@ -111,7 +111,7 @@ module Kettle
111
111
  @otp_coordinator = otp_coordinator
112
112
  end
113
113
 
114
- def call(member:, phase:, command:, env: {}, interactive: false)
114
+ def call(member:, phase:, command:, env: {}, interactive: false, stdout_line_handler: nil)
115
115
  argv = command_argv(member: member, command: command, env: env)
116
116
  process_env = process_env(member: member, env: env)
117
117
  spawn_options = process_options
@@ -120,6 +120,8 @@ module Kettle
120
120
  started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
121
121
  stdout, stderr, status = if interactive
122
122
  run_interactive(env: process_env, argv: argv, chdir: member.root, member_name: member.name, process_options: spawn_options)
123
+ elsif stdout_line_handler
124
+ run_streaming(env: process_env, argv: argv, chdir: member.root, process_options: spawn_options, stdout_line_handler: stdout_line_handler)
123
125
  else
124
126
  Open3.capture3(process_env, *argv, chdir: member.root, **spawn_options)
125
127
  end
@@ -145,6 +147,41 @@ module Kettle
145
147
 
146
148
  attr_reader :execute, :accept, :gem_signing_password, :otp_coordinator
147
149
 
150
+ def run_streaming(env:, argv:, chdir:, process_options:, stdout_line_handler:)
151
+ captured_stdout = +""
152
+ captured_stderr = +""
153
+ stdout_line_buffer = +""
154
+ status = nil
155
+ Open3.popen3(env, *argv, chdir: chdir, **process_options) do |_input, output, error, wait_thread|
156
+ readers = [output, error]
157
+ until readers.empty?
158
+ ready = IO.select(readers)
159
+ ready.first.each do |reader|
160
+ if reader.equal?(output)
161
+ chunk = reader.readpartial(1024)
162
+ captured_stdout << chunk
163
+ stdout_line_buffer = stream_stdout_lines(stdout_line_buffer, chunk, stdout_line_handler)
164
+ else
165
+ captured_stderr << reader.readpartial(1024)
166
+ end
167
+ rescue EOFError
168
+ readers.delete(reader)
169
+ end
170
+ end
171
+ stdout_line_handler.call(stdout_line_buffer) unless stdout_line_buffer.empty?
172
+ status = wait_thread.value
173
+ end
174
+ [captured_stdout, captured_stderr, status]
175
+ end
176
+
177
+ def stream_stdout_lines(buffer, chunk, handler)
178
+ pending = buffer + chunk
179
+ lines = pending.lines
180
+ remainder = pending.end_with?("\n") ? +"" : lines.pop.to_s
181
+ lines.each { |line| handler.call(line.chomp) }
182
+ remainder
183
+ end
184
+
148
185
  def run_interactive(env:, argv:, chdir:, member_name:, process_options:)
149
186
  return run_interactive_pty(env: env, argv: argv, chdir: chdir, member_name: member_name, process_options: process_options) if pty_available?
150
187
 
@@ -9,6 +9,8 @@ module Kettle
9
9
  DEFAULT_MEMBER_EXCLUDES = [
10
10
  "vendor/**",
11
11
  "**/vendor/**",
12
+ "benchmarks/**",
13
+ "**/benchmarks/**",
12
14
  "tmp/**",
13
15
  "**/tmp/**",
14
16
  "spec/**",
@@ -190,6 +192,13 @@ module Kettle
190
192
  fetch_path("template", "jobs")
191
193
  end
192
194
 
195
+ def readme_corporate_sponsors
196
+ raw = fetch_path("readme", "corporate_sponsors") ||
197
+ fetch_path("sponsorships", "corporate") ||
198
+ fetch_path("corporate_sponsors")
199
+ normalize_corporate_sponsors(raw)
200
+ end
201
+
193
202
  def normalize_lockfiles?
194
203
  fetch_path("template", "normalize_lockfiles") == true
195
204
  end
@@ -278,9 +287,9 @@ module Kettle
278
287
  def default_release_disable_local_path_env
279
288
  local_envs = %w[
280
289
  K_JEM_TEMPLATING
281
- SMORG_RB_DEV
290
+ STRUCTUREDMERGE_DEV
282
291
  TSLP_DEV
283
- KETTLE_RB_DEV
292
+ KETTLE_DEV_DEV
284
293
  RUBOCOP_LTS_DEV
285
294
  PBOLING_DEV
286
295
  GALTZO_FLOSS_DEV
@@ -306,6 +315,28 @@ module Kettle
306
315
  .select { |path| File.directory?(path) }
307
316
  end
308
317
 
318
+ def normalize_corporate_sponsors(raw)
319
+ Array(raw).filter_map do |entry|
320
+ unless entry.is_a?(Hash)
321
+ raise ArgumentError, "corporate sponsor entries must be mappings with name, url, and img_src"
322
+ end
323
+
324
+ sponsor = stringify_keys(entry)
325
+ name = sponsor["name"].to_s.strip
326
+ url = sponsor["url"].to_s.strip
327
+ img_src = sponsor["img_src"].to_s.strip
328
+ if name.empty? || url.empty? || img_src.empty?
329
+ raise ArgumentError, "corporate sponsor entries require name, url, and img_src"
330
+ end
331
+
332
+ {
333
+ "name" => name,
334
+ "url" => url,
335
+ "img_src" => img_src
336
+ }
337
+ end
338
+ end
339
+
309
340
  def fetch_path(*keys)
310
341
  keys.reduce(data) do |memo, key|
311
342
  break nil unless memo.is_a?(Hash)