kettle-dev 2.4.2 → 2.4.4
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 +57 -1
- data/README.md +1 -1
- data/exe/kettle-release +50 -3
- data/lib/kettle/dev/release_cli.rb +244 -19
- data/lib/kettle/dev/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +28 -8
- 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: a4bf3f7184786c2e3511e39e034241b7818591eaf45b86b56cd40a281b582b6a
|
|
4
|
+
data.tar.gz: 305319beff2fe1cab480ae0570e1658afc625282e47a865f8579bc4f880a9846
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c7046d8d77386cebc3f9379735621d8cdb29b7ca9d02cf88933ac2583bc7d55dfdf013835373c1c0997ffb7cbb383d8fcbfb761023805e41b0b2b0fb4e9f2eaf
|
|
7
|
+
data.tar.gz: 751fa7efe9475e49fd0748e9f90875120385bbc98c5ca8364d680c4d549a544b7e1cfc4ca7bf2fa03eefdb00e16151e7769cae3f3c8216f5f8cb19d60d3f63fb
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,58 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [2.4.4] - 2026-07-25
|
|
34
|
+
|
|
35
|
+
- TAG: [v2.4.4][2.4.4t]
|
|
36
|
+
- COVERAGE: 92.01% -- 4079/4433 lines in 38 files
|
|
37
|
+
- BRANCH COVERAGE: 76.04% -- 1587/2087 branches in 38 files
|
|
38
|
+
- 59.16% documented
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- `kettle-release` availability validation now resolves the exact published gem
|
|
43
|
+
from gem.coop without requiring it, so framework-dependent gems are not
|
|
44
|
+
mistaken for unavailable releases after RubyGems accepts the push.
|
|
45
|
+
- `kettle-release` now records a candidate as published immediately after the
|
|
46
|
+
push command succeeds, preventing post-publish validation failures from
|
|
47
|
+
triggering unpublished-gem local cleanup.
|
|
48
|
+
|
|
49
|
+
- Legacy-engine GitHub Actions workflows now configure Bundler to mirror
|
|
50
|
+
gem.coop to RubyGems.org during explicit CI installs, avoiding unsupported
|
|
51
|
+
gem.coop full-index requests from old Bundler/RubyGems combinations.
|
|
52
|
+
|
|
53
|
+
## [2.4.3] - 2026-07-25
|
|
54
|
+
|
|
55
|
+
- TAG: [v2.4.3][2.4.3t]
|
|
56
|
+
- COVERAGE: 92.17% -- 4082/4429 lines in 38 files
|
|
57
|
+
- BRANCH COVERAGE: 76.38% -- 1594/2087 branches in 38 files
|
|
58
|
+
- 59.16% documented
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- `kettle-release` now supports `--events[=TYPES]`, `--json`, and
|
|
63
|
+
`--report PATH` machine-readable release reporting backed by the shared
|
|
64
|
+
`kettle-ndjson` event toolkit.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- kettle-jem-template-20260725-001 - Generated JRuby and TruffleRuby workflow
|
|
69
|
+
files now run when pull request head branches start with `feature/release`,
|
|
70
|
+
so release CI monitoring does not report intentionally skipped engine
|
|
71
|
+
workflows as failures.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- `kettle-release` now retries transient git remote fetch failures during
|
|
76
|
+
strict pre-push trunk parity checks, and failures now include an explicit
|
|
77
|
+
`--skip-remotes` recovery hint for intentionally unavailable remotes.
|
|
78
|
+
- `kettle-release` now waits briefly and retries post-publish gem.coop
|
|
79
|
+
availability validation before failing, so normal registry mirror lag does
|
|
80
|
+
not abort immediately after a successful RubyGems push.
|
|
81
|
+
|
|
82
|
+
- Raised the `kettle-ndjson` runtime dependency floor to v0.1.1 so the
|
|
83
|
+
dependency remains installable on kettle-dev's Ruby 2.4+ support floor.
|
|
84
|
+
|
|
33
85
|
## [2.4.2] - 2026-07-24
|
|
34
86
|
|
|
35
87
|
- TAG: [v2.4.2][2.4.2t]
|
|
@@ -2674,7 +2726,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2674
2726
|
- Selecting will run the selected workflow via `act`
|
|
2675
2727
|
- This may move to its own gem in the future.
|
|
2676
2728
|
|
|
2677
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.
|
|
2729
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.4...HEAD
|
|
2730
|
+
[2.4.4]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.3...v2.4.4
|
|
2731
|
+
[2.4.4t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.4.4
|
|
2732
|
+
[2.4.3]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.2...v2.4.3
|
|
2733
|
+
[2.4.3t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.4.3
|
|
2678
2734
|
[2.4.2]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.1...v2.4.2
|
|
2679
2735
|
[2.4.2t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.4.2
|
|
2680
2736
|
[2.4.1]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.0...v2.4.1
|
data/README.md
CHANGED
|
@@ -891,7 +891,7 @@ Thanks for RTFM. ☺️
|
|
|
891
891
|
[📌gitmoji]: https://gitmoji.dev
|
|
892
892
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
893
893
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
894
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.
|
|
894
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.433-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
895
895
|
[🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
|
|
896
896
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
897
897
|
[📄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
|
@@ -35,8 +35,10 @@ $LOAD_PATH.unshift(repo_lib) if File.directory?(repo_lib) && !$LOAD_PATH.include
|
|
|
35
35
|
begin
|
|
36
36
|
require "kettle/dev"
|
|
37
37
|
require "kettle/dev/executable_version"
|
|
38
|
+
require "kettle/dev/release_cli"
|
|
38
39
|
Kettle::Dev::ExecutableVersion.print_and_exit!(script_basename, ARGV, value_option: true)
|
|
39
|
-
|
|
40
|
+
machine_output = ARGV.any? { |arg| arg == "--json" || arg.start_with?("--events") }
|
|
41
|
+
Kettle::Dev::ExecutableVersion.print_header(script_basename) unless machine_output
|
|
40
42
|
rescue LoadError => e
|
|
41
43
|
warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
|
|
42
44
|
warn("Hint: Ensure the host project has kettle-dev as a dependency and run bundle install.")
|
|
@@ -47,7 +49,7 @@ end
|
|
|
47
49
|
# Do not guard with __FILE__ == $PROGRAM_NAME because binstubs use Kernel.load.
|
|
48
50
|
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
49
51
|
puts <<~USAGE
|
|
50
|
-
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>]
|
|
52
|
+
Usage: kettle-release [--version VERSION] [--local-ci] [--appraisal-update] [--skip-bundle-audit] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [--skip-remotes REMOTES] [--json|--events[=TYPES]] [--report PATH] [start_step=<0-19>]
|
|
51
53
|
|
|
52
54
|
Automates the release flow for a Ruby gem in the host project.
|
|
53
55
|
|
|
@@ -87,6 +89,9 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
87
89
|
--skip-bundle-audit # Skip bundle:audit/update during release rake checks
|
|
88
90
|
--ci-workflows WORKFLOWS # Comma-separated workflow files or stems to monitor at CI step 10
|
|
89
91
|
--skip-remotes REMOTES # Comma-separated git remote names to exclude from release fetch/push parity
|
|
92
|
+
--json # Print final machine-readable release report as JSON
|
|
93
|
+
--events[=TYPES] # Print newline-delimited JSON release events, optionally filtered by type
|
|
94
|
+
--report PATH # Write final machine-readable release report as JSON
|
|
90
95
|
--local-ci # Sensitive release mode: run act locally, publish before any git push,
|
|
91
96
|
# create the git tag locally, then push commits and tags after publish
|
|
92
97
|
|
|
@@ -195,18 +200,60 @@ def extract_skip_remotes_arg!(argv)
|
|
|
195
200
|
remotes
|
|
196
201
|
end
|
|
197
202
|
|
|
203
|
+
def extract_report_arg!(argv)
|
|
204
|
+
report_path = nil
|
|
205
|
+
if (idx = argv.index("--report"))
|
|
206
|
+
report_path = argv[idx + 1]
|
|
207
|
+
if report_path.to_s.empty? || report_path.to_s.start_with?("-")
|
|
208
|
+
Kettle::Dev::ExitAdapter.abort("--report requires a path")
|
|
209
|
+
end
|
|
210
|
+
argv.slice!(idx, 2)
|
|
211
|
+
end
|
|
212
|
+
argv.delete_if do |arg|
|
|
213
|
+
if arg.start_with?("--report=", "report=")
|
|
214
|
+
report_path = arg.split("=", 2)[1]
|
|
215
|
+
Kettle::Dev::ExitAdapter.abort("--report requires a path") if report_path.to_s.empty?
|
|
216
|
+
true
|
|
217
|
+
else
|
|
218
|
+
false
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
report_path
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def extract_events_arg!(argv)
|
|
225
|
+
events = false
|
|
226
|
+
event_types = nil
|
|
227
|
+
argv.delete_if do |arg|
|
|
228
|
+
if arg == "--events"
|
|
229
|
+
events = true
|
|
230
|
+
elsif arg.start_with?("--events=")
|
|
231
|
+
events = true
|
|
232
|
+
event_types = arg.split("=", 2)[1]
|
|
233
|
+
else
|
|
234
|
+
false
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
[events, event_types]
|
|
238
|
+
end
|
|
239
|
+
|
|
198
240
|
local_ci = ARGV.include?("--local-ci")
|
|
199
241
|
appraisal_task = ARGV.delete("--appraisal-update") ? "appraisal:update" : nil
|
|
200
242
|
skip_bundle_audit = !!ARGV.delete("--skip-bundle-audit")
|
|
243
|
+
json_output = !!ARGV.delete("--json")
|
|
244
|
+
events_output, event_types = extract_events_arg!(ARGV)
|
|
245
|
+
Kettle::Dev::ExitAdapter.abort("--events cannot be combined with --json") if events_output && json_output
|
|
201
246
|
start_step_arg = ARGV.find { |a| a.start_with?("start_step=") }
|
|
202
247
|
start_step = start_step_arg ? start_step_arg.split("=", 2)[1].to_i : 0
|
|
203
248
|
version_override = extract_version_arg!(ARGV)
|
|
204
249
|
skip_steps = extract_skip_steps_arg!(ARGV)
|
|
205
250
|
ci_workflows = extract_ci_workflows_arg!(ARGV)
|
|
206
251
|
skip_remotes = extract_skip_remotes_arg!(ARGV)
|
|
252
|
+
report_path = extract_report_arg!(ARGV)
|
|
207
253
|
|
|
208
254
|
begin
|
|
209
|
-
Kettle::
|
|
255
|
+
event_stream = events_output ? Kettle::Ndjson.event_stream($stdout, types: event_types) : nil
|
|
256
|
+
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, event_stream: event_stream, json_output: json_output, json_io: $stdout, report_path: report_path).run
|
|
210
257
|
rescue LoadError => e
|
|
211
258
|
warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
|
|
212
259
|
warn(Array(e.backtrace).join("\n")) if ENV["DEBUG"]
|
|
@@ -13,6 +13,7 @@ require "yaml"
|
|
|
13
13
|
require "set"
|
|
14
14
|
|
|
15
15
|
# External gems
|
|
16
|
+
require "kettle/ndjson"
|
|
16
17
|
require "kettle/rb/compat_matrix"
|
|
17
18
|
require "ruby-progressbar"
|
|
18
19
|
|
|
@@ -124,14 +125,43 @@ module Kettle
|
|
|
124
125
|
@version_override = Kettle::Dev::Versioning.normalize_explicit_version(version)
|
|
125
126
|
@appraisal_task = normalize_appraisal_task(appraisal_task || ENV["KETTLE_RELEASE_APPRAISAL_TASK"])
|
|
126
127
|
@release_candidate = nil
|
|
128
|
+
@event_stream = options[:event_stream]
|
|
129
|
+
@event_recorder = Kettle::Ndjson.event_recorder(@event_stream, phase_timings: [])
|
|
130
|
+
@report_path = options[:report_path]
|
|
131
|
+
@json_output = !!options[:json_output]
|
|
132
|
+
@json_io = options[:json_io] || $stdout
|
|
133
|
+
@command_events = []
|
|
134
|
+
@diagnostics = []
|
|
135
|
+
@started_at = nil
|
|
136
|
+
@finished_report = nil
|
|
127
137
|
end
|
|
128
138
|
|
|
129
139
|
def run
|
|
140
|
+
@started_at = monotonic_time
|
|
141
|
+
emit_run_start
|
|
142
|
+
status = "ok"
|
|
143
|
+
error = nil
|
|
130
144
|
with_bundle_audit_skip_env do
|
|
131
|
-
|
|
145
|
+
with_machine_stdout_redirect do
|
|
146
|
+
run_with_release_environment
|
|
147
|
+
end
|
|
132
148
|
end
|
|
149
|
+
rescue SystemExit => e
|
|
150
|
+
status = e.status.to_i.zero? ? "ok" : "failed"
|
|
151
|
+
error = e
|
|
152
|
+
record_diagnostic("release_exit", e.message, severity: (status == "ok") ? "info" : "error", blocking: status != "ok")
|
|
153
|
+
raise
|
|
154
|
+
rescue => e
|
|
155
|
+
status = "failed"
|
|
156
|
+
error = e
|
|
157
|
+
record_diagnostic("release_error", "#{e.class}: #{e.message}", severity: "error", blocking: true)
|
|
158
|
+
raise
|
|
159
|
+
ensure
|
|
160
|
+
finish_release_report(status: status, error: error)
|
|
133
161
|
end
|
|
134
162
|
|
|
163
|
+
attr_reader :finished_report
|
|
164
|
+
|
|
135
165
|
def run_with_release_environment
|
|
136
166
|
run_pre_release_checks! if run_step?(0)
|
|
137
167
|
|
|
@@ -358,6 +388,7 @@ module Kettle
|
|
|
358
388
|
puts "Running release (you may be prompted for signing key password and RubyGems MFA OTP)..."
|
|
359
389
|
with_unpublished_candidate_cleanup do
|
|
360
390
|
run_cmd!("bundle exec rake release")
|
|
391
|
+
@release_candidate.published = true
|
|
361
392
|
confirm_release_candidate_available!(@release_candidate)
|
|
362
393
|
end
|
|
363
394
|
mark_gem_coop_release_cache_bust(version)
|
|
@@ -395,11 +426,11 @@ module Kettle
|
|
|
395
426
|
begin
|
|
396
427
|
version ||= detect_version
|
|
397
428
|
gem_name = detect_gem_name
|
|
398
|
-
puts "\n🚀 Release #{gem_name} v#{version} Complete 🚀"
|
|
429
|
+
human_output.puts "\n🚀 Release #{gem_name} v#{version} Complete 🚀"
|
|
399
430
|
rescue => e
|
|
400
431
|
Kettle::Dev.debug_error(e, __method__)
|
|
401
432
|
# Fallback if detection fails for any reason
|
|
402
|
-
puts "\n🚀 Release v#{version || "unknown"} Complete 🚀"
|
|
433
|
+
human_output.puts "\n🚀 Release v#{version || "unknown"} Complete 🚀"
|
|
403
434
|
end
|
|
404
435
|
end
|
|
405
436
|
|
|
@@ -1007,9 +1038,19 @@ module Kettle
|
|
|
1007
1038
|
|
|
1008
1039
|
def run_cmd!(cmd)
|
|
1009
1040
|
cmd = bundle_audit_skip_command(cmd)
|
|
1041
|
+
emit_command_event(cmd, "started")
|
|
1010
1042
|
with_bundle_audit_skip_env do
|
|
1011
|
-
|
|
1043
|
+
with_machine_stdout_redirect do
|
|
1044
|
+
self.class.run_cmd!(cmd)
|
|
1045
|
+
end
|
|
1012
1046
|
end
|
|
1047
|
+
emit_command_event(cmd, "ok")
|
|
1048
|
+
rescue SystemExit => e
|
|
1049
|
+
emit_command_event(cmd, "failed", reason: e.message)
|
|
1050
|
+
raise
|
|
1051
|
+
rescue => e
|
|
1052
|
+
emit_command_event(cmd, "failed", reason: "#{e.class}: #{e.message}")
|
|
1053
|
+
raise
|
|
1013
1054
|
end
|
|
1014
1055
|
|
|
1015
1056
|
def bundle_audit_skip_command(cmd)
|
|
@@ -1152,8 +1193,11 @@ module Kettle
|
|
|
1152
1193
|
|
|
1153
1194
|
puts "Publishing #{File.basename(gem_path)} to RubyGems without pushing git refs..."
|
|
1154
1195
|
run_cmd!("gem push #{Shellwords.escape(gem_path)}")
|
|
1155
|
-
|
|
1156
|
-
|
|
1196
|
+
gem_name = gem_name_from_gem_path(gem_path, version)
|
|
1197
|
+
@release_candidate ||= build_release_candidate(gem_name, version)
|
|
1198
|
+
@release_candidate.published = true
|
|
1199
|
+
confirm_release_candidate_available!(@release_candidate)
|
|
1200
|
+
mark_gem_coop_release_cache_bust(version, gem_name: gem_name)
|
|
1157
1201
|
end
|
|
1158
1202
|
|
|
1159
1203
|
def build_release_candidate(gem_name, version)
|
|
@@ -1218,21 +1262,45 @@ module Kettle
|
|
|
1218
1262
|
|
|
1219
1263
|
def run_release_availability_probe(candidate)
|
|
1220
1264
|
script_path = write_release_availability_probe(candidate)
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1265
|
+
attempts = release_availability_probe_attempts
|
|
1266
|
+
last_stderr = nil
|
|
1267
|
+
last_status = nil
|
|
1268
|
+
sleep(release_availability_probe_initial_delay)
|
|
1269
|
+
attempts.times do |index|
|
|
1270
|
+
attempt = index + 1
|
|
1271
|
+
puts("Validating #{candidate.gem_name} #{candidate.version} from #{RELEASE_VALIDATION_SOURCE} (attempt #{attempt}/#{attempts})")
|
|
1272
|
+
stdout_str = nil
|
|
1273
|
+
stderr_str = nil
|
|
1274
|
+
status = nil
|
|
1275
|
+
with_unbundled_release_probe_env do
|
|
1276
|
+
stdout_str, stderr_str, status = Open3.capture3(self.class.send(:command_env), Gem.ruby, script_path)
|
|
1277
|
+
end
|
|
1278
|
+
$stdout.print(stdout_str) unless stdout_str.to_s.empty?
|
|
1279
|
+
return true if status.success?
|
|
1280
|
+
|
|
1281
|
+
last_stderr = stderr_str
|
|
1282
|
+
last_status = status
|
|
1283
|
+
sleep(release_availability_probe_interval) if attempt < attempts
|
|
1226
1284
|
end
|
|
1227
|
-
$stdout.print(stdout_str) unless stdout_str.to_s.empty?
|
|
1228
|
-
return true if status.success?
|
|
1229
1285
|
|
|
1230
|
-
diag =
|
|
1231
|
-
abort("Published gem availability probe failed for #{candidate.gem_name} #{candidate.version} (exit #{
|
|
1286
|
+
diag = last_stderr.to_s.empty? ? "" : "\n--- STDERR ---\n#{last_stderr}".rstrip
|
|
1287
|
+
abort("Published gem availability probe failed for #{candidate.gem_name} #{candidate.version} after #{attempts} attempt(s) (exit #{last_status.exitstatus})#{diag}")
|
|
1232
1288
|
ensure
|
|
1233
1289
|
FileUtils.rm_f(script_path) if script_path
|
|
1234
1290
|
end
|
|
1235
1291
|
|
|
1292
|
+
def release_availability_probe_attempts
|
|
1293
|
+
15
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
def release_availability_probe_initial_delay
|
|
1297
|
+
5
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1300
|
+
def release_availability_probe_interval
|
|
1301
|
+
10
|
|
1302
|
+
end
|
|
1303
|
+
|
|
1236
1304
|
def with_unbundled_release_probe_env(&block)
|
|
1237
1305
|
if defined?(Bundler)
|
|
1238
1306
|
Bundler.with_unbundled_env(&block)
|
|
@@ -1260,11 +1328,12 @@ module Kettle
|
|
|
1260
1328
|
|
|
1261
1329
|
gemfile(true) do
|
|
1262
1330
|
source #{RELEASE_VALIDATION_SOURCE.dump}
|
|
1263
|
-
gem gem_name, "= \#{version}"
|
|
1331
|
+
gem gem_name, "= \#{version}", require: false
|
|
1264
1332
|
end
|
|
1265
1333
|
|
|
1266
|
-
spec =
|
|
1267
|
-
abort("
|
|
1334
|
+
spec = Bundler.load.specs.find { |candidate| candidate.name == gem_name }
|
|
1335
|
+
abort("resolved no \#{gem_name} spec") unless spec
|
|
1336
|
+
abort("resolved \#{gem_name} \#{spec.version}, expected \#{version}") unless spec.version.to_s == version
|
|
1268
1337
|
puts "Validated \#{gem_name} \#{version} from #{RELEASE_VALIDATION_SOURCE}"
|
|
1269
1338
|
RUBY
|
|
1270
1339
|
end
|
|
@@ -1369,6 +1438,118 @@ module Kettle
|
|
|
1369
1438
|
end
|
|
1370
1439
|
end
|
|
1371
1440
|
|
|
1441
|
+
def emit_run_start
|
|
1442
|
+
Kettle::Ndjson.emit_event(
|
|
1443
|
+
@event_recorder,
|
|
1444
|
+
"run_start",
|
|
1445
|
+
command: "release",
|
|
1446
|
+
root: @root,
|
|
1447
|
+
start_step: @start_step,
|
|
1448
|
+
skipped_steps: @skip_steps,
|
|
1449
|
+
local_ci: @local_ci,
|
|
1450
|
+
ci_workflows: @ci_workflows,
|
|
1451
|
+
skipped_remotes: @skip_remotes
|
|
1452
|
+
)
|
|
1453
|
+
end
|
|
1454
|
+
|
|
1455
|
+
def emit_command_event(command, status, reason: nil)
|
|
1456
|
+
event = {
|
|
1457
|
+
phase: "release",
|
|
1458
|
+
index: @command_events.length + 1,
|
|
1459
|
+
name: command_event_name(command),
|
|
1460
|
+
status: status,
|
|
1461
|
+
reason: reason,
|
|
1462
|
+
command: command,
|
|
1463
|
+
changed_files: []
|
|
1464
|
+
}
|
|
1465
|
+
@command_events << event unless status == "started"
|
|
1466
|
+
Kettle::Ndjson.emit_step_event(@event_recorder, "command_step", event, phase: "release", index: event[:index])
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
def command_event_name(command)
|
|
1470
|
+
command.to_s.split(/\s+/, 3).first(2).join("_").gsub(/[^A-Za-z0-9_]+/, "_").sub(/_+\z/, "")
|
|
1471
|
+
end
|
|
1472
|
+
|
|
1473
|
+
def record_diagnostic(kind, message, severity:, blocking:)
|
|
1474
|
+
diagnostic = {
|
|
1475
|
+
kind: kind,
|
|
1476
|
+
severity: severity,
|
|
1477
|
+
message: message.to_s,
|
|
1478
|
+
blocking: blocking
|
|
1479
|
+
}
|
|
1480
|
+
@diagnostics << diagnostic
|
|
1481
|
+
Kettle::Ndjson.emit_diagnostic_event(@event_recorder, diagnostic, index: @diagnostics.length)
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
def finish_release_report(status:, error:)
|
|
1485
|
+
return if @finished_report
|
|
1486
|
+
|
|
1487
|
+
duration_ms = @started_at ? duration_ms_since(@started_at) : nil
|
|
1488
|
+
@finished_report = {
|
|
1489
|
+
command: "release",
|
|
1490
|
+
root: @root,
|
|
1491
|
+
version: safe_detect_version,
|
|
1492
|
+
gem_name: safe_detect_gem_name,
|
|
1493
|
+
start_step: @start_step,
|
|
1494
|
+
skipped_steps: @skip_steps,
|
|
1495
|
+
local_ci: @local_ci,
|
|
1496
|
+
ci_workflows: @ci_workflows,
|
|
1497
|
+
skipped_remotes: @skip_remotes,
|
|
1498
|
+
status: status,
|
|
1499
|
+
error_class: error&.class&.name,
|
|
1500
|
+
error_message: error&.message,
|
|
1501
|
+
duration_ms: duration_ms,
|
|
1502
|
+
command_events: @command_events,
|
|
1503
|
+
diagnostics: @diagnostics,
|
|
1504
|
+
diagnostics_count: @diagnostics.length,
|
|
1505
|
+
phase_timings: @event_recorder.phase_timings
|
|
1506
|
+
}.compact
|
|
1507
|
+
Kettle::Ndjson.emit_summary_event(@event_recorder, @finished_report)
|
|
1508
|
+
write_release_report(@finished_report)
|
|
1509
|
+
@json_io.puts(JSON.pretty_generate(@finished_report)) if @json_output
|
|
1510
|
+
end
|
|
1511
|
+
|
|
1512
|
+
def write_release_report(report)
|
|
1513
|
+
return unless @report_path
|
|
1514
|
+
|
|
1515
|
+
FileUtils.mkdir_p(File.dirname(@report_path))
|
|
1516
|
+
File.write(@report_path, "#{JSON.pretty_generate(report)}\n")
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
def with_machine_stdout_redirect
|
|
1520
|
+
return yield unless @json_output || @event_stream
|
|
1521
|
+
|
|
1522
|
+
previous_stdout = $stdout
|
|
1523
|
+
$stdout = $stderr
|
|
1524
|
+
yield
|
|
1525
|
+
ensure
|
|
1526
|
+
$stdout = previous_stdout if previous_stdout
|
|
1527
|
+
end
|
|
1528
|
+
|
|
1529
|
+
def human_output
|
|
1530
|
+
(@json_output || @event_stream) ? $stderr : $stdout
|
|
1531
|
+
end
|
|
1532
|
+
|
|
1533
|
+
def safe_detect_version
|
|
1534
|
+
detect_version
|
|
1535
|
+
rescue
|
|
1536
|
+
nil
|
|
1537
|
+
end
|
|
1538
|
+
|
|
1539
|
+
def safe_detect_gem_name
|
|
1540
|
+
detect_gem_name
|
|
1541
|
+
rescue
|
|
1542
|
+
nil
|
|
1543
|
+
end
|
|
1544
|
+
|
|
1545
|
+
def monotonic_time
|
|
1546
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1547
|
+
end
|
|
1548
|
+
|
|
1549
|
+
def duration_ms_since(started_at)
|
|
1550
|
+
((monotonic_time - started_at) * 1000).round(3)
|
|
1551
|
+
end
|
|
1552
|
+
|
|
1372
1553
|
def push_tags!
|
|
1373
1554
|
# After release, push tags to remotes according to policy:
|
|
1374
1555
|
# 1) If a remote named "all" exists, push tags only to it.
|
|
@@ -1506,7 +1687,7 @@ module Kettle
|
|
|
1506
1687
|
remotes.each do |remote|
|
|
1507
1688
|
next if remote == "all"
|
|
1508
1689
|
|
|
1509
|
-
|
|
1690
|
+
fetch_remote_for_parity!(remote)
|
|
1510
1691
|
end
|
|
1511
1692
|
missing_from = []
|
|
1512
1693
|
remotes.each do |r|
|
|
@@ -1582,6 +1763,50 @@ module Kettle
|
|
|
1582
1763
|
end
|
|
1583
1764
|
end
|
|
1584
1765
|
|
|
1766
|
+
def fetch_remote_for_parity!(remote)
|
|
1767
|
+
command = "git fetch #{Shellwords.escape(remote)}"
|
|
1768
|
+
attempts = remote_fetch_parity_attempts
|
|
1769
|
+
last_error = nil
|
|
1770
|
+
attempts.times do |index|
|
|
1771
|
+
attempt = index + 1
|
|
1772
|
+
puts "Fetching remote '#{remote}' for release parity (attempt #{attempt}/#{attempts})"
|
|
1773
|
+
begin
|
|
1774
|
+
run_cmd!(command)
|
|
1775
|
+
return true
|
|
1776
|
+
rescue SystemExit => error
|
|
1777
|
+
last_error = error
|
|
1778
|
+
rescue => error
|
|
1779
|
+
last_error = error
|
|
1780
|
+
end
|
|
1781
|
+
sleep(remote_fetch_parity_interval) if attempt < attempts
|
|
1782
|
+
end
|
|
1783
|
+
|
|
1784
|
+
abort(remote_fetch_failure_message(remote, last_error&.message.to_s))
|
|
1785
|
+
end
|
|
1786
|
+
|
|
1787
|
+
def remote_fetch_parity_attempts
|
|
1788
|
+
3
|
|
1789
|
+
end
|
|
1790
|
+
|
|
1791
|
+
def remote_fetch_parity_interval
|
|
1792
|
+
5
|
|
1793
|
+
end
|
|
1794
|
+
|
|
1795
|
+
def remote_fetch_failure_message(remote, detail)
|
|
1796
|
+
<<~MSG
|
|
1797
|
+
Unable to fetch git remote '#{remote}' during release parity checks.
|
|
1798
|
+
kettle-release enforces trunk parity across active remotes before publishing.
|
|
1799
|
+
|
|
1800
|
+
If this remote is temporarily unavailable or intentionally not part of this release,
|
|
1801
|
+
rerun with one of:
|
|
1802
|
+
kettle-release --skip-remotes #{remote}
|
|
1803
|
+
K_RELEASE_SKIP_REMOTES=#{remote} kettle-release
|
|
1804
|
+
|
|
1805
|
+
Original failure:
|
|
1806
|
+
#{detail}
|
|
1807
|
+
MSG
|
|
1808
|
+
end
|
|
1809
|
+
|
|
1585
1810
|
def merge_feature_into_trunk_and_push!(trunk, feature)
|
|
1586
1811
|
return if feature.nil? || feature == trunk
|
|
1587
1812
|
|
data/lib/kettle/dev/version.rb
CHANGED
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.4.
|
|
4
|
+
version: 2.4.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -71,6 +71,26 @@ dependencies:
|
|
|
71
71
|
- - ">="
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
73
|
version: 0.3.1
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: kettle-ndjson
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - "~>"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '0.1'
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: 0.1.1
|
|
84
|
+
type: :runtime
|
|
85
|
+
prerelease: false
|
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - "~>"
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0.1'
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: 0.1.1
|
|
74
94
|
- !ruby/object:Gem::Dependency
|
|
75
95
|
name: kettle-rb
|
|
76
96
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -216,7 +236,7 @@ dependencies:
|
|
|
216
236
|
version: '3.2'
|
|
217
237
|
- - ">="
|
|
218
238
|
- !ruby/object:Gem::Version
|
|
219
|
-
version: 3.2.
|
|
239
|
+
version: 3.2.1
|
|
220
240
|
type: :development
|
|
221
241
|
prerelease: false
|
|
222
242
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -226,7 +246,7 @@ dependencies:
|
|
|
226
246
|
version: '3.2'
|
|
227
247
|
- - ">="
|
|
228
248
|
- !ruby/object:Gem::Version
|
|
229
|
-
version: 3.2.
|
|
249
|
+
version: 3.2.1
|
|
230
250
|
- !ruby/object:Gem::Dependency
|
|
231
251
|
name: stone_checksums
|
|
232
252
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -256,7 +276,7 @@ dependencies:
|
|
|
256
276
|
version: '2.0'
|
|
257
277
|
- - ">="
|
|
258
278
|
- !ruby/object:Gem::Version
|
|
259
|
-
version: 2.0.
|
|
279
|
+
version: 2.0.5
|
|
260
280
|
type: :development
|
|
261
281
|
prerelease: false
|
|
262
282
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -266,7 +286,7 @@ dependencies:
|
|
|
266
286
|
version: '2.0'
|
|
267
287
|
- - ">="
|
|
268
288
|
- !ruby/object:Gem::Version
|
|
269
|
-
version: 2.0.
|
|
289
|
+
version: 2.0.5
|
|
270
290
|
description: "\U0001F372 Kettle::Dev is a meta tool from kettle-dev to streamline
|
|
271
291
|
development and testing. Acts as a shim dependency, pulling in many other dependencies,
|
|
272
292
|
to give you OOTB productivity with a RubyGem, or Ruby app project. Configures a
|
|
@@ -349,10 +369,10 @@ licenses:
|
|
|
349
369
|
- AGPL-3.0-only
|
|
350
370
|
metadata:
|
|
351
371
|
homepage_uri: https://kettle-dev.galtzo.com
|
|
352
|
-
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.4.
|
|
353
|
-
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.4.
|
|
372
|
+
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.4.4
|
|
373
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.4.4/CHANGELOG.md
|
|
354
374
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
|
|
355
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.4.
|
|
375
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.4.4
|
|
356
376
|
funding_uri: https://github.com/sponsors/pboling
|
|
357
377
|
wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
|
|
358
378
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
|
Binary file
|