kettle-dev 2.4.2 → 2.4.3
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 +1 -1
- data/exe/kettle-release +50 -3
- data/lib/kettle/dev/release_cli.rb +234 -14
- data/lib/kettle/dev/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +24 -4
- 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: 0764aba0c8669f0c075868db0109a393fb508835db1540ac2b1f3cdf0c1b4cd0
|
|
4
|
+
data.tar.gz: a5d418a258af62045142025f314820330eaa18dba697036e2c4861bbb3874fad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4edf261a1f8819f89890b26eb1eb3666e388e771dde67bee0aa70d97d7920a7719493c0786267a6e651744c0a9c62462efa690105d0f7e124b612f386da9d09
|
|
7
|
+
data.tar.gz: 32423487dc0b9ea6af5d22695785f7d2ba2620b8128d6629f922f93433090d71600c4105ca5a1eecb96640c55699405edb78fea174e8bac46ef3eee1439e36cd
|
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.4.3] - 2026-07-25
|
|
34
|
+
|
|
35
|
+
- TAG: [v2.4.3][2.4.3t]
|
|
36
|
+
- COVERAGE: 92.17% -- 4082/4429 lines in 38 files
|
|
37
|
+
- BRANCH COVERAGE: 76.38% -- 1594/2087 branches in 38 files
|
|
38
|
+
- 59.16% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `kettle-release` now supports `--events[=TYPES]`, `--json`, and
|
|
43
|
+
`--report PATH` machine-readable release reporting backed by the shared
|
|
44
|
+
`kettle-ndjson` event toolkit.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- kettle-jem-template-20260725-001 - Generated JRuby and TruffleRuby workflow
|
|
49
|
+
files now run when pull request head branches start with `feature/release`,
|
|
50
|
+
so release CI monitoring does not report intentionally skipped engine
|
|
51
|
+
workflows as failures.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- `kettle-release` now retries transient git remote fetch failures during
|
|
56
|
+
strict pre-push trunk parity checks, and failures now include an explicit
|
|
57
|
+
`--skip-remotes` recovery hint for intentionally unavailable remotes.
|
|
58
|
+
- `kettle-release` now waits briefly and retries post-publish gem.coop
|
|
59
|
+
availability validation before failing, so normal registry mirror lag does
|
|
60
|
+
not abort immediately after a successful RubyGems push.
|
|
61
|
+
|
|
62
|
+
- Raised the `kettle-ndjson` runtime dependency floor to v0.1.1 so the
|
|
63
|
+
dependency remains installable on kettle-dev's Ruby 2.4+ support floor.
|
|
64
|
+
|
|
33
65
|
## [2.4.2] - 2026-07-24
|
|
34
66
|
|
|
35
67
|
- TAG: [v2.4.2][2.4.2t]
|
|
@@ -2674,7 +2706,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2674
2706
|
- Selecting will run the selected workflow via `act`
|
|
2675
2707
|
- This may move to its own gem in the future.
|
|
2676
2708
|
|
|
2677
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.
|
|
2709
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.3...HEAD
|
|
2710
|
+
[2.4.3]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.2...v2.4.3
|
|
2711
|
+
[2.4.3t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.4.3
|
|
2678
2712
|
[2.4.2]: https://github.com/kettle-dev/kettle-dev/compare/v2.4.1...v2.4.2
|
|
2679
2713
|
[2.4.2t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.4.2
|
|
2680
2714
|
[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.429-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
|
|
|
@@ -395,11 +425,11 @@ module Kettle
|
|
|
395
425
|
begin
|
|
396
426
|
version ||= detect_version
|
|
397
427
|
gem_name = detect_gem_name
|
|
398
|
-
puts "\n🚀 Release #{gem_name} v#{version} Complete 🚀"
|
|
428
|
+
human_output.puts "\n🚀 Release #{gem_name} v#{version} Complete 🚀"
|
|
399
429
|
rescue => e
|
|
400
430
|
Kettle::Dev.debug_error(e, __method__)
|
|
401
431
|
# Fallback if detection fails for any reason
|
|
402
|
-
puts "\n🚀 Release v#{version || "unknown"} Complete 🚀"
|
|
432
|
+
human_output.puts "\n🚀 Release v#{version || "unknown"} Complete 🚀"
|
|
403
433
|
end
|
|
404
434
|
end
|
|
405
435
|
|
|
@@ -1007,9 +1037,19 @@ module Kettle
|
|
|
1007
1037
|
|
|
1008
1038
|
def run_cmd!(cmd)
|
|
1009
1039
|
cmd = bundle_audit_skip_command(cmd)
|
|
1040
|
+
emit_command_event(cmd, "started")
|
|
1010
1041
|
with_bundle_audit_skip_env do
|
|
1011
|
-
|
|
1042
|
+
with_machine_stdout_redirect do
|
|
1043
|
+
self.class.run_cmd!(cmd)
|
|
1044
|
+
end
|
|
1012
1045
|
end
|
|
1046
|
+
emit_command_event(cmd, "ok")
|
|
1047
|
+
rescue SystemExit => e
|
|
1048
|
+
emit_command_event(cmd, "failed", reason: e.message)
|
|
1049
|
+
raise
|
|
1050
|
+
rescue => e
|
|
1051
|
+
emit_command_event(cmd, "failed", reason: "#{e.class}: #{e.message}")
|
|
1052
|
+
raise
|
|
1013
1053
|
end
|
|
1014
1054
|
|
|
1015
1055
|
def bundle_audit_skip_command(cmd)
|
|
@@ -1218,21 +1258,45 @@ module Kettle
|
|
|
1218
1258
|
|
|
1219
1259
|
def run_release_availability_probe(candidate)
|
|
1220
1260
|
script_path = write_release_availability_probe(candidate)
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1261
|
+
attempts = release_availability_probe_attempts
|
|
1262
|
+
last_stderr = nil
|
|
1263
|
+
last_status = nil
|
|
1264
|
+
sleep(release_availability_probe_initial_delay)
|
|
1265
|
+
attempts.times do |index|
|
|
1266
|
+
attempt = index + 1
|
|
1267
|
+
puts("Validating #{candidate.gem_name} #{candidate.version} from #{RELEASE_VALIDATION_SOURCE} (attempt #{attempt}/#{attempts})")
|
|
1268
|
+
stdout_str = nil
|
|
1269
|
+
stderr_str = nil
|
|
1270
|
+
status = nil
|
|
1271
|
+
with_unbundled_release_probe_env do
|
|
1272
|
+
stdout_str, stderr_str, status = Open3.capture3(self.class.send(:command_env), Gem.ruby, script_path)
|
|
1273
|
+
end
|
|
1274
|
+
$stdout.print(stdout_str) unless stdout_str.to_s.empty?
|
|
1275
|
+
return true if status.success?
|
|
1276
|
+
|
|
1277
|
+
last_stderr = stderr_str
|
|
1278
|
+
last_status = status
|
|
1279
|
+
sleep(release_availability_probe_interval) if attempt < attempts
|
|
1226
1280
|
end
|
|
1227
|
-
$stdout.print(stdout_str) unless stdout_str.to_s.empty?
|
|
1228
|
-
return true if status.success?
|
|
1229
1281
|
|
|
1230
|
-
diag =
|
|
1231
|
-
abort("Published gem availability probe failed for #{candidate.gem_name} #{candidate.version} (exit #{
|
|
1282
|
+
diag = last_stderr.to_s.empty? ? "" : "\n--- STDERR ---\n#{last_stderr}".rstrip
|
|
1283
|
+
abort("Published gem availability probe failed for #{candidate.gem_name} #{candidate.version} after #{attempts} attempt(s) (exit #{last_status.exitstatus})#{diag}")
|
|
1232
1284
|
ensure
|
|
1233
1285
|
FileUtils.rm_f(script_path) if script_path
|
|
1234
1286
|
end
|
|
1235
1287
|
|
|
1288
|
+
def release_availability_probe_attempts
|
|
1289
|
+
15
|
|
1290
|
+
end
|
|
1291
|
+
|
|
1292
|
+
def release_availability_probe_initial_delay
|
|
1293
|
+
5
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
def release_availability_probe_interval
|
|
1297
|
+
10
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1236
1300
|
def with_unbundled_release_probe_env(&block)
|
|
1237
1301
|
if defined?(Bundler)
|
|
1238
1302
|
Bundler.with_unbundled_env(&block)
|
|
@@ -1369,6 +1433,118 @@ module Kettle
|
|
|
1369
1433
|
end
|
|
1370
1434
|
end
|
|
1371
1435
|
|
|
1436
|
+
def emit_run_start
|
|
1437
|
+
Kettle::Ndjson.emit_event(
|
|
1438
|
+
@event_recorder,
|
|
1439
|
+
"run_start",
|
|
1440
|
+
command: "release",
|
|
1441
|
+
root: @root,
|
|
1442
|
+
start_step: @start_step,
|
|
1443
|
+
skipped_steps: @skip_steps,
|
|
1444
|
+
local_ci: @local_ci,
|
|
1445
|
+
ci_workflows: @ci_workflows,
|
|
1446
|
+
skipped_remotes: @skip_remotes
|
|
1447
|
+
)
|
|
1448
|
+
end
|
|
1449
|
+
|
|
1450
|
+
def emit_command_event(command, status, reason: nil)
|
|
1451
|
+
event = {
|
|
1452
|
+
phase: "release",
|
|
1453
|
+
index: @command_events.length + 1,
|
|
1454
|
+
name: command_event_name(command),
|
|
1455
|
+
status: status,
|
|
1456
|
+
reason: reason,
|
|
1457
|
+
command: command,
|
|
1458
|
+
changed_files: []
|
|
1459
|
+
}
|
|
1460
|
+
@command_events << event unless status == "started"
|
|
1461
|
+
Kettle::Ndjson.emit_step_event(@event_recorder, "command_step", event, phase: "release", index: event[:index])
|
|
1462
|
+
end
|
|
1463
|
+
|
|
1464
|
+
def command_event_name(command)
|
|
1465
|
+
command.to_s.split(/\s+/, 3).first(2).join("_").gsub(/[^A-Za-z0-9_]+/, "_").sub(/_+\z/, "")
|
|
1466
|
+
end
|
|
1467
|
+
|
|
1468
|
+
def record_diagnostic(kind, message, severity:, blocking:)
|
|
1469
|
+
diagnostic = {
|
|
1470
|
+
kind: kind,
|
|
1471
|
+
severity: severity,
|
|
1472
|
+
message: message.to_s,
|
|
1473
|
+
blocking: blocking
|
|
1474
|
+
}
|
|
1475
|
+
@diagnostics << diagnostic
|
|
1476
|
+
Kettle::Ndjson.emit_diagnostic_event(@event_recorder, diagnostic, index: @diagnostics.length)
|
|
1477
|
+
end
|
|
1478
|
+
|
|
1479
|
+
def finish_release_report(status:, error:)
|
|
1480
|
+
return if @finished_report
|
|
1481
|
+
|
|
1482
|
+
duration_ms = @started_at ? duration_ms_since(@started_at) : nil
|
|
1483
|
+
@finished_report = {
|
|
1484
|
+
command: "release",
|
|
1485
|
+
root: @root,
|
|
1486
|
+
version: safe_detect_version,
|
|
1487
|
+
gem_name: safe_detect_gem_name,
|
|
1488
|
+
start_step: @start_step,
|
|
1489
|
+
skipped_steps: @skip_steps,
|
|
1490
|
+
local_ci: @local_ci,
|
|
1491
|
+
ci_workflows: @ci_workflows,
|
|
1492
|
+
skipped_remotes: @skip_remotes,
|
|
1493
|
+
status: status,
|
|
1494
|
+
error_class: error&.class&.name,
|
|
1495
|
+
error_message: error&.message,
|
|
1496
|
+
duration_ms: duration_ms,
|
|
1497
|
+
command_events: @command_events,
|
|
1498
|
+
diagnostics: @diagnostics,
|
|
1499
|
+
diagnostics_count: @diagnostics.length,
|
|
1500
|
+
phase_timings: @event_recorder.phase_timings
|
|
1501
|
+
}.compact
|
|
1502
|
+
Kettle::Ndjson.emit_summary_event(@event_recorder, @finished_report)
|
|
1503
|
+
write_release_report(@finished_report)
|
|
1504
|
+
@json_io.puts(JSON.pretty_generate(@finished_report)) if @json_output
|
|
1505
|
+
end
|
|
1506
|
+
|
|
1507
|
+
def write_release_report(report)
|
|
1508
|
+
return unless @report_path
|
|
1509
|
+
|
|
1510
|
+
FileUtils.mkdir_p(File.dirname(@report_path))
|
|
1511
|
+
File.write(@report_path, "#{JSON.pretty_generate(report)}\n")
|
|
1512
|
+
end
|
|
1513
|
+
|
|
1514
|
+
def with_machine_stdout_redirect
|
|
1515
|
+
return yield unless @json_output || @event_stream
|
|
1516
|
+
|
|
1517
|
+
previous_stdout = $stdout
|
|
1518
|
+
$stdout = $stderr
|
|
1519
|
+
yield
|
|
1520
|
+
ensure
|
|
1521
|
+
$stdout = previous_stdout if previous_stdout
|
|
1522
|
+
end
|
|
1523
|
+
|
|
1524
|
+
def human_output
|
|
1525
|
+
(@json_output || @event_stream) ? $stderr : $stdout
|
|
1526
|
+
end
|
|
1527
|
+
|
|
1528
|
+
def safe_detect_version
|
|
1529
|
+
detect_version
|
|
1530
|
+
rescue
|
|
1531
|
+
nil
|
|
1532
|
+
end
|
|
1533
|
+
|
|
1534
|
+
def safe_detect_gem_name
|
|
1535
|
+
detect_gem_name
|
|
1536
|
+
rescue
|
|
1537
|
+
nil
|
|
1538
|
+
end
|
|
1539
|
+
|
|
1540
|
+
def monotonic_time
|
|
1541
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1542
|
+
end
|
|
1543
|
+
|
|
1544
|
+
def duration_ms_since(started_at)
|
|
1545
|
+
((monotonic_time - started_at) * 1000).round(3)
|
|
1546
|
+
end
|
|
1547
|
+
|
|
1372
1548
|
def push_tags!
|
|
1373
1549
|
# After release, push tags to remotes according to policy:
|
|
1374
1550
|
# 1) If a remote named "all" exists, push tags only to it.
|
|
@@ -1506,7 +1682,7 @@ module Kettle
|
|
|
1506
1682
|
remotes.each do |remote|
|
|
1507
1683
|
next if remote == "all"
|
|
1508
1684
|
|
|
1509
|
-
|
|
1685
|
+
fetch_remote_for_parity!(remote)
|
|
1510
1686
|
end
|
|
1511
1687
|
missing_from = []
|
|
1512
1688
|
remotes.each do |r|
|
|
@@ -1582,6 +1758,50 @@ module Kettle
|
|
|
1582
1758
|
end
|
|
1583
1759
|
end
|
|
1584
1760
|
|
|
1761
|
+
def fetch_remote_for_parity!(remote)
|
|
1762
|
+
command = "git fetch #{Shellwords.escape(remote)}"
|
|
1763
|
+
attempts = remote_fetch_parity_attempts
|
|
1764
|
+
last_error = nil
|
|
1765
|
+
attempts.times do |index|
|
|
1766
|
+
attempt = index + 1
|
|
1767
|
+
puts "Fetching remote '#{remote}' for release parity (attempt #{attempt}/#{attempts})"
|
|
1768
|
+
begin
|
|
1769
|
+
run_cmd!(command)
|
|
1770
|
+
return true
|
|
1771
|
+
rescue SystemExit => error
|
|
1772
|
+
last_error = error
|
|
1773
|
+
rescue => error
|
|
1774
|
+
last_error = error
|
|
1775
|
+
end
|
|
1776
|
+
sleep(remote_fetch_parity_interval) if attempt < attempts
|
|
1777
|
+
end
|
|
1778
|
+
|
|
1779
|
+
abort(remote_fetch_failure_message(remote, last_error&.message.to_s))
|
|
1780
|
+
end
|
|
1781
|
+
|
|
1782
|
+
def remote_fetch_parity_attempts
|
|
1783
|
+
3
|
|
1784
|
+
end
|
|
1785
|
+
|
|
1786
|
+
def remote_fetch_parity_interval
|
|
1787
|
+
5
|
|
1788
|
+
end
|
|
1789
|
+
|
|
1790
|
+
def remote_fetch_failure_message(remote, detail)
|
|
1791
|
+
<<~MSG
|
|
1792
|
+
Unable to fetch git remote '#{remote}' during release parity checks.
|
|
1793
|
+
kettle-release enforces trunk parity across active remotes before publishing.
|
|
1794
|
+
|
|
1795
|
+
If this remote is temporarily unavailable or intentionally not part of this release,
|
|
1796
|
+
rerun with one of:
|
|
1797
|
+
kettle-release --skip-remotes #{remote}
|
|
1798
|
+
K_RELEASE_SKIP_REMOTES=#{remote} kettle-release
|
|
1799
|
+
|
|
1800
|
+
Original failure:
|
|
1801
|
+
#{detail}
|
|
1802
|
+
MSG
|
|
1803
|
+
end
|
|
1804
|
+
|
|
1585
1805
|
def merge_feature_into_trunk_and_push!(trunk, feature)
|
|
1586
1806
|
return if feature.nil? || feature == trunk
|
|
1587
1807
|
|
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.3
|
|
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
|
|
@@ -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.3
|
|
373
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.4.3/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.3
|
|
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
|