kettle-dev 2.3.7 → 2.3.9
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 +55 -1
- data/README.md +4 -2
- data/exe/kettle-changelog +5 -2
- data/exe/kettle-release +26 -2
- data/lib/kettle/dev/changelog_cli.rb +39 -8
- data/lib/kettle/dev/gem_coop_versions.rb +92 -0
- data/lib/kettle/dev/rakelib/appraisal.rake +1 -1
- data/lib/kettle/dev/release_cli.rb +134 -16
- data/lib/kettle/dev/version.rb +1 -1
- data/lib/kettle/dev.rb +2 -0
- data/sig/kettle/dev.rbs +1 -5
- data.tar.gz.sig +0 -0
- metadata +13 -12
- 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: 846ffb7540c0c7d69659591baf978274184f722f726aacc6e4915c0f376dbeb4
|
|
4
|
+
data.tar.gz: '08818034dfa875f33cdc89579cf455a011ecebf25be759b31af8702458e8eb6b'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0014c68a3e93d78a01c8b561956cbf474eaf25c6c8491b8ff1d229e7070eca06d3a3ed0282f739b1a3e4c38040f9bd8e5155e030a6baf1e7694a731a4bce5e2d
|
|
7
|
+
data.tar.gz: eeb028f65feddaac27a4f268a627d77fa686836bf368ca62c8b4b169acff3b43abf8aa432d96dc24341b4d27a61d74cd1197a529db1f2287c05661257f67880c
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,56 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [2.3.9] - 2026-07-21
|
|
34
|
+
|
|
35
|
+
- TAG: [v2.3.9][2.3.9t]
|
|
36
|
+
- COVERAGE: 92.06% -- 4443/4826 lines in 37 files
|
|
37
|
+
- BRANCH COVERAGE: 74.09% -- 1733/2339 branches in 37 files
|
|
38
|
+
- 54.64% documented
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- `kettle-release` now records a short-lived gem.coop release-state cache-bust
|
|
43
|
+
marker after publishing, and `kettle-changelog --release-state` uses it to
|
|
44
|
+
refresh just-published gem version lookups.
|
|
45
|
+
|
|
46
|
+
- `dep-heads.yml` now runs each engine directly from
|
|
47
|
+
`gemfiles/dep_heads.gemfile`, avoiding `Appraisal.root.gemfile` bootstrap
|
|
48
|
+
failures with RubyGems/Bundler and gem.coop before the dep-heads appraisal is
|
|
49
|
+
selected.
|
|
50
|
+
|
|
51
|
+
## [2.3.8] - 2026-07-21
|
|
52
|
+
|
|
53
|
+
- TAG: [v2.3.8][2.3.8t]
|
|
54
|
+
- COVERAGE: 91.98% -- 4379/4761 lines in 36 files
|
|
55
|
+
- BRANCH COVERAGE: 73.91% -- 1711/2315 branches in 36 files
|
|
56
|
+
- 56.04% documented
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- `kettle-changelog --release-state` now reports an `ahead` value counting
|
|
61
|
+
commits from the latest release tag to the local default branch when
|
|
62
|
+
available.
|
|
63
|
+
- `kettle-release` now accepts `--skip-remotes` and
|
|
64
|
+
`K_RELEASE_SKIP_REMOTES` to exclude temporarily unavailable git remotes from
|
|
65
|
+
release fetch/push parity checks.
|
|
66
|
+
- `kettle-release` now creates or reuses an open GitHub pull request for
|
|
67
|
+
non-trunk release branches before waiting for GitHub Actions, so
|
|
68
|
+
pull-request-only workflows start after release branch pushes.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- kettle-jem-template-20260720-001 - Generated READMEs can now render
|
|
73
|
+
template-managed corporate sponsor logos from project or family config.
|
|
74
|
+
- kettle-jem-template-20260720-002 - Generated development Gemfiles now use the
|
|
75
|
+
released `tree_sitter_language_pack` gem 1.13.3 or newer by default.
|
|
76
|
+
- kettle-jem-template-20260720-003 - Generated StructuredMerge Git diff driver
|
|
77
|
+
config now uses the installed `smorg-rb` Ruby driver name.
|
|
78
|
+
- kettle-jem-template-20260720-004 - Generated multi-engine workflow files now
|
|
79
|
+
omit JRuby and TruffleRuby jobs when project config declares MRI-only engines.
|
|
80
|
+
- kettle-jem-template-20260720-005 - Generated README Support & Community rows
|
|
81
|
+
now include a RubyForum help badge.
|
|
82
|
+
|
|
33
83
|
## [2.3.7] - 2026-07-18
|
|
34
84
|
|
|
35
85
|
- TAG: [v2.3.7][2.3.7t]
|
|
@@ -2536,7 +2586,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2536
2586
|
- Selecting will run the selected workflow via `act`
|
|
2537
2587
|
- This may move to its own gem in the future.
|
|
2538
2588
|
|
|
2539
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.
|
|
2589
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.9...HEAD
|
|
2590
|
+
[2.3.9]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.8...v2.3.9
|
|
2591
|
+
[2.3.9t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.9
|
|
2592
|
+
[2.3.8]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.7...v2.3.8
|
|
2593
|
+
[2.3.8t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.8
|
|
2540
2594
|
[2.3.7]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.6...v2.3.7
|
|
2541
2595
|
[2.3.7t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.7
|
|
2542
2596
|
[2.3.6]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.5...v2.3.6
|
data/README.md
CHANGED
|
@@ -93,7 +93,7 @@ bin/kettle-release
|
|
|
93
93
|
| Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]|
|
|
94
94
|
| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎ruby-3.0-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎ruby-3.1-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎ruby-3.2-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎ruby-3.3-wf] [![Ruby 3.4 Compat][💎ruby-3.4i]][🚎ruby-3.4-wf]|
|
|
95
95
|
| Works with MRI Ruby 2 | [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎ruby-2.4-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎ruby-2.5-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎ruby-2.6-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎ruby-2.7-wf]|
|
|
96
|
-
| 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] |
|
|
96
|
+
| 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] |
|
|
97
97
|
| 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] |
|
|
98
98
|
| 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] |
|
|
99
99
|
| 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] |
|
|
@@ -734,6 +734,8 @@ Thanks for RTFM. ☺️
|
|
|
734
734
|
[✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
|
|
735
735
|
[✉️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
|
|
736
736
|
[✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
|
|
737
|
+
[✉️ruby-forum-img]: https://img.shields.io/badge/RubyForum-Help-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
738
|
+
[✉️ruby-forum]: https://www.rubyforum.org/c/help/8
|
|
737
739
|
|
|
738
740
|
[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
|
|
739
741
|
[⛳️gem-namespace]: https://github.com/kettle-dev/kettle-dev
|
|
@@ -887,7 +889,7 @@ Thanks for RTFM. ☺️
|
|
|
887
889
|
[📌gitmoji]: https://gitmoji.dev
|
|
888
890
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
889
891
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
890
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.
|
|
892
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.826-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
891
893
|
[🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
|
|
892
894
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
893
895
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/exe/kettle-changelog
CHANGED
|
@@ -55,7 +55,7 @@ end
|
|
|
55
55
|
|
|
56
56
|
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
57
57
|
puts <<~USAGE
|
|
58
|
-
Usage: kettle-changelog [--version VERSION] [--update-prep] [--pending-release] [--release-state] [--add-unreleased-entry --section SECTION --entry TEXT] [--json] [--no-strict] [--no-coverage-threshold]
|
|
58
|
+
Usage: kettle-changelog [--version VERSION] [--update-prep] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--json] [--no-strict] [--no-coverage-threshold]
|
|
59
59
|
|
|
60
60
|
Detects the current version from lib/**/version.rb, the latest live release, and
|
|
61
61
|
the most recent CHANGELOG.md release section, then prompts to confirm the selected plan:
|
|
@@ -70,6 +70,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
70
70
|
--pending-release Query whether the changelog has release work pending; exits 0 for yes, 1 for no
|
|
71
71
|
--release-state,
|
|
72
72
|
--release-status Print changelog release state, including latest published release and pending sources
|
|
73
|
+
--refresh-cache Bypass cached gem.coop version data for this invocation
|
|
73
74
|
--add-unreleased-entry Add one entry to an existing section under ## [Unreleased]
|
|
74
75
|
--section SECTION Unreleased section to receive the entry (Added, Changed, Deprecated, Removed, Fixed, Security)
|
|
75
76
|
--entry TEXT Changelog entry text; "- " is added when omitted
|
|
@@ -82,6 +83,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
82
83
|
K_CHANGELOG_COVERAGE_HARD=false Disable coverage threshold hard-failure
|
|
83
84
|
K_CHANGELOG_VERSION_FILE=path Read VERSION from this file instead of lib/**/version.rb
|
|
84
85
|
K_CHANGELOG_GEM_NAME=name Read live release data as this gem instead of root .gemspec
|
|
86
|
+
KETTLE_GEM_COOP_REFRESH=true Bypass cached gem.coop version data
|
|
85
87
|
|
|
86
88
|
Data generation:
|
|
87
89
|
Strict mode is the default. Release plans remove stale coverage output,
|
|
@@ -131,6 +133,7 @@ begin
|
|
|
131
133
|
update_prep = ARGV.delete("--update-prep")
|
|
132
134
|
pending_release_query = ARGV.delete("--pending-release")
|
|
133
135
|
release_state_query = ARGV.delete("--release-state") || ARGV.delete("--release-status")
|
|
136
|
+
refresh_cache = ARGV.delete("--refresh-cache")
|
|
134
137
|
add_unreleased_entry = ARGV.delete("--add-unreleased-entry") || ARGV.delete("--add-changelog-entry")
|
|
135
138
|
json_output = ARGV.delete("--json")
|
|
136
139
|
coverage_threshold_disabled = ARGV.include?("--no-coverage-threshold") || ARGV.include?("--no-coverage-thresholds")
|
|
@@ -167,7 +170,7 @@ begin
|
|
|
167
170
|
exit(0)
|
|
168
171
|
end
|
|
169
172
|
|
|
170
|
-
cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, version: version_override)
|
|
173
|
+
cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, version: version_override, refresh_cache: refresh_cache)
|
|
171
174
|
if release_state_query
|
|
172
175
|
state = cli.release_state
|
|
173
176
|
puts(json_output ? JSON.pretty_generate(state) : cli.release_state_table(state))
|
data/exe/kettle-release
CHANGED
|
@@ -43,7 +43,7 @@ end
|
|
|
43
43
|
# Do not guard with __FILE__ == $PROGRAM_NAME because binstubs use Kernel.load.
|
|
44
44
|
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
45
45
|
puts <<~USAGE
|
|
46
|
-
Usage: kettle-release [--version VERSION] [--local-ci] [--appraisal-update] [--skip-bundle-audit] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [start_step=<0-19>]
|
|
46
|
+
Usage: kettle-release [--version VERSION] [--local-ci] [--appraisal-update] [--skip-bundle-audit] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [--skip-remotes REMOTES] [start_step=<0-19>]
|
|
47
47
|
|
|
48
48
|
Automates the release flow for a Ruby gem in the host project.
|
|
49
49
|
|
|
@@ -82,6 +82,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
82
82
|
--appraisal-update # Use slower appraisal:update instead of default appraisal:generate
|
|
83
83
|
--skip-bundle-audit # Skip bundle:audit/update during release rake checks
|
|
84
84
|
--ci-workflows WORKFLOWS # Comma-separated workflow files or stems to monitor at CI step 10
|
|
85
|
+
--skip-remotes REMOTES # Comma-separated git remote names to exclude from release fetch/push parity
|
|
85
86
|
--local-ci # Sensitive release mode: run act locally, publish before any git push,
|
|
86
87
|
# create the git tag locally, then push commits and tags after publish
|
|
87
88
|
|
|
@@ -91,6 +92,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
91
92
|
K_RELEASE_LOCAL_CI=ask|1|0 # Use 'act' locally before push; 'ask' prompts, '1' forces, default off
|
|
92
93
|
K_RELEASE_LOCAL_CI_WORKFLOW # Name of workflow (without .yml) for local CI; defaults to locked_deps or first
|
|
93
94
|
K_RELEASE_CI_WORKFLOWS # Comma-separated workflow files or stems to monitor at CI step 10
|
|
95
|
+
K_RELEASE_SKIP_REMOTES # Comma-separated git remote names to exclude from release fetch/push parity
|
|
94
96
|
KETTLE_RELEASE_APPRAISAL_TASK=appraisal:update
|
|
95
97
|
# Use slower appraisal:update instead of default appraisal:generate
|
|
96
98
|
KETTLE_DEV_SKIP_BUNDLE_AUDIT=true
|
|
@@ -168,6 +170,27 @@ def extract_ci_workflows_arg!(argv)
|
|
|
168
170
|
workflows
|
|
169
171
|
end
|
|
170
172
|
|
|
173
|
+
def extract_skip_remotes_arg!(argv)
|
|
174
|
+
remotes = nil
|
|
175
|
+
if (idx = argv.index("--skip-remotes"))
|
|
176
|
+
remotes = argv[idx + 1]
|
|
177
|
+
if remotes.to_s.empty? || remotes.to_s.start_with?("-")
|
|
178
|
+
Kettle::Dev::ExitAdapter.abort("--skip-remotes requires a comma-separated remote list")
|
|
179
|
+
end
|
|
180
|
+
argv.slice!(idx, 2)
|
|
181
|
+
end
|
|
182
|
+
argv.delete_if do |arg|
|
|
183
|
+
if arg.start_with?("--skip-remotes=", "skip_remotes=")
|
|
184
|
+
remotes = arg.split("=", 2)[1]
|
|
185
|
+
Kettle::Dev::ExitAdapter.abort("--skip-remotes requires a comma-separated remote list") if remotes.to_s.empty?
|
|
186
|
+
true
|
|
187
|
+
else
|
|
188
|
+
false
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
remotes
|
|
192
|
+
end
|
|
193
|
+
|
|
171
194
|
local_ci = ARGV.include?("--local-ci")
|
|
172
195
|
appraisal_task = ARGV.delete("--appraisal-update") ? "appraisal:update" : nil
|
|
173
196
|
skip_bundle_audit = !!ARGV.delete("--skip-bundle-audit")
|
|
@@ -176,9 +199,10 @@ start_step = start_step_arg ? start_step_arg.split("=", 2)[1].to_i : 0
|
|
|
176
199
|
version_override = extract_version_arg!(ARGV)
|
|
177
200
|
skip_steps = extract_skip_steps_arg!(ARGV)
|
|
178
201
|
ci_workflows = extract_ci_workflows_arg!(ARGV)
|
|
202
|
+
skip_remotes = extract_skip_remotes_arg!(ARGV)
|
|
179
203
|
|
|
180
204
|
begin
|
|
181
|
-
Kettle::Dev::ReleaseCLI.new(start_step: start_step, local_ci: local_ci, version: version_override, appraisal_task: appraisal_task, skip_steps: skip_steps, skip_bundle_audit: skip_bundle_audit, ci_workflows: ci_workflows).run
|
|
205
|
+
Kettle::Dev::ReleaseCLI.new(start_step: start_step, local_ci: local_ci, version: version_override, appraisal_task: appraisal_task, skip_steps: skip_steps, skip_bundle_audit: skip_bundle_audit, ci_workflows: ci_workflows, skip_remotes: skip_remotes).run
|
|
182
206
|
rescue LoadError => e
|
|
183
207
|
warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
|
|
184
208
|
warn(Array(e.backtrace).join("\n")) if ENV["DEBUG"]
|
|
@@ -27,7 +27,7 @@ module Kettle
|
|
|
27
27
|
# @param enforce_coverage_thresholds [Boolean] when true, fail strict coverage generation below project thresholds
|
|
28
28
|
# @param update_prep [Boolean] when true, update the most recent prepared release section in place
|
|
29
29
|
# @param version [String, nil] explicit version override for gems without a literal VERSION constant
|
|
30
|
-
def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, version: nil, root: Kettle::Dev::CIHelpers.project_root)
|
|
30
|
+
def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, version: nil, root: Kettle::Dev::CIHelpers.project_root, refresh_cache: false)
|
|
31
31
|
@root = root
|
|
32
32
|
@changelog_path = File.join(@root, "CHANGELOG.md")
|
|
33
33
|
@coverage_path = File.join(@root, "coverage", "coverage.json")
|
|
@@ -35,6 +35,7 @@ module Kettle
|
|
|
35
35
|
@enforce_coverage_thresholds = enforce_coverage_thresholds
|
|
36
36
|
@update_prep = update_prep
|
|
37
37
|
@version_override = Kettle::Dev::Versioning.normalize_explicit_version(version)
|
|
38
|
+
@refresh_cache = refresh_cache
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
# Main entry point to update CHANGELOG.md
|
|
@@ -150,6 +151,7 @@ module Kettle
|
|
|
150
151
|
unless changelog_present
|
|
151
152
|
latest_overall, latest_for_series, latest_for_major = latest_released_versions(gem_name, version)
|
|
152
153
|
latest_target = latest_release_target(version, latest_overall, latest_for_series, latest_for_major)
|
|
154
|
+
ahead = commits_ahead_of_release(latest_target || latest_overall)
|
|
153
155
|
return {
|
|
154
156
|
root: @root,
|
|
155
157
|
gem_name: gem_name,
|
|
@@ -164,7 +166,8 @@ module Kettle
|
|
|
164
166
|
latest_released_overall: latest_overall,
|
|
165
167
|
latest_released_for_current_major: latest_for_major,
|
|
166
168
|
latest_released_for_current_series: latest_for_series,
|
|
167
|
-
latest_release_target: latest_target
|
|
169
|
+
latest_release_target: latest_target,
|
|
170
|
+
ahead: ahead
|
|
168
171
|
}
|
|
169
172
|
end
|
|
170
173
|
|
|
@@ -176,6 +179,7 @@ module Kettle
|
|
|
176
179
|
latest_overall, latest_for_series, latest_for_major = latest_released_versions(gem_name, release_lookup_version)
|
|
177
180
|
latest_target = latest_release_target(release_lookup_version, latest_overall, latest_for_series, latest_for_major)
|
|
178
181
|
prepared_release_pending = !!latest_changelog_version && latest_target != latest_changelog_version
|
|
182
|
+
ahead = commits_ahead_of_release(latest_target || latest_overall)
|
|
179
183
|
|
|
180
184
|
{
|
|
181
185
|
root: @root,
|
|
@@ -191,18 +195,20 @@ module Kettle
|
|
|
191
195
|
latest_released_overall: latest_overall,
|
|
192
196
|
latest_released_for_current_major: latest_for_major,
|
|
193
197
|
latest_released_for_current_series: latest_for_series,
|
|
194
|
-
latest_release_target: latest_target
|
|
198
|
+
latest_release_target: latest_target,
|
|
199
|
+
ahead: ahead
|
|
195
200
|
}
|
|
196
201
|
end
|
|
197
202
|
|
|
198
203
|
def release_state_table(state = release_state)
|
|
199
204
|
rows = [
|
|
200
|
-
["gem", "version.rb", "latest released", "latest changelog", "unreleased", "prepared", "pending"],
|
|
205
|
+
["gem", "version.rb", "latest released", "latest changelog", "ahead", "unreleased", "prepared", "pending"],
|
|
201
206
|
[
|
|
202
207
|
state.fetch(:gem_name),
|
|
203
208
|
state.fetch(:version),
|
|
204
209
|
state.fetch(:latest_released) || "unknown",
|
|
205
210
|
state.fetch(:latest_changelog_version) || "none",
|
|
211
|
+
state.fetch(:ahead, nil).nil? ? "unknown" : state.fetch(:ahead).to_s,
|
|
206
212
|
yes_no(state.fetch(:unreleased_entries)),
|
|
207
213
|
yes_no(state.fetch(:prepared_release_pending)),
|
|
208
214
|
yes_no(state.fetch(:pending_release))
|
|
@@ -239,6 +245,33 @@ module Kettle
|
|
|
239
245
|
value ? "yes" : "no"
|
|
240
246
|
end
|
|
241
247
|
|
|
248
|
+
def commits_ahead_of_release(version)
|
|
249
|
+
tag = release_tag_for_version(version)
|
|
250
|
+
branch = default_branch_ref
|
|
251
|
+
return nil unless tag && branch
|
|
252
|
+
|
|
253
|
+
stdout, _stderr, status = Open3.capture3("git", "rev-list", "--count", "#{tag}..#{branch}", chdir: @root)
|
|
254
|
+
status.success? ? stdout.to_i : nil
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def release_tag_for_version(version)
|
|
258
|
+
return nil if version.to_s.empty?
|
|
259
|
+
|
|
260
|
+
["v#{version}", version.to_s].find { |tag| git_ref_exists?("refs/tags/#{tag}^{commit}") }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def default_branch_ref
|
|
264
|
+
stdout, _stderr, status = Open3.capture3("git", "symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD", chdir: @root)
|
|
265
|
+
return stdout.strip if status.success? && !stdout.strip.empty?
|
|
266
|
+
|
|
267
|
+
%w[main master HEAD].find { |ref| git_ref_exists?(ref) }
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def git_ref_exists?(ref)
|
|
271
|
+
_stdout, _stderr, status = Open3.capture3("git", "rev-parse", "--verify", "--quiet", ref, chdir: @root)
|
|
272
|
+
status.success?
|
|
273
|
+
end
|
|
274
|
+
|
|
242
275
|
def detect_plan(changelog, version)
|
|
243
276
|
latest_overall = nil
|
|
244
277
|
latest_for_series = nil
|
|
@@ -367,11 +400,9 @@ module Kettle
|
|
|
367
400
|
end
|
|
368
401
|
|
|
369
402
|
def latest_released_versions(gem_name, current_version)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return [nil, nil] unless res.is_a?(Net::HTTPSuccess)
|
|
403
|
+
data = Kettle::Dev::GemCoopVersions.fetch(gem_name, version_hint: current_version, refresh: @refresh_cache)
|
|
404
|
+
return [nil, nil, nil] unless data.is_a?(Array)
|
|
373
405
|
|
|
374
|
-
data = JSON.parse(res.body)
|
|
375
406
|
versions = data.map { |h| h["number"] }.compact
|
|
376
407
|
versions.reject! { |v| v.to_s.include?("-pre") || v.to_s.include?(".pre") || v.to_s.match?(/[a-zA-Z]/) }
|
|
377
408
|
gversions = versions.map { |s| Gem::Version.new(s) }.sort
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require "net/http"
|
|
6
|
+
require "time"
|
|
7
|
+
require "uri"
|
|
8
|
+
|
|
9
|
+
module Kettle
|
|
10
|
+
module Dev
|
|
11
|
+
module GemCoopVersions
|
|
12
|
+
CACHE_BUST_TTL_SECONDS = 15 * 60
|
|
13
|
+
ENV_REFRESH = "KETTLE_GEM_COOP_REFRESH"
|
|
14
|
+
ENV_MARKER_PATH = "KETTLE_GEM_COOP_CACHE_BUST_PATH"
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def fetch(gem_name, version_hint: nil, refresh: false)
|
|
18
|
+
cache_bust = refresh || env_refresh? || fresh_release_marker?(gem_name, version_hint)
|
|
19
|
+
uri = versions_uri(gem_name, cache_bust: cache_bust)
|
|
20
|
+
request = Net::HTTP::Get.new(uri)
|
|
21
|
+
if cache_bust
|
|
22
|
+
request["Cache-Control"] = "no-cache"
|
|
23
|
+
request["Pragma"] = "no-cache"
|
|
24
|
+
end
|
|
25
|
+
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
|
|
26
|
+
http.request(request)
|
|
27
|
+
end
|
|
28
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
29
|
+
|
|
30
|
+
JSON.parse(response.body)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def mark_released(gem_name, version)
|
|
34
|
+
return if gem_name.to_s.empty? || version.to_s.empty?
|
|
35
|
+
|
|
36
|
+
path = marker_path
|
|
37
|
+
data = read_marker(path)
|
|
38
|
+
data["releases"] ||= {}
|
|
39
|
+
data["releases"][gem_name.to_s] = {
|
|
40
|
+
"version" => version.to_s,
|
|
41
|
+
"released_at" => Time.now.utc.iso8601
|
|
42
|
+
}
|
|
43
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
44
|
+
File.write(path, JSON.pretty_generate(data) << "\n")
|
|
45
|
+
rescue => error
|
|
46
|
+
warn("[kettle-dev] could not update gem.coop cache-bust marker: #{error.class}: #{error.message}") if Kettle::Dev::DEBUGGING
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def marker_path
|
|
50
|
+
configured = ENV.fetch(ENV_MARKER_PATH, "").to_s
|
|
51
|
+
return configured unless configured.empty?
|
|
52
|
+
|
|
53
|
+
state_home = ENV["XDG_STATE_HOME"]
|
|
54
|
+
state_home = File.join(Dir.home, ".local", "state") if state_home.to_s.empty?
|
|
55
|
+
File.join(state_home, "kettle-dev", "gem-coop-cache-bust.json")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def versions_uri(gem_name, cache_bust:)
|
|
61
|
+
uri = URI("https://gem.coop/api/v1/versions/#{gem_name}.json")
|
|
62
|
+
uri.query = "_kettle_cache_bust=#{Time.now.to_i}" if cache_bust
|
|
63
|
+
uri
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fresh_release_marker?(gem_name, version_hint)
|
|
67
|
+
entry = read_marker(marker_path).fetch("releases", {})[gem_name.to_s]
|
|
68
|
+
return false unless entry
|
|
69
|
+
return false if version_hint && entry["version"].to_s != version_hint.to_s
|
|
70
|
+
|
|
71
|
+
released_at = Time.iso8601(entry["released_at"].to_s)
|
|
72
|
+
released_at >= Time.now.utc - CACHE_BUST_TTL_SECONDS
|
|
73
|
+
rescue ArgumentError
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def read_marker(path)
|
|
78
|
+
return {} unless File.file?(path)
|
|
79
|
+
|
|
80
|
+
parsed = JSON.parse(File.read(path))
|
|
81
|
+
parsed.is_a?(Hash) ? parsed : {}
|
|
82
|
+
rescue JSON::ParserError
|
|
83
|
+
{}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def env_refresh?
|
|
87
|
+
ENV.fetch(ENV_REFRESH, "").match?(Kettle::Dev::ENV_TRUE_RE)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -22,7 +22,7 @@ module Kettle
|
|
|
22
22
|
"KETTLE_JEM_QUIET" => "true",
|
|
23
23
|
"KETTLE_JEM_DEBUG" => "false",
|
|
24
24
|
"KETTLE_DEV_DEBUG" => "false",
|
|
25
|
-
"
|
|
25
|
+
"STRUCTUREDMERGE_DEBUG" => "false",
|
|
26
26
|
"DEBUG" => nil,
|
|
27
27
|
"BUNDLE_QUIET" => "true",
|
|
28
28
|
"BUNDLE_DEBUG" => "false",
|
|
@@ -108,13 +108,14 @@ module Kettle
|
|
|
108
108
|
|
|
109
109
|
public
|
|
110
110
|
|
|
111
|
-
def initialize(start_step: 0, local_ci: false, version: nil, appraisal_task: nil, skip_steps: nil, skip_bundle_audit: nil, ci_workflows: nil, **options)
|
|
111
|
+
def initialize(start_step: 0, local_ci: false, version: nil, appraisal_task: nil, skip_steps: nil, skip_bundle_audit: nil, ci_workflows: nil, skip_remotes: nil, **options)
|
|
112
112
|
@root = Kettle::Dev::CIHelpers.project_root
|
|
113
113
|
@git = Kettle::Dev::GitAdapter.new
|
|
114
114
|
@start_step = (start_step || 0).to_i
|
|
115
115
|
@start_step = 0 if @start_step < 0
|
|
116
116
|
@skip_steps = normalize_skip_steps(skip_steps)
|
|
117
117
|
@ci_workflows = normalize_ci_workflows(ci_workflows || ENV["K_RELEASE_CI_WORKFLOWS"])
|
|
118
|
+
@skip_remotes = normalize_skip_remotes(skip_remotes || ENV["K_RELEASE_SKIP_REMOTES"])
|
|
118
119
|
@local_ci = !!local_ci
|
|
119
120
|
@skip_bundle_audit = truthy_value?(skip_bundle_audit) || truthy_value?(ENV["KETTLE_DEV_SKIP_BUNDLE_AUDIT"])
|
|
120
121
|
@version_override = Kettle::Dev::Versioning.normalize_explicit_version(version)
|
|
@@ -343,6 +344,7 @@ module Kettle
|
|
|
343
344
|
else
|
|
344
345
|
puts "Running release (you may be prompted for signing key password and RubyGems MFA OTP)..."
|
|
345
346
|
run_cmd!("bundle exec rake release")
|
|
347
|
+
mark_gem_coop_release_cache_bust(version || detect_version)
|
|
346
348
|
end
|
|
347
349
|
end
|
|
348
350
|
|
|
@@ -411,6 +413,14 @@ module Kettle
|
|
|
411
413
|
workflows.map { |workflow| workflow.match?(/\.ya?ml\z/) ? workflow : "#{workflow}.yml" }.uniq
|
|
412
414
|
end
|
|
413
415
|
|
|
416
|
+
def normalize_skip_remotes(value)
|
|
417
|
+
remotes = Array(value).flat_map { |part| part.to_s.split(",") }.map(&:strip).reject(&:empty?)
|
|
418
|
+
invalid = remotes.find { |remote| !remote.match?(/\A[A-Za-z0-9_.-]+\z/) }
|
|
419
|
+
abort("Invalid skip remotes value #{invalid.inspect}; use comma-separated git remote names.") if invalid
|
|
420
|
+
|
|
421
|
+
remotes.uniq
|
|
422
|
+
end
|
|
423
|
+
|
|
414
424
|
private
|
|
415
425
|
|
|
416
426
|
def local_ci?
|
|
@@ -719,10 +729,85 @@ module Kettle
|
|
|
719
729
|
end
|
|
720
730
|
|
|
721
731
|
def monitor_workflows_after_push!
|
|
732
|
+
ensure_github_pull_request_for_ci!
|
|
722
733
|
# Use abort-on-failure CI monitor to match historical behavior and specs
|
|
723
734
|
Kettle::Dev::CIMonitor.monitor_all!(restart_hint: "bundle exec kettle-release start_step=10", workflows: @ci_workflows)
|
|
724
735
|
end
|
|
725
736
|
|
|
737
|
+
def ensure_github_pull_request_for_ci!
|
|
738
|
+
branch = current_branch
|
|
739
|
+
return if branch.to_s.empty?
|
|
740
|
+
|
|
741
|
+
trunk = detect_trunk_branch
|
|
742
|
+
return if branch == trunk
|
|
743
|
+
|
|
744
|
+
gh_remote = preferred_github_remote
|
|
745
|
+
return unless gh_remote
|
|
746
|
+
|
|
747
|
+
owner, repo = parse_github_owner_repo(remote_url(gh_remote))
|
|
748
|
+
return unless owner && repo
|
|
749
|
+
|
|
750
|
+
pull_request = github_pull_request_for_branch(owner: owner, repo: repo, branch: branch, base: trunk)
|
|
751
|
+
if pull_request
|
|
752
|
+
puts "GitHub pull request ##{pull_request.fetch("number")} already open for #{branch} -> #{trunk}: #{pull_request.fetch("url")}"
|
|
753
|
+
return
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
create_github_pull_request!(owner: owner, repo: repo, branch: branch, base: trunk)
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
def github_pull_request_for_branch(owner:, repo:, branch:, base:)
|
|
760
|
+
output = gh_output!(
|
|
761
|
+
"pr",
|
|
762
|
+
"list",
|
|
763
|
+
"--repo",
|
|
764
|
+
"#{owner}/#{repo}",
|
|
765
|
+
"--head",
|
|
766
|
+
branch,
|
|
767
|
+
"--base",
|
|
768
|
+
base,
|
|
769
|
+
"--state",
|
|
770
|
+
"open",
|
|
771
|
+
"--json",
|
|
772
|
+
"number,url",
|
|
773
|
+
"--limit",
|
|
774
|
+
"1"
|
|
775
|
+
)
|
|
776
|
+
Array(JSON.parse(output)).first
|
|
777
|
+
rescue JSON::ParserError => e
|
|
778
|
+
Kettle::Dev.debug_error(e, __method__)
|
|
779
|
+
abort("Could not parse GitHub pull request list from gh CLI.")
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
def create_github_pull_request!(owner:, repo:, branch:, base:)
|
|
783
|
+
output = gh_output!(
|
|
784
|
+
"pr",
|
|
785
|
+
"create",
|
|
786
|
+
"--repo",
|
|
787
|
+
"#{owner}/#{repo}",
|
|
788
|
+
"--head",
|
|
789
|
+
branch,
|
|
790
|
+
"--base",
|
|
791
|
+
base,
|
|
792
|
+
"--title",
|
|
793
|
+
"Release #{branch}",
|
|
794
|
+
"--body",
|
|
795
|
+
"Automated release validation PR for `#{branch}`.\n\nThis PR lets GitHub Actions run before kettle-release merges the branch into `#{base}`."
|
|
796
|
+
)
|
|
797
|
+
puts "Created GitHub pull request for #{branch} -> #{base}: #{output.strip}"
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
def gh_output!(*args)
|
|
801
|
+
stdout_str, stderr_str, status = Open3.capture3(self.class.send(:command_env), "gh", *args)
|
|
802
|
+
return stdout_str if status.success?
|
|
803
|
+
|
|
804
|
+
exit_code = status.respond_to?(:exitstatus) ? status.exitstatus : 1
|
|
805
|
+
diag = stderr_str.to_s.empty? ? "" : "\n--- STDERR ---\n#{stderr_str}".rstrip
|
|
806
|
+
abort("GitHub pull request setup failed: gh #{args.join(" ")} (exit #{exit_code})#{diag}")
|
|
807
|
+
rescue Errno::ENOENT
|
|
808
|
+
abort("GitHub pull request setup failed: gh CLI is required to create or find the release PR.")
|
|
809
|
+
end
|
|
810
|
+
|
|
726
811
|
def run_cmd!(cmd)
|
|
727
812
|
cmd = bundle_audit_skip_command(cmd)
|
|
728
813
|
with_bundle_audit_skip_env do
|
|
@@ -870,6 +955,7 @@ module Kettle
|
|
|
870
955
|
|
|
871
956
|
puts "Publishing #{File.basename(gem_path)} to RubyGems without pushing git refs..."
|
|
872
957
|
run_cmd!("gem push #{Shellwords.escape(gem_path)}")
|
|
958
|
+
mark_gem_coop_release_cache_bust(version, gem_name: gem_name_from_gem_path(gem_path, version))
|
|
873
959
|
end
|
|
874
960
|
|
|
875
961
|
def detect_version
|
|
@@ -887,11 +973,9 @@ module Kettle
|
|
|
887
973
|
end
|
|
888
974
|
|
|
889
975
|
def latest_released_versions(gem_name, current_version)
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
return [nil, nil] unless res.is_a?(Net::HTTPSuccess)
|
|
976
|
+
data = Kettle::Dev::GemCoopVersions.fetch(gem_name, version_hint: current_version)
|
|
977
|
+
return [nil, nil] unless data.is_a?(Array)
|
|
893
978
|
|
|
894
|
-
data = JSON.parse(res.body)
|
|
895
979
|
versions = data.map { |h| h["number"] }.compact
|
|
896
980
|
versions.reject! { |v| v.to_s.include?("-pre") || v.to_s.include?(".pre") || /[a-zA-Z]/.match?(v.to_s) }
|
|
897
981
|
gversions = versions.map { |s| Gem::Version.new(s) }.sort
|
|
@@ -907,6 +991,18 @@ module Kettle
|
|
|
907
991
|
[nil, nil]
|
|
908
992
|
end
|
|
909
993
|
|
|
994
|
+
def mark_gem_coop_release_cache_bust(version, gem_name: nil)
|
|
995
|
+
gem_name ||= detect_gem_name
|
|
996
|
+
Kettle::Dev::GemCoopVersions.mark_released(gem_name, version)
|
|
997
|
+
rescue => error
|
|
998
|
+
warn("[kettle-release] could not mark gem.coop cache-bust for release-state: #{error.class}: #{error.message}") if Kettle::Dev::DEBUGGING
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
def gem_name_from_gem_path(gem_path, version)
|
|
1002
|
+
basename = File.basename(gem_path, ".gem")
|
|
1003
|
+
basename.delete_suffix("-#{version}")
|
|
1004
|
+
end
|
|
1005
|
+
|
|
910
1006
|
def commit_release_prep!(version)
|
|
911
1007
|
msg = "🔖 Prepare release v#{version}"
|
|
912
1008
|
# Stage all changes (including new/untracked files) prior to committing
|
|
@@ -925,7 +1021,7 @@ module Kettle
|
|
|
925
1021
|
branch = current_branch
|
|
926
1022
|
abort("Could not determine current branch to push.") unless branch
|
|
927
1023
|
|
|
928
|
-
if
|
|
1024
|
+
if use_all_remote?
|
|
929
1025
|
puts "$ git push all #{branch}"
|
|
930
1026
|
success = @git.push("all", branch)
|
|
931
1027
|
unless success
|
|
@@ -936,7 +1032,7 @@ module Kettle
|
|
|
936
1032
|
end
|
|
937
1033
|
|
|
938
1034
|
remotes = []
|
|
939
|
-
remotes << "origin" if has_remote?("origin")
|
|
1035
|
+
remotes << "origin" if has_remote?("origin") && !skipped_remote?("origin")
|
|
940
1036
|
remotes |= github_remote_candidates
|
|
941
1037
|
remotes |= gitlab_remote_candidates
|
|
942
1038
|
remotes |= codeberg_remote_candidates
|
|
@@ -967,13 +1063,13 @@ module Kettle
|
|
|
967
1063
|
# 1) If a remote named "all" exists, push tags only to it.
|
|
968
1064
|
# 2) Otherwise, if other remotes exist, push tags to each of them.
|
|
969
1065
|
# 3) If no remotes are configured, push tags using default remote.
|
|
970
|
-
if
|
|
1066
|
+
if use_all_remote?
|
|
971
1067
|
ok = @git.push_tags("all")
|
|
972
1068
|
warn("Push tags to 'all' reported failure.") unless ok
|
|
973
1069
|
return
|
|
974
1070
|
end
|
|
975
1071
|
|
|
976
|
-
remotes =
|
|
1072
|
+
remotes = active_remotes
|
|
977
1073
|
remotes -= ["all"] if remotes
|
|
978
1074
|
if remotes.nil? || remotes.empty?
|
|
979
1075
|
ok = @git.push_tags(nil)
|
|
@@ -1021,15 +1117,15 @@ module Kettle
|
|
|
1021
1117
|
end
|
|
1022
1118
|
|
|
1023
1119
|
def github_remote_candidates
|
|
1024
|
-
remotes_with_urls.select { |n, u| u.include?("github.com") }.keys
|
|
1120
|
+
active_remote_candidates(remotes_with_urls.select { |n, u| u.include?("github.com") }.keys)
|
|
1025
1121
|
end
|
|
1026
1122
|
|
|
1027
1123
|
def gitlab_remote_candidates
|
|
1028
|
-
remotes_with_urls.select { |n, u| u.include?("gitlab.com") }.keys
|
|
1124
|
+
active_remote_candidates(remotes_with_urls.select { |n, u| u.include?("gitlab.com") }.keys)
|
|
1029
1125
|
end
|
|
1030
1126
|
|
|
1031
1127
|
def codeberg_remote_candidates
|
|
1032
|
-
remotes_with_urls.select { |n, u| u.include?("codeberg.org") }.keys
|
|
1128
|
+
active_remote_candidates(remotes_with_urls.select { |n, u| u.include?("codeberg.org") }.keys)
|
|
1033
1129
|
end
|
|
1034
1130
|
|
|
1035
1131
|
def preferred_github_remote
|
|
@@ -1052,6 +1148,22 @@ module Kettle
|
|
|
1052
1148
|
list_remotes.include?(name)
|
|
1053
1149
|
end
|
|
1054
1150
|
|
|
1151
|
+
def skipped_remote?(name)
|
|
1152
|
+
@skip_remotes.include?(name)
|
|
1153
|
+
end
|
|
1154
|
+
|
|
1155
|
+
def active_remotes
|
|
1156
|
+
list_remotes.reject { |remote| skipped_remote?(remote) }
|
|
1157
|
+
end
|
|
1158
|
+
|
|
1159
|
+
def active_remote_candidates(candidates)
|
|
1160
|
+
candidates.reject { |remote| skipped_remote?(remote) }
|
|
1161
|
+
end
|
|
1162
|
+
|
|
1163
|
+
def use_all_remote?
|
|
1164
|
+
has_remote?("all") && @skip_remotes.empty?
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1055
1167
|
def remote_branch_exists?(remote, branch)
|
|
1056
1168
|
_out, ok = git_output(["show-ref", "--verify", "--quiet", "refs/remotes/#{remote}/#{branch}"])
|
|
1057
1169
|
ok
|
|
@@ -1076,9 +1188,15 @@ module Kettle
|
|
|
1076
1188
|
|
|
1077
1189
|
def ensure_trunk_synced_before_push!(trunk, feature)
|
|
1078
1190
|
if has_remote?("all")
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
remotes
|
|
1191
|
+
remotes = active_remotes
|
|
1192
|
+
skipped = list_remotes.select { |remote| skipped_remote?(remote) }
|
|
1193
|
+
puts "Remote 'all' detected. Fetching from active remotes and enforcing strict trunk parity..."
|
|
1194
|
+
puts "Skipping configured remotes: #{skipped.join(", ")}" unless skipped.empty?
|
|
1195
|
+
remotes.each do |remote|
|
|
1196
|
+
next if remote == "all"
|
|
1197
|
+
|
|
1198
|
+
run_cmd!("git fetch #{Shellwords.escape(remote)}")
|
|
1199
|
+
end
|
|
1082
1200
|
missing_from = []
|
|
1083
1201
|
remotes.each do |r|
|
|
1084
1202
|
next if r == "all"
|
data/lib/kettle/dev/version.rb
CHANGED
data/lib/kettle/dev.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative "dev/version"
|
|
|
5
5
|
if Gem.ruby_version < Gem::Version.new("2.7")
|
|
6
6
|
begin
|
|
7
7
|
require "backports/2.7.0/enumerable/filter_map"
|
|
8
|
+
require "backports/2.5.0/string/delete_suffix"
|
|
8
9
|
rescue LoadError => error
|
|
9
10
|
message = "kettle-dev on Ruby <= 2.6 requires the backports gem; " \
|
|
10
11
|
"add `gem \"backports\", \">= 3.25\"` to your Gemfile (#{error.message})"
|
|
@@ -33,6 +34,7 @@ module Kettle
|
|
|
33
34
|
autoload :CommitMsg, "kettle/dev/commit_msg"
|
|
34
35
|
autoload :DvcsCLI, "kettle/dev/dvcs_cli"
|
|
35
36
|
autoload :ExitAdapter, "kettle/dev/exit_adapter"
|
|
37
|
+
autoload :GemCoopVersions, "kettle/dev/gem_coop_versions"
|
|
36
38
|
autoload :GemSpecReader, "kettle/dev/gem_spec_reader"
|
|
37
39
|
autoload :GitAdapter, "kettle/dev/git_adapter"
|
|
38
40
|
autoload :GitCommitFooter, "kettle/dev/git_commit_footer"
|
data/sig/kettle/dev.rbs
CHANGED
|
@@ -21,7 +21,6 @@ module Kettle
|
|
|
21
21
|
def self.defaults: () -> Array[String]
|
|
22
22
|
def self.register_default: (String | Symbol task_name) -> Array[String]
|
|
23
23
|
VERSION: String
|
|
24
|
-
# AST-driven merger for Appraisals files using Prism
|
|
25
24
|
module AppraisalsAstMerger
|
|
26
25
|
TRACKED_METHODS: Array[Symbol]
|
|
27
26
|
|
|
@@ -382,7 +381,7 @@ module Kettle
|
|
|
382
381
|
end
|
|
383
382
|
|
|
384
383
|
class ReleaseCLI
|
|
385
|
-
def initialize: (?start_step: Integer, ?local_ci: bool, ?version: String?, ?appraisal_task: String?, ?skip_steps: untyped, ?skip_bundle_audit: bool?, ?ci_workflows: untyped, **untyped) -> void
|
|
384
|
+
def initialize: (?start_step: Integer, ?local_ci: bool, ?version: String?, ?appraisal_task: String?, ?skip_steps: untyped, ?skip_bundle_audit: bool?, ?ci_workflows: untyped, ?skip_remotes: untyped, **untyped) -> void
|
|
386
385
|
def run: () -> void
|
|
387
386
|
|
|
388
387
|
private
|
|
@@ -469,9 +468,6 @@ module Kettle
|
|
|
469
468
|
end
|
|
470
469
|
|
|
471
470
|
module Tasks
|
|
472
|
-
# Namespace module for task-related classes and Rake task loaders.
|
|
473
|
-
# Namespace module for task-related classes and Rake task loaders.
|
|
474
|
-
# Namespace module for task-related classes and Rake task loaders.
|
|
475
471
|
# Namespace module for task-related classes and Rake task loaders.
|
|
476
472
|
module CITask
|
|
477
473
|
def self.act: (?String opt) -> void
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kettle-dev
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -159,40 +159,40 @@ dependencies:
|
|
|
159
159
|
requirements:
|
|
160
160
|
- - "~>"
|
|
161
161
|
- !ruby/object:Gem::Version
|
|
162
|
-
version: '3.
|
|
162
|
+
version: '3.2'
|
|
163
163
|
- - ">="
|
|
164
164
|
- !ruby/object:Gem::Version
|
|
165
|
-
version: 3.
|
|
165
|
+
version: 3.2.0
|
|
166
166
|
type: :development
|
|
167
167
|
prerelease: false
|
|
168
168
|
version_requirements: !ruby/object:Gem::Requirement
|
|
169
169
|
requirements:
|
|
170
170
|
- - "~>"
|
|
171
171
|
- !ruby/object:Gem::Version
|
|
172
|
-
version: '3.
|
|
172
|
+
version: '3.2'
|
|
173
173
|
- - ">="
|
|
174
174
|
- !ruby/object:Gem::Version
|
|
175
|
-
version: 3.
|
|
175
|
+
version: 3.2.0
|
|
176
176
|
- !ruby/object:Gem::Dependency
|
|
177
177
|
name: turbo_tests2
|
|
178
178
|
requirement: !ruby/object:Gem::Requirement
|
|
179
179
|
requirements:
|
|
180
180
|
- - "~>"
|
|
181
181
|
- !ruby/object:Gem::Version
|
|
182
|
-
version: '3.
|
|
182
|
+
version: '3.2'
|
|
183
183
|
- - ">="
|
|
184
184
|
- !ruby/object:Gem::Version
|
|
185
|
-
version: 3.
|
|
185
|
+
version: 3.2.0
|
|
186
186
|
type: :development
|
|
187
187
|
prerelease: false
|
|
188
188
|
version_requirements: !ruby/object:Gem::Requirement
|
|
189
189
|
requirements:
|
|
190
190
|
- - "~>"
|
|
191
191
|
- !ruby/object:Gem::Version
|
|
192
|
-
version: '3.
|
|
192
|
+
version: '3.2'
|
|
193
193
|
- - ">="
|
|
194
194
|
- !ruby/object:Gem::Version
|
|
195
|
-
version: 3.
|
|
195
|
+
version: 3.2.0
|
|
196
196
|
- !ruby/object:Gem::Dependency
|
|
197
197
|
name: stone_checksums
|
|
198
198
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -282,6 +282,7 @@ files:
|
|
|
282
282
|
- lib/kettle/dev/commit_msg.rb
|
|
283
283
|
- lib/kettle/dev/dvcs_cli.rb
|
|
284
284
|
- lib/kettle/dev/exit_adapter.rb
|
|
285
|
+
- lib/kettle/dev/gem_coop_versions.rb
|
|
285
286
|
- lib/kettle/dev/gem_spec_reader.rb
|
|
286
287
|
- lib/kettle/dev/gha_sha_pins_cli.rb
|
|
287
288
|
- lib/kettle/dev/git_adapter.rb
|
|
@@ -313,10 +314,10 @@ licenses:
|
|
|
313
314
|
- AGPL-3.0-only
|
|
314
315
|
metadata:
|
|
315
316
|
homepage_uri: https://kettle-dev.galtzo.com
|
|
316
|
-
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.3.
|
|
317
|
-
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.3.
|
|
317
|
+
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.3.9
|
|
318
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.3.9/CHANGELOG.md
|
|
318
319
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
|
|
319
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.3.
|
|
320
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.3.9
|
|
320
321
|
funding_uri: https://github.com/sponsors/pboling
|
|
321
322
|
wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
|
|
322
323
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
|
Binary file
|