kettle-dev 2.3.7 → 2.3.8
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 +35 -1
- data/README.md +4 -2
- data/exe/kettle-release +26 -2
- data/lib/kettle/dev/changelog_cli.rb +35 -3
- data/lib/kettle/dev/rakelib/appraisal.rake +1 -1
- data/lib/kettle/dev/release_cli.rb +118 -12
- data/lib/kettle/dev/version.rb +1 -1
- data/sig/kettle/dev.rbs +1 -5
- data.tar.gz.sig +0 -0
- metadata +12 -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: fb500cfcc82a5cb325eb83ae9a5464e8333cce1c3ec03814b597063d41ec0e8a
|
|
4
|
+
data.tar.gz: 124a6c4ad4a3dd9172c131d6d5276a685d7e15f2be8f5fc51722c028fc83f136
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1dd9b51023413bc9301d87dfc989dcec0844190b0cccfa90d8344bcd428403c70837f3972fdedc0cf2b4dee103af88923cc41ec43f80a869098a8a20da8a579d
|
|
7
|
+
data.tar.gz: 94084430abf75c714d2b5be26a25f1a9c1be691fc9c4f2fa760fc767e7ce1698e01ab71f5b978ec744f9981eeedc7b474a663b51504c6d842c0569ffe7e79cb0
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,38 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [2.3.8] - 2026-07-21
|
|
34
|
+
|
|
35
|
+
- TAG: [v2.3.8][2.3.8t]
|
|
36
|
+
- COVERAGE: 91.98% -- 4379/4761 lines in 36 files
|
|
37
|
+
- BRANCH COVERAGE: 73.91% -- 1711/2315 branches in 36 files
|
|
38
|
+
- 56.04% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `kettle-changelog --release-state` now reports an `ahead` value counting
|
|
43
|
+
commits from the latest release tag to the local default branch when
|
|
44
|
+
available.
|
|
45
|
+
- `kettle-release` now accepts `--skip-remotes` and
|
|
46
|
+
`K_RELEASE_SKIP_REMOTES` to exclude temporarily unavailable git remotes from
|
|
47
|
+
release fetch/push parity checks.
|
|
48
|
+
- `kettle-release` now creates or reuses an open GitHub pull request for
|
|
49
|
+
non-trunk release branches before waiting for GitHub Actions, so
|
|
50
|
+
pull-request-only workflows start after release branch pushes.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- kettle-jem-template-20260720-001 - Generated READMEs can now render
|
|
55
|
+
template-managed corporate sponsor logos from project or family config.
|
|
56
|
+
- kettle-jem-template-20260720-002 - Generated development Gemfiles now use the
|
|
57
|
+
released `tree_sitter_language_pack` gem 1.13.3 or newer by default.
|
|
58
|
+
- kettle-jem-template-20260720-003 - Generated StructuredMerge Git diff driver
|
|
59
|
+
config now uses the installed `smorg-rb` Ruby driver name.
|
|
60
|
+
- kettle-jem-template-20260720-004 - Generated multi-engine workflow files now
|
|
61
|
+
omit JRuby and TruffleRuby jobs when project config declares MRI-only engines.
|
|
62
|
+
- kettle-jem-template-20260720-005 - Generated README Support & Community rows
|
|
63
|
+
now include a RubyForum help badge.
|
|
64
|
+
|
|
33
65
|
## [2.3.7] - 2026-07-18
|
|
34
66
|
|
|
35
67
|
- TAG: [v2.3.7][2.3.7t]
|
|
@@ -2536,7 +2568,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2536
2568
|
- Selecting will run the selected workflow via `act`
|
|
2537
2569
|
- This may move to its own gem in the future.
|
|
2538
2570
|
|
|
2539
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.
|
|
2571
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.8...HEAD
|
|
2572
|
+
[2.3.8]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.7...v2.3.8
|
|
2573
|
+
[2.3.8t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.8
|
|
2540
2574
|
[2.3.7]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.6...v2.3.7
|
|
2541
2575
|
[2.3.7t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.7
|
|
2542
2576
|
[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.761-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-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"]
|
|
@@ -150,6 +150,7 @@ module Kettle
|
|
|
150
150
|
unless changelog_present
|
|
151
151
|
latest_overall, latest_for_series, latest_for_major = latest_released_versions(gem_name, version)
|
|
152
152
|
latest_target = latest_release_target(version, latest_overall, latest_for_series, latest_for_major)
|
|
153
|
+
ahead = commits_ahead_of_release(latest_target || latest_overall)
|
|
153
154
|
return {
|
|
154
155
|
root: @root,
|
|
155
156
|
gem_name: gem_name,
|
|
@@ -164,7 +165,8 @@ module Kettle
|
|
|
164
165
|
latest_released_overall: latest_overall,
|
|
165
166
|
latest_released_for_current_major: latest_for_major,
|
|
166
167
|
latest_released_for_current_series: latest_for_series,
|
|
167
|
-
latest_release_target: latest_target
|
|
168
|
+
latest_release_target: latest_target,
|
|
169
|
+
ahead: ahead
|
|
168
170
|
}
|
|
169
171
|
end
|
|
170
172
|
|
|
@@ -176,6 +178,7 @@ module Kettle
|
|
|
176
178
|
latest_overall, latest_for_series, latest_for_major = latest_released_versions(gem_name, release_lookup_version)
|
|
177
179
|
latest_target = latest_release_target(release_lookup_version, latest_overall, latest_for_series, latest_for_major)
|
|
178
180
|
prepared_release_pending = !!latest_changelog_version && latest_target != latest_changelog_version
|
|
181
|
+
ahead = commits_ahead_of_release(latest_target || latest_overall)
|
|
179
182
|
|
|
180
183
|
{
|
|
181
184
|
root: @root,
|
|
@@ -191,18 +194,20 @@ module Kettle
|
|
|
191
194
|
latest_released_overall: latest_overall,
|
|
192
195
|
latest_released_for_current_major: latest_for_major,
|
|
193
196
|
latest_released_for_current_series: latest_for_series,
|
|
194
|
-
latest_release_target: latest_target
|
|
197
|
+
latest_release_target: latest_target,
|
|
198
|
+
ahead: ahead
|
|
195
199
|
}
|
|
196
200
|
end
|
|
197
201
|
|
|
198
202
|
def release_state_table(state = release_state)
|
|
199
203
|
rows = [
|
|
200
|
-
["gem", "version.rb", "latest released", "latest changelog", "unreleased", "prepared", "pending"],
|
|
204
|
+
["gem", "version.rb", "latest released", "latest changelog", "ahead", "unreleased", "prepared", "pending"],
|
|
201
205
|
[
|
|
202
206
|
state.fetch(:gem_name),
|
|
203
207
|
state.fetch(:version),
|
|
204
208
|
state.fetch(:latest_released) || "unknown",
|
|
205
209
|
state.fetch(:latest_changelog_version) || "none",
|
|
210
|
+
state.fetch(:ahead, nil).nil? ? "unknown" : state.fetch(:ahead).to_s,
|
|
206
211
|
yes_no(state.fetch(:unreleased_entries)),
|
|
207
212
|
yes_no(state.fetch(:prepared_release_pending)),
|
|
208
213
|
yes_no(state.fetch(:pending_release))
|
|
@@ -239,6 +244,33 @@ module Kettle
|
|
|
239
244
|
value ? "yes" : "no"
|
|
240
245
|
end
|
|
241
246
|
|
|
247
|
+
def commits_ahead_of_release(version)
|
|
248
|
+
tag = release_tag_for_version(version)
|
|
249
|
+
branch = default_branch_ref
|
|
250
|
+
return nil unless tag && branch
|
|
251
|
+
|
|
252
|
+
stdout, _stderr, status = Open3.capture3("git", "rev-list", "--count", "#{tag}..#{branch}", chdir: @root)
|
|
253
|
+
status.success? ? stdout.to_i : nil
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def release_tag_for_version(version)
|
|
257
|
+
return nil if version.to_s.empty?
|
|
258
|
+
|
|
259
|
+
["v#{version}", version.to_s].find { |tag| git_ref_exists?("refs/tags/#{tag}^{commit}") }
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def default_branch_ref
|
|
263
|
+
stdout, _stderr, status = Open3.capture3("git", "symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD", chdir: @root)
|
|
264
|
+
return stdout.strip if status.success? && !stdout.strip.empty?
|
|
265
|
+
|
|
266
|
+
%w[main master HEAD].find { |ref| git_ref_exists?(ref) }
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def git_ref_exists?(ref)
|
|
270
|
+
_stdout, _stderr, status = Open3.capture3("git", "rev-parse", "--verify", "--quiet", ref, chdir: @root)
|
|
271
|
+
status.success?
|
|
272
|
+
end
|
|
273
|
+
|
|
242
274
|
def detect_plan(changelog, version)
|
|
243
275
|
latest_overall = nil
|
|
244
276
|
latest_for_series = nil
|
|
@@ -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)
|
|
@@ -411,6 +412,14 @@ module Kettle
|
|
|
411
412
|
workflows.map { |workflow| workflow.match?(/\.ya?ml\z/) ? workflow : "#{workflow}.yml" }.uniq
|
|
412
413
|
end
|
|
413
414
|
|
|
415
|
+
def normalize_skip_remotes(value)
|
|
416
|
+
remotes = Array(value).flat_map { |part| part.to_s.split(",") }.map(&:strip).reject(&:empty?)
|
|
417
|
+
invalid = remotes.find { |remote| !remote.match?(/\A[A-Za-z0-9_.-]+\z/) }
|
|
418
|
+
abort("Invalid skip remotes value #{invalid.inspect}; use comma-separated git remote names.") if invalid
|
|
419
|
+
|
|
420
|
+
remotes.uniq
|
|
421
|
+
end
|
|
422
|
+
|
|
414
423
|
private
|
|
415
424
|
|
|
416
425
|
def local_ci?
|
|
@@ -719,10 +728,85 @@ module Kettle
|
|
|
719
728
|
end
|
|
720
729
|
|
|
721
730
|
def monitor_workflows_after_push!
|
|
731
|
+
ensure_github_pull_request_for_ci!
|
|
722
732
|
# Use abort-on-failure CI monitor to match historical behavior and specs
|
|
723
733
|
Kettle::Dev::CIMonitor.monitor_all!(restart_hint: "bundle exec kettle-release start_step=10", workflows: @ci_workflows)
|
|
724
734
|
end
|
|
725
735
|
|
|
736
|
+
def ensure_github_pull_request_for_ci!
|
|
737
|
+
branch = current_branch
|
|
738
|
+
return if branch.to_s.empty?
|
|
739
|
+
|
|
740
|
+
trunk = detect_trunk_branch
|
|
741
|
+
return if branch == trunk
|
|
742
|
+
|
|
743
|
+
gh_remote = preferred_github_remote
|
|
744
|
+
return unless gh_remote
|
|
745
|
+
|
|
746
|
+
owner, repo = parse_github_owner_repo(remote_url(gh_remote))
|
|
747
|
+
return unless owner && repo
|
|
748
|
+
|
|
749
|
+
pull_request = github_pull_request_for_branch(owner: owner, repo: repo, branch: branch, base: trunk)
|
|
750
|
+
if pull_request
|
|
751
|
+
puts "GitHub pull request ##{pull_request.fetch("number")} already open for #{branch} -> #{trunk}: #{pull_request.fetch("url")}"
|
|
752
|
+
return
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
create_github_pull_request!(owner: owner, repo: repo, branch: branch, base: trunk)
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
def github_pull_request_for_branch(owner:, repo:, branch:, base:)
|
|
759
|
+
output = gh_output!(
|
|
760
|
+
"pr",
|
|
761
|
+
"list",
|
|
762
|
+
"--repo",
|
|
763
|
+
"#{owner}/#{repo}",
|
|
764
|
+
"--head",
|
|
765
|
+
branch,
|
|
766
|
+
"--base",
|
|
767
|
+
base,
|
|
768
|
+
"--state",
|
|
769
|
+
"open",
|
|
770
|
+
"--json",
|
|
771
|
+
"number,url",
|
|
772
|
+
"--limit",
|
|
773
|
+
"1"
|
|
774
|
+
)
|
|
775
|
+
Array(JSON.parse(output)).first
|
|
776
|
+
rescue JSON::ParserError => e
|
|
777
|
+
Kettle::Dev.debug_error(e, __method__)
|
|
778
|
+
abort("Could not parse GitHub pull request list from gh CLI.")
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def create_github_pull_request!(owner:, repo:, branch:, base:)
|
|
782
|
+
output = gh_output!(
|
|
783
|
+
"pr",
|
|
784
|
+
"create",
|
|
785
|
+
"--repo",
|
|
786
|
+
"#{owner}/#{repo}",
|
|
787
|
+
"--head",
|
|
788
|
+
branch,
|
|
789
|
+
"--base",
|
|
790
|
+
base,
|
|
791
|
+
"--title",
|
|
792
|
+
"Release #{branch}",
|
|
793
|
+
"--body",
|
|
794
|
+
"Automated release validation PR for `#{branch}`.\n\nThis PR lets GitHub Actions run before kettle-release merges the branch into `#{base}`."
|
|
795
|
+
)
|
|
796
|
+
puts "Created GitHub pull request for #{branch} -> #{base}: #{output.strip}"
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
def gh_output!(*args)
|
|
800
|
+
stdout_str, stderr_str, status = Open3.capture3(self.class.send(:command_env), "gh", *args)
|
|
801
|
+
return stdout_str if status.success?
|
|
802
|
+
|
|
803
|
+
exit_code = status.respond_to?(:exitstatus) ? status.exitstatus : 1
|
|
804
|
+
diag = stderr_str.to_s.empty? ? "" : "\n--- STDERR ---\n#{stderr_str}".rstrip
|
|
805
|
+
abort("GitHub pull request setup failed: gh #{args.join(" ")} (exit #{exit_code})#{diag}")
|
|
806
|
+
rescue Errno::ENOENT
|
|
807
|
+
abort("GitHub pull request setup failed: gh CLI is required to create or find the release PR.")
|
|
808
|
+
end
|
|
809
|
+
|
|
726
810
|
def run_cmd!(cmd)
|
|
727
811
|
cmd = bundle_audit_skip_command(cmd)
|
|
728
812
|
with_bundle_audit_skip_env do
|
|
@@ -925,7 +1009,7 @@ module Kettle
|
|
|
925
1009
|
branch = current_branch
|
|
926
1010
|
abort("Could not determine current branch to push.") unless branch
|
|
927
1011
|
|
|
928
|
-
if
|
|
1012
|
+
if use_all_remote?
|
|
929
1013
|
puts "$ git push all #{branch}"
|
|
930
1014
|
success = @git.push("all", branch)
|
|
931
1015
|
unless success
|
|
@@ -936,7 +1020,7 @@ module Kettle
|
|
|
936
1020
|
end
|
|
937
1021
|
|
|
938
1022
|
remotes = []
|
|
939
|
-
remotes << "origin" if has_remote?("origin")
|
|
1023
|
+
remotes << "origin" if has_remote?("origin") && !skipped_remote?("origin")
|
|
940
1024
|
remotes |= github_remote_candidates
|
|
941
1025
|
remotes |= gitlab_remote_candidates
|
|
942
1026
|
remotes |= codeberg_remote_candidates
|
|
@@ -967,13 +1051,13 @@ module Kettle
|
|
|
967
1051
|
# 1) If a remote named "all" exists, push tags only to it.
|
|
968
1052
|
# 2) Otherwise, if other remotes exist, push tags to each of them.
|
|
969
1053
|
# 3) If no remotes are configured, push tags using default remote.
|
|
970
|
-
if
|
|
1054
|
+
if use_all_remote?
|
|
971
1055
|
ok = @git.push_tags("all")
|
|
972
1056
|
warn("Push tags to 'all' reported failure.") unless ok
|
|
973
1057
|
return
|
|
974
1058
|
end
|
|
975
1059
|
|
|
976
|
-
remotes =
|
|
1060
|
+
remotes = active_remotes
|
|
977
1061
|
remotes -= ["all"] if remotes
|
|
978
1062
|
if remotes.nil? || remotes.empty?
|
|
979
1063
|
ok = @git.push_tags(nil)
|
|
@@ -1021,15 +1105,15 @@ module Kettle
|
|
|
1021
1105
|
end
|
|
1022
1106
|
|
|
1023
1107
|
def github_remote_candidates
|
|
1024
|
-
remotes_with_urls.select { |n, u| u.include?("github.com") }.keys
|
|
1108
|
+
active_remote_candidates(remotes_with_urls.select { |n, u| u.include?("github.com") }.keys)
|
|
1025
1109
|
end
|
|
1026
1110
|
|
|
1027
1111
|
def gitlab_remote_candidates
|
|
1028
|
-
remotes_with_urls.select { |n, u| u.include?("gitlab.com") }.keys
|
|
1112
|
+
active_remote_candidates(remotes_with_urls.select { |n, u| u.include?("gitlab.com") }.keys)
|
|
1029
1113
|
end
|
|
1030
1114
|
|
|
1031
1115
|
def codeberg_remote_candidates
|
|
1032
|
-
remotes_with_urls.select { |n, u| u.include?("codeberg.org") }.keys
|
|
1116
|
+
active_remote_candidates(remotes_with_urls.select { |n, u| u.include?("codeberg.org") }.keys)
|
|
1033
1117
|
end
|
|
1034
1118
|
|
|
1035
1119
|
def preferred_github_remote
|
|
@@ -1052,6 +1136,22 @@ module Kettle
|
|
|
1052
1136
|
list_remotes.include?(name)
|
|
1053
1137
|
end
|
|
1054
1138
|
|
|
1139
|
+
def skipped_remote?(name)
|
|
1140
|
+
@skip_remotes.include?(name)
|
|
1141
|
+
end
|
|
1142
|
+
|
|
1143
|
+
def active_remotes
|
|
1144
|
+
list_remotes.reject { |remote| skipped_remote?(remote) }
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
def active_remote_candidates(candidates)
|
|
1148
|
+
candidates.reject { |remote| skipped_remote?(remote) }
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def use_all_remote?
|
|
1152
|
+
has_remote?("all") && @skip_remotes.empty?
|
|
1153
|
+
end
|
|
1154
|
+
|
|
1055
1155
|
def remote_branch_exists?(remote, branch)
|
|
1056
1156
|
_out, ok = git_output(["show-ref", "--verify", "--quiet", "refs/remotes/#{remote}/#{branch}"])
|
|
1057
1157
|
ok
|
|
@@ -1076,9 +1176,15 @@ module Kettle
|
|
|
1076
1176
|
|
|
1077
1177
|
def ensure_trunk_synced_before_push!(trunk, feature)
|
|
1078
1178
|
if has_remote?("all")
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
remotes
|
|
1179
|
+
remotes = active_remotes
|
|
1180
|
+
skipped = list_remotes.select { |remote| skipped_remote?(remote) }
|
|
1181
|
+
puts "Remote 'all' detected. Fetching from active remotes and enforcing strict trunk parity..."
|
|
1182
|
+
puts "Skipping configured remotes: #{skipped.join(", ")}" unless skipped.empty?
|
|
1183
|
+
remotes.each do |remote|
|
|
1184
|
+
next if remote == "all"
|
|
1185
|
+
|
|
1186
|
+
run_cmd!("git fetch #{Shellwords.escape(remote)}")
|
|
1187
|
+
end
|
|
1082
1188
|
missing_from = []
|
|
1083
1189
|
remotes.each do |r|
|
|
1084
1190
|
next if r == "all"
|
data/lib/kettle/dev/version.rb
CHANGED
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.8
|
|
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
|
|
@@ -313,10 +313,10 @@ licenses:
|
|
|
313
313
|
- AGPL-3.0-only
|
|
314
314
|
metadata:
|
|
315
315
|
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.
|
|
316
|
+
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.3.8
|
|
317
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.3.8/CHANGELOG.md
|
|
318
318
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
|
|
319
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.3.
|
|
319
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.3.8
|
|
320
320
|
funding_uri: https://github.com/sponsors/pboling
|
|
321
321
|
wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
|
|
322
322
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
|
Binary file
|