kettle-family 1.3.36 → 1.3.37
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 +26 -1
- data/README.md +1 -1
- data/lib/kettle/family/cli.rb +19 -2
- data/lib/kettle/family/config.rb +29 -0
- data/lib/kettle/family/release_state_check.rb +25 -8
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +156 -8
- data.tar.gz.sig +0 -0
- metadata +8 -8
- metadata.gz.sig +1 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b5f2d0bb8696a55925ff46bffa665da140b2f861e47eab151769c9522d160385
|
|
4
|
+
data.tar.gz: e64870163a6fa68862654f72c68ba492382fb8e637d9d1b91c9fb92f4c73dfb5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 940c6a65b81be4855325eae21951069520d5ac8ae0e1385e5f31d0060256ce4aed0a81c2baa2e9ec335f010119d0ebdb211d113297c6b7620a5884da637acac5
|
|
7
|
+
data.tar.gz: 896a7fed86b1e22518425858c99720c93ae1740b5b1cc32eb7639212fc691313ae9e619dfb3c7c60186454f32ff4624f02427feda30137e860b6545a40e05666
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,29 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [1.3.37] - 2026-10-30
|
|
34
|
+
|
|
35
|
+
- TAG: [v1.3.37][1.3.37t]
|
|
36
|
+
- COVERAGE: 93.13% -- 7665/8230 lines in 45 files
|
|
37
|
+
- BRANCH COVERAGE: 77.52% -- 3076/3968 branches in 45 files
|
|
38
|
+
- 39.41% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- Release preflight now blocks on bootstrap dependency drift: a new `bootstrap dependency drift` phase compares each selected member's committed Gemfile.lock against the newest published version satisfying the member's declared floor for bootstrap-managed gems (`release.drift_check.gems`, default `[nomono]`), failing with a `bundle update <gem>` / family template sweep remediation hint; registry lookup failures skip non-fatally instead of blocking.
|
|
43
|
+
|
|
44
|
+
- New family config release.version_series.independent_members: members that keep their own monotonic version series in a shared-version family. They are skipped by lockstep shared-version raises and exempt from the aggregate GitHub Release same-version invariant, while their built artifacts still attach to the aggregate release.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- kettle-family bump now raises shared-changelog members to the family's next shared version when their release is pending, including a misaligned cohort that previously only aligned to the current maximum and reported ok, leaving the unreleased-entries bump gate uncleared with no documented command able to clear it (#17).
|
|
49
|
+
|
|
50
|
+
- Independently versioned members (release.version_series.independent_members) can no longer be captured by the lockstep shared-version raise: they bump on their own series, are never force-marked pending by the shared cohort, their higher own-series version no longer drags the cohort line, and the aggregate GitHub Release no longer fails its same-version check on them (#18).
|
|
51
|
+
|
|
52
|
+
### Security
|
|
53
|
+
|
|
54
|
+
- Generated resume commands no longer embed secret env values: secret-shaped keys (at minimum `KETTLE_RELEASE_GEM_SIGNING_PASSPHRASE`) are redacted to the shell-safe placeholder `<redacted-reauthorize>`; re-authorize the secret through the release secrets provider (1Password or the `KETTLE_RELEASE_SECRETS_BROKER` cache), or paste the real value over the placeholder, before resuming.
|
|
55
|
+
|
|
33
56
|
## [1.3.36] - 2026-10-28
|
|
34
57
|
|
|
35
58
|
- TAG: [v1.3.36][1.3.36t]
|
|
@@ -3320,7 +3343,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
3320
3343
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
3321
3344
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
3322
3345
|
|
|
3323
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.3.
|
|
3346
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.3.37...HEAD
|
|
3347
|
+
[1.3.37]: https://github.com/kettle-dev/kettle-family/compare/v1.3.36...v1.3.37
|
|
3348
|
+
[1.3.37t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.3.37
|
|
3324
3349
|
[1.3.36]: https://github.com/kettle-dev/kettle-family/compare/v1.3.35...v1.3.36
|
|
3325
3350
|
[1.3.36t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.3.36
|
|
3326
3351
|
[1.3.35]: https://github.com/kettle-dev/kettle-family/compare/v1.3.34...v1.3.35
|
data/README.md
CHANGED
|
@@ -1191,7 +1191,7 @@ Thanks for RTFM. ☺️
|
|
|
1191
1191
|
[📌gitmoji]: https://gitmoji.dev
|
|
1192
1192
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1193
1193
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1194
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-8.
|
|
1194
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-8.230-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1195
1195
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
1196
1196
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1197
1197
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/lib/kettle/family/cli.rb
CHANGED
|
@@ -1475,19 +1475,29 @@ module Kettle
|
|
|
1475
1475
|
def shared_bump_member_names(config:, members:)
|
|
1476
1476
|
return [] unless config.shared_changelog?
|
|
1477
1477
|
|
|
1478
|
-
members
|
|
1478
|
+
members
|
|
1479
|
+
.reject { |member| config.member_local_changelog?(member) }
|
|
1480
|
+
.reject { |member| config.independent_version_series_member?(member) }
|
|
1481
|
+
.map(&:name)
|
|
1479
1482
|
end
|
|
1480
1483
|
|
|
1481
1484
|
def shared_bump_target_version(config:, members:, options:, release_state_results: nil)
|
|
1482
1485
|
return unless config.shared_changelog?
|
|
1483
1486
|
return unless Kettle::Dev::VersionBump::BUMP_TYPES.include?(options[:target_version].to_s)
|
|
1484
1487
|
|
|
1488
|
+
# The lockstep raise cohort excludes members carrying their own
|
|
1489
|
+
# changelog AND independently versioned members: an independent
|
|
1490
|
+
# series must never be captured by (or drag) the shared version
|
|
1491
|
+
# line, in either direction.
|
|
1485
1492
|
shared_members = members.reject { |member| config.member_local_changelog?(member) }
|
|
1493
|
+
.reject { |member| config.independent_version_series_member?(member) }
|
|
1486
1494
|
return if shared_members.empty?
|
|
1487
1495
|
|
|
1488
1496
|
versions = shared_members.map { |member| Gem::Version.new(member.version.to_s) }.uniq
|
|
1489
1497
|
current = versions.max
|
|
1490
|
-
|
|
1498
|
+
# Without release-state results (explicit member selection) a
|
|
1499
|
+
# misaligned cohort still aligns to its maximum, as before.
|
|
1500
|
+
return current.to_s if versions.length > 1 && release_state_results.nil?
|
|
1491
1501
|
|
|
1492
1502
|
released_versions = Array(release_state_results).filter_map do |result|
|
|
1493
1503
|
next unless shared_members.any? { |member| member.name == result.member_name }
|
|
@@ -1502,8 +1512,15 @@ module Kettle
|
|
|
1502
1512
|
rescue ArgumentError
|
|
1503
1513
|
nil
|
|
1504
1514
|
end
|
|
1515
|
+
# Mid-prep (the cohort line already moved past the last release):
|
|
1516
|
+
# laggards align up to the in-flight version and nothing increments.
|
|
1505
1517
|
return current.to_s if released_versions.any? && current > released_versions.max
|
|
1506
1518
|
|
|
1519
|
+
# Otherwise the cohort owes a release: every shared member is raised
|
|
1520
|
+
# to the family's NEXT shared version — including a misaligned
|
|
1521
|
+
# cohort, which previously aligned to the current maximum and then
|
|
1522
|
+
# reported ok, leaving the unreleased-entries bump gate uncleared
|
|
1523
|
+
# with no documented command able to clear it (#17).
|
|
1507
1524
|
Kettle::Dev::VersionBump.resolve_target_version(options[:target_version].to_s, current.to_s)
|
|
1508
1525
|
end
|
|
1509
1526
|
|
data/lib/kettle/family/config.rb
CHANGED
|
@@ -308,6 +308,17 @@ module Kettle
|
|
|
308
308
|
end.compact
|
|
309
309
|
end
|
|
310
310
|
|
|
311
|
+
# Bootstrap-managed gems whose committed member locks must not lag the
|
|
312
|
+
# newest published version satisfying the member's declared requirement
|
|
313
|
+
# (release.drift_check.gems; default: nomono, the bootstrap gem whose
|
|
314
|
+
# lock/registry drift killed the SM-ruby wave on 2026-10-30). An empty
|
|
315
|
+
# list disables the drift preflight phase entirely.
|
|
316
|
+
def release_drift_check_gems
|
|
317
|
+
configured = fetch_path("release", "drift_check", "gems")
|
|
318
|
+
gems = configured.nil? ? [NomonoBootstrap::GEM_NAME] : Array(configured)
|
|
319
|
+
gems.map(&:to_s).map(&:strip).reject(&:empty?).uniq
|
|
320
|
+
end
|
|
321
|
+
|
|
311
322
|
def release_required_remotes
|
|
312
323
|
fetch_path("release", "required_remotes")
|
|
313
324
|
end
|
|
@@ -405,6 +416,24 @@ module Kettle
|
|
|
405
416
|
fetch_path("release", "aggregate_validation", "enabled") == true
|
|
406
417
|
end
|
|
407
418
|
|
|
419
|
+
# Members that keep their own monotonic version series even in a
|
|
420
|
+
# shared-version family (release.version_series.independent_members;
|
|
421
|
+
# default empty). An independent-series member is skipped by lockstep
|
|
422
|
+
# shared-version raises — its bumps stay on its own series — and is
|
|
423
|
+
# exempt from the aggregate GitHub Release same-version invariant,
|
|
424
|
+
# while its built artifacts still attach to the aggregate release.
|
|
425
|
+
def release_version_series_independent_members
|
|
426
|
+
Array(fetch_path("release", "version_series", "independent_members"))
|
|
427
|
+
.map(&:to_s)
|
|
428
|
+
.map(&:strip)
|
|
429
|
+
.reject(&:empty?)
|
|
430
|
+
.uniq
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def independent_version_series_member?(member)
|
|
434
|
+
release_version_series_independent_members.include?(member.name.to_s)
|
|
435
|
+
end
|
|
436
|
+
|
|
408
437
|
def release_aggregate_validation_excluded_members
|
|
409
438
|
Array(fetch_path("release", "aggregate_validation", "exclude_members"))
|
|
410
439
|
.map(&:to_s)
|
|
@@ -830,8 +830,9 @@ module Kettle
|
|
|
830
830
|
end
|
|
831
831
|
|
|
832
832
|
# Version-locked siblings move in lockstep. Members not declared
|
|
833
|
-
# independently releasable (release.aggregate_validation.exclude_members
|
|
834
|
-
# share the family
|
|
833
|
+
# independently releasable (release.aggregate_validation.exclude_members
|
|
834
|
+
# or release.version_series.independent_members) share the family
|
|
835
|
+
# version line, so:
|
|
835
836
|
# 1. when any of them needs a version bump, all of them do — including
|
|
836
837
|
# members without unreleased changelog entries of their own and
|
|
837
838
|
# brand-new members that have never been released (empty
|
|
@@ -839,12 +840,13 @@ module Kettle
|
|
|
839
840
|
# 2. a member whose checkout version sits behind the cohort's version
|
|
840
841
|
# line (e.g. a cohort that already bumped while it stayed put) needs
|
|
841
842
|
# a catch-up bump on its own.
|
|
842
|
-
# Independently
|
|
843
|
-
# bump only when their own unreleased entries require it
|
|
844
|
-
#
|
|
845
|
-
#
|
|
843
|
+
# Independently versioned members keep per-member bump semantics: they
|
|
844
|
+
# bump only when their own unreleased entries require it, and their
|
|
845
|
+
# (possibly higher) own-series version never drags the cohort line.
|
|
846
|
+
# Members whose checkout version already moved past their last release
|
|
847
|
+
# are mid-prep and are never double-bumped.
|
|
846
848
|
def normalize_lockstep_version_bump(results)
|
|
847
|
-
excluded =
|
|
849
|
+
excluded = independent_bump_member_names
|
|
848
850
|
return results if excluded.empty?
|
|
849
851
|
|
|
850
852
|
aggregate = results.select do |result|
|
|
@@ -894,6 +896,17 @@ module Kettle
|
|
|
894
896
|
nil
|
|
895
897
|
end
|
|
896
898
|
|
|
899
|
+
# Members exempt from lockstep bump semantics: aggregate-validation
|
|
900
|
+
# exclusions plus independently versioned members. Both keep per-member
|
|
901
|
+
# bump behavior; neither may be force-marked pending by the cohort, and
|
|
902
|
+
# an independent-series version never participates in the cohort line.
|
|
903
|
+
def independent_bump_member_names
|
|
904
|
+
Array(config&.release_aggregate_validation_excluded_members)
|
|
905
|
+
.concat(Array(config&.release_version_series_independent_members))
|
|
906
|
+
.map(&:to_s)
|
|
907
|
+
.uniq
|
|
908
|
+
end
|
|
909
|
+
|
|
897
910
|
def normalize_shared_version_bump(results)
|
|
898
911
|
shared_results = results.select { |result| result.ok? && shared_changelog_member_result?(result) }
|
|
899
912
|
return results unless shared_results.any? { |result| result.state["bump_release_pending"] == true }
|
|
@@ -919,7 +932,11 @@ module Kettle
|
|
|
919
932
|
|
|
920
933
|
def shared_changelog_member_result?(result)
|
|
921
934
|
member = members.find { |candidate| candidate.name == result.member_name }
|
|
922
|
-
member && !member_local_changelog?(member)
|
|
935
|
+
member && !member_local_changelog?(member) && !independent_version_series_member?(member)
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
def independent_version_series_member?(member)
|
|
939
|
+
config&.independent_version_series_member?(member) || false
|
|
923
940
|
end
|
|
924
941
|
|
|
925
942
|
def git_root
|
|
@@ -5,7 +5,7 @@ module Kettle
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = "1.3.
|
|
8
|
+
VERSION = "1.3.37"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
|
@@ -108,6 +108,16 @@ module Kettle
|
|
|
108
108
|
REGISTRY_WAIT_ATTEMPTS = 15
|
|
109
109
|
REGISTRY_WAIT_INTERVAL_SECONDS = 15
|
|
110
110
|
TEMPLATE_GEMSPEC_RETRY_DELAY_SECONDS = 0.25
|
|
111
|
+
# Placeholder substituted for secret-valued env keys in generated resume
|
|
112
|
+
# commands (see aggregate_github_release_resume_command).
|
|
113
|
+
RESUME_COMMAND_REDACTED_VALUE = "<redacted-reauthorize>"
|
|
114
|
+
# Env key names whose values must never appear in printed commands.
|
|
115
|
+
# Matches by name shape, not a fixed list, so future secrets plumbing
|
|
116
|
+
# (new passphrase/OTP/token transports) is scrubbed by default. The
|
|
117
|
+
# excluded suffixes are routing metadata an operator needs to
|
|
118
|
+
# re-authorize (source/provider/broker/field NAME), never a secret.
|
|
119
|
+
RESUME_COMMAND_SECRET_ENV_KEY_RE = /PASSPHRASE|PASSWORD|SECRET|TOKEN|OTP|CREDENTIAL/i
|
|
120
|
+
RESUME_COMMAND_SECRET_ENV_KEY_EXCLUDE_RE = /(?:_SOURCE|_PROVIDER|_BROKER|_FIELD)\z/i
|
|
111
121
|
|
|
112
122
|
def initialize(command:, config:, members:, family_members: nil, execute: false, accept: true, commit: true, allow_dirty: false, autostash: true, template_cleanup: true, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, fast_recovery: nil, fast_recovery_members: nil, skip_ci: false, skip_changelog: false, skip_appraisals: false, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, validate_ci_bundles: nil, gha_sha_pins_upgrade: "patch", gha_sha_pins_check: false, gha_sha_pins_ttl_days: nil, env_overrides: {}, debug: false, verbose: false, gem_signing_password: nil, secrets_provider: nil, jobs: nil, progress_io: nil, reset_target: nil, bup_args: [], bex_args: [], start_member: nil, start_branch: nil, template_branch_selection: {}, release_state_results: nil, event_tape: nil, mise_adapter: nil, **options)
|
|
113
123
|
@command = command
|
|
@@ -2024,12 +2034,13 @@ module Kettle
|
|
|
2024
2034
|
def release_preflight_phases
|
|
2025
2035
|
maintenance_phase = {label: "pre-release maintenance", method: :release_preflight_maintenance_results} unless config.release_preflight_commands.empty?
|
|
2026
2036
|
bump_phase = {label: "release version bump", method: :release_preflight_version_bump_results} unless release_state_results.empty?
|
|
2037
|
+
drift_phase = {label: "bootstrap dependency drift", method: :release_preflight_bootstrap_drift_results} unless config.release_drift_check_gems.empty?
|
|
2027
2038
|
dirty_phase = {label: "branch checkout readiness", method: :release_preflight_branch_checkout_dirty_results}
|
|
2028
2039
|
signing_phase = {label: "gem signing password", method: :release_preflight_signing_password_results}
|
|
2029
2040
|
secrets_phase = release_secrets_authorization_required? ? {label: "secrets provider authorization", method: :release_preflight_secrets_authorization_results} : nil
|
|
2030
2041
|
gha_sha_pins_phase = release_gha_sha_pins_review_required? ? {label: "GitHub Actions SHA pins", method: :release_preflight_gha_sha_pins_results} : nil
|
|
2031
2042
|
|
|
2032
|
-
[maintenance_phase, bump_phase, secrets_phase, gha_sha_pins_phase, dirty_phase, signing_phase].compact
|
|
2043
|
+
[maintenance_phase, bump_phase, drift_phase, secrets_phase, gha_sha_pins_phase, dirty_phase, signing_phase].compact
|
|
2033
2044
|
end
|
|
2034
2045
|
|
|
2035
2046
|
# Family-declared pre-release maintenance hooks (release.preflight_commands
|
|
@@ -2075,6 +2086,108 @@ module Kettle
|
|
|
2075
2086
|
results
|
|
2076
2087
|
end
|
|
2077
2088
|
|
|
2089
|
+
# Bootstrap-managed gems (config release.drift_check.gems, default:
|
|
2090
|
+
# nomono) are activated by a member's own Gemfile bootstrap, while CI
|
|
2091
|
+
# resolves fresh bundles from the registry. When a newer published
|
|
2092
|
+
# version satisfies the member's declared floor but the committed lock
|
|
2093
|
+
# pins an older one, CI activates the newer version while the member
|
|
2094
|
+
# bootstrap activates the locked older one, and the wave dies mid-job
|
|
2095
|
+
# with a Gem::LoadError deep in a cold-start (observed 2026-10-30:
|
|
2096
|
+
# nomono locked 1.1.16 vs published 1.1.17 satisfying a ">= 1.1.16"
|
|
2097
|
+
# floor, run 40998206316). Nothing compared locked vs
|
|
2098
|
+
# published-satisfying versions pre-release, so this phase blocks early
|
|
2099
|
+
# with the remediation instead of letting CI discover the drift.
|
|
2100
|
+
def release_preflight_bootstrap_drift_results
|
|
2101
|
+
config.release_drift_check_gems.flat_map do |gem|
|
|
2102
|
+
members.filter_map { |member| bootstrap_dependency_drift_result(member, gem) }
|
|
2103
|
+
end
|
|
2104
|
+
end
|
|
2105
|
+
|
|
2106
|
+
def bootstrap_dependency_drift_result(member, gem)
|
|
2107
|
+
locked = locked_bootstrap_gem_version(member, gem)
|
|
2108
|
+
return nil if locked.nil?
|
|
2109
|
+
|
|
2110
|
+
requirement = declared_bootstrap_gem_requirement(member, gem)
|
|
2111
|
+
newest = newest_published_satisfying_version(gem, requirement)
|
|
2112
|
+
if newest.nil?
|
|
2113
|
+
# A registry outage must never become a false release block: treat
|
|
2114
|
+
# "could not consult the registry" as a skip with a diagnostic.
|
|
2115
|
+
warn "[kettle-family] bootstrap dependency drift check skipped for #{gem}: registry could not be consulted"
|
|
2116
|
+
return nil
|
|
2117
|
+
end
|
|
2118
|
+
return nil if locked >= newest
|
|
2119
|
+
|
|
2120
|
+
release_preflight_error_result(
|
|
2121
|
+
"bootstrap_dependency_drift",
|
|
2122
|
+
"Release blocked: bootstrap dependency drift: #{member.name} locks #{gem} #{locked} but #{newest} satisfies its floor; " \
|
|
2123
|
+
"run the family template sweep or `bundle update #{gem}` in the member before releasing.",
|
|
2124
|
+
member: member
|
|
2125
|
+
)
|
|
2126
|
+
end
|
|
2127
|
+
|
|
2128
|
+
# The version a member's committed lock pins for the gem, from the
|
|
2129
|
+
# registry-sourced GEM section only: a PATH or GIT pin is local-only
|
|
2130
|
+
# state that has no published counterpart to drift against.
|
|
2131
|
+
def locked_bootstrap_gem_version(member, gem)
|
|
2132
|
+
lockfile_path = File.join(member.root, "Gemfile.lock")
|
|
2133
|
+
return nil unless File.file?(lockfile_path)
|
|
2134
|
+
|
|
2135
|
+
section = nil
|
|
2136
|
+
File.foreach(lockfile_path) do |line|
|
|
2137
|
+
section = line.strip unless line.match?(/\A\s/)
|
|
2138
|
+
next unless section == "GEM"
|
|
2139
|
+
|
|
2140
|
+
match = line.match(/\A {4}#{Regexp.escape(gem)} \(([^)]+)\)\s*\z/)
|
|
2141
|
+
next unless match
|
|
2142
|
+
|
|
2143
|
+
begin
|
|
2144
|
+
return Gem::Version.new(match[1])
|
|
2145
|
+
rescue ArgumentError
|
|
2146
|
+
return nil
|
|
2147
|
+
end
|
|
2148
|
+
end
|
|
2149
|
+
nil
|
|
2150
|
+
end
|
|
2151
|
+
|
|
2152
|
+
# The requirement the member's Gemfile declared for the gem, read from
|
|
2153
|
+
# the lock's DEPENDENCIES section (Bundler records the DSL requirement
|
|
2154
|
+
# there verbatim). A bare declaration means "any version".
|
|
2155
|
+
def declared_bootstrap_gem_requirement(member, gem)
|
|
2156
|
+
lockfile_path = File.join(member.root, "Gemfile.lock")
|
|
2157
|
+
return Gem::Requirement.default unless File.file?(lockfile_path)
|
|
2158
|
+
|
|
2159
|
+
section = nil
|
|
2160
|
+
File.foreach(lockfile_path) do |line|
|
|
2161
|
+
section = line.strip unless line.match?(/\A\s/)
|
|
2162
|
+
next unless section == "DEPENDENCIES"
|
|
2163
|
+
|
|
2164
|
+
match = line.match(/\A {2}#{Regexp.escape(gem)}(?: \((.+)\))?\s*\z/)
|
|
2165
|
+
next unless match
|
|
2166
|
+
|
|
2167
|
+
constraints = (match[1] || "").split(",").map(&:strip).reject(&:empty?)
|
|
2168
|
+
return Gem::Requirement.new(constraints) unless constraints.empty?
|
|
2169
|
+
|
|
2170
|
+
return Gem::Requirement.default
|
|
2171
|
+
end
|
|
2172
|
+
Gem::Requirement.default
|
|
2173
|
+
end
|
|
2174
|
+
|
|
2175
|
+
# Newest published (non-prerelease) version satisfying the requirement,
|
|
2176
|
+
# via the ETag-cached registry lookup shared with the rest of the
|
|
2177
|
+
# toolchain; nil when the registry could not be consulted.
|
|
2178
|
+
def newest_published_satisfying_version(gem, requirement)
|
|
2179
|
+
numbers = Kettle::Dev::RubyGemsVersions.published_version_numbers(gem)
|
|
2180
|
+
return nil if numbers.nil?
|
|
2181
|
+
|
|
2182
|
+
numbers.filter_map do |number|
|
|
2183
|
+
Gem::Version.new(number)
|
|
2184
|
+
rescue ArgumentError
|
|
2185
|
+
nil
|
|
2186
|
+
end.select do |version|
|
|
2187
|
+
!version.prerelease? && requirement.satisfied_by?(version)
|
|
2188
|
+
end.max
|
|
2189
|
+
end
|
|
2190
|
+
|
|
2078
2191
|
def release_gha_sha_pins_review_required?
|
|
2079
2192
|
execute && kettle_release_command?(raw_release_command) && !release_gha_sha_pins_offline_requested?
|
|
2080
2193
|
end
|
|
@@ -2119,11 +2232,14 @@ module Kettle
|
|
|
2119
2232
|
@secrets_provider.is_a?(Secrets::OnePassword)
|
|
2120
2233
|
end
|
|
2121
2234
|
|
|
2122
|
-
def release_preflight_error_result(phase, message)
|
|
2235
|
+
def release_preflight_error_result(phase, message, member: nil)
|
|
2123
2236
|
# A preflight failure blocks the selected release invocation. Attribute
|
|
2124
2237
|
# it to that invocation's first member so generated resume commands do
|
|
2125
|
-
# not target the synthetic family name or an unselected sibling.
|
|
2126
|
-
member
|
|
2238
|
+
# not target the synthetic family name or an unselected sibling. Phases
|
|
2239
|
+
# that identify a specific offending member (e.g. bootstrap dependency
|
|
2240
|
+
# drift) override that attribution so the resume hint targets the
|
|
2241
|
+
# member that needs the fix.
|
|
2242
|
+
member ||= members.first || family_member
|
|
2127
2243
|
CommandResult.new(
|
|
2128
2244
|
member_name: member.name,
|
|
2129
2245
|
phase: phase,
|
|
@@ -3889,6 +4005,16 @@ module Kettle
|
|
|
3889
4005
|
aggregate_validation_members.include?(member)
|
|
3890
4006
|
end
|
|
3891
4007
|
|
|
4008
|
+
# An independently versioned member (release.version_series.
|
|
4009
|
+
# independent_members) keeps its own monotonic series: it is exempt
|
|
4010
|
+
# from lockstep raises and from the aggregate same-version invariant,
|
|
4011
|
+
# but remains part of the aggregate release for artifact attachment.
|
|
4012
|
+
def independent_version_series_member?(member)
|
|
4013
|
+
return false unless member
|
|
4014
|
+
|
|
4015
|
+
config.independent_version_series_member?(member)
|
|
4016
|
+
end
|
|
4017
|
+
|
|
3892
4018
|
def aggregate_validation_member_name?(member)
|
|
3893
4019
|
member && member.name == (@aggregate_validation_member_name || aggregate_validation_members.first&.name)
|
|
3894
4020
|
end
|
|
@@ -4109,12 +4235,19 @@ module Kettle
|
|
|
4109
4235
|
|
|
4110
4236
|
# simplecov:disable Covered by monorepo release integration, not sibling-repository suite.
|
|
4111
4237
|
def aggregate_monorepo_github_release(members)
|
|
4112
|
-
version
|
|
4238
|
+
# The same-version invariant describes the shared-version cohort.
|
|
4239
|
+
# Independently versioned members (release.version_series.
|
|
4240
|
+
# independent_members) can never satisfy it by design, so they are
|
|
4241
|
+
# exempt from the uniformity check while their artifacts still
|
|
4242
|
+
# attach — at their own series version, not the cohort's.
|
|
4243
|
+
shared_members = members.reject { |member| independent_version_series_member?(member) }
|
|
4244
|
+
version = shared_members.map { |member| member.version.to_s }.uniq
|
|
4113
4245
|
return aggregate_release_result("all selected members must have the same version") unless version.length == 1
|
|
4114
4246
|
|
|
4115
4247
|
assets = members.flat_map do |member|
|
|
4116
|
-
|
|
4117
|
-
|
|
4248
|
+
asset_version = independent_version_series_member?(member) ? member.version.to_s : version.first
|
|
4249
|
+
gem_path = Paths.glob(member.root, "pkg", "*.gem").select { |path| File.basename(path).end_with?("-#{asset_version}.gem") }
|
|
4250
|
+
checksum_paths = %w[sha256 sha512].map { |algorithm| File.join(member.root, "checksums", "#{member.name}-#{asset_version}.gem.#{algorithm}") }
|
|
4118
4251
|
gem_path + checksum_paths.select { |path| File.file?(path) }
|
|
4119
4252
|
end
|
|
4120
4253
|
command = ["bundle", "exec", "kettle-gh-release", "--allow-unpublished", "--events", "--release-version", version.first]
|
|
@@ -4159,11 +4292,26 @@ module Kettle
|
|
|
4159
4292
|
|
|
4160
4293
|
def aggregate_github_release_resume_command(env, command)
|
|
4161
4294
|
assignments = env.sort_by { |key, _value| key }.filter_map do |key, value|
|
|
4162
|
-
|
|
4295
|
+
next if value.nil?
|
|
4296
|
+
|
|
4297
|
+
# Resume commands land in terminal output, run summaries, and
|
|
4298
|
+
# transcripts. Env built from the broker-cached secrets used to
|
|
4299
|
+
# embed KETTLE_RELEASE_GEM_SIGNING_PASSPHRASE in plaintext here
|
|
4300
|
+
# (2026-10-30 incident). Redact secret-valued keys instead: the
|
|
4301
|
+
# printed command keeps its shape, and the operator re-authorizes
|
|
4302
|
+
# the secret through the release secrets provider (1Password, or
|
|
4303
|
+
# the KETTLE_RELEASE_SECRETS_BROKER cache) — or pastes the real
|
|
4304
|
+
# passphrase in place of the placeholder — before resuming.
|
|
4305
|
+
rendered_value = resume_command_secret_env_key?(key) ? Shellwords.escape(RESUME_COMMAND_REDACTED_VALUE) : Shellwords.escape(value.to_s)
|
|
4306
|
+
"#{key}=#{rendered_value}"
|
|
4163
4307
|
end
|
|
4164
4308
|
(["env", *assignments].join(" ") + " " + Shellwords.join(command)).strip
|
|
4165
4309
|
end
|
|
4166
4310
|
|
|
4311
|
+
def resume_command_secret_env_key?(key)
|
|
4312
|
+
key.match?(RESUME_COMMAND_SECRET_ENV_KEY_RE) && !key.match?(RESUME_COMMAND_SECRET_ENV_KEY_EXCLUDE_RE)
|
|
4313
|
+
end
|
|
4314
|
+
|
|
4167
4315
|
def aggregate_github_release_event_line_handler(member)
|
|
4168
4316
|
lambda do |line|
|
|
4169
4317
|
event = parse_template_event(line)
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kettle-family
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.37
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -234,7 +234,7 @@ dependencies:
|
|
|
234
234
|
version: '0.1'
|
|
235
235
|
- - ">="
|
|
236
236
|
- !ruby/object:Gem::Version
|
|
237
|
-
version: 0.1.
|
|
237
|
+
version: 0.1.23
|
|
238
238
|
type: :runtime
|
|
239
239
|
prerelease: false
|
|
240
240
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -244,7 +244,7 @@ dependencies:
|
|
|
244
244
|
version: '0.1'
|
|
245
245
|
- - ">="
|
|
246
246
|
- !ruby/object:Gem::Version
|
|
247
|
-
version: 0.1.
|
|
247
|
+
version: 0.1.23
|
|
248
248
|
- !ruby/object:Gem::Dependency
|
|
249
249
|
name: kettle-test
|
|
250
250
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -254,7 +254,7 @@ dependencies:
|
|
|
254
254
|
version: '2.0'
|
|
255
255
|
- - ">="
|
|
256
256
|
- !ruby/object:Gem::Version
|
|
257
|
-
version: 2.0.
|
|
257
|
+
version: 2.0.29
|
|
258
258
|
type: :runtime
|
|
259
259
|
prerelease: false
|
|
260
260
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -264,7 +264,7 @@ dependencies:
|
|
|
264
264
|
version: '2.0'
|
|
265
265
|
- - ">="
|
|
266
266
|
- !ruby/object:Gem::Version
|
|
267
|
-
version: 2.0.
|
|
267
|
+
version: 2.0.29
|
|
268
268
|
- !ruby/object:Gem::Dependency
|
|
269
269
|
name: psych-merge
|
|
270
270
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -628,10 +628,10 @@ licenses:
|
|
|
628
628
|
- AGPL-3.0-only
|
|
629
629
|
metadata:
|
|
630
630
|
homepage_uri: https://kettle-family.galtzo.com
|
|
631
|
-
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.3.
|
|
632
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.3.
|
|
631
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.3.37
|
|
632
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.3.37/CHANGELOG.md
|
|
633
633
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
|
|
634
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.3.
|
|
634
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.3.37
|
|
635
635
|
funding_uri: https://github.com/sponsors/pboling
|
|
636
636
|
wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
|
|
637
637
|
news_uri: https://www.railsbling.com/tags/kettle-family
|
metadata.gz.sig
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
|
|
2
|
-
aڈ.F�ܥ�}PC��P��� xX5�T���eT�9�v����!<�j�S��X|�1(2��`�\^���}����bԠ��Z�;b�I�о�f�����,
|
|
3
|
-
XY�Q�3%2x�4�2�4���W�]5�`�Po���j��݂j�ꚓ_8+�;
|
|
1
|
+
}��C�UhײlrH!s<���^&K)�^SO���j�]Ib���L���;N���m�̒2"]x(*7)�`�j&����쇜9���۟~�/?�m��+v��9�t��KV�)ߣ���V���}�e�MS�<N��T6��������b%��T�?�,_㮁2)���;p�Y F��а�18��J�[�@V���-s������.���]�t�#��va`O5YF�pz~I���Y�~?�j �.��-��8�w?"`~������s��5+�=�Sjǂ�~��2n�`�����b�6��@�=%o_�J���T,��Z֥��1�њ\Jِ��X����%[; ��N nq��b_�x�ϝ�!�.���v��&�̆M&텃
|