kettle-dev 2.5.11 → 2.5.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2c0c759c751e79d77e5cf8495f9c461d514d0b120c97cd95b4ae7222afe7f0c
4
- data.tar.gz: 95f799d83838660c1fa6200fe4783368a59456f1e8c6221bfdccff0eb967cecf
3
+ metadata.gz: 76cf339458385418bcf752559dbb9c6278f3b95ebfa27b26781591564179f263
4
+ data.tar.gz: 66cae3bfb62ecd922bf2b6ecc16049389a860a20dbaa8492fd6578bdcf3b4922
5
5
  SHA512:
6
- metadata.gz: 6e95a43755c42b2cf9e45e6d5df069aa0fd5cea4291aeae0ef9f9020a43eed84959a7c734bf8dbd0f719f9d78f6b3f135399cddb9374b616e74abbdb200c6017
7
- data.tar.gz: 9bee1f5843526fc154502c909c0144c4440da1610611d6eaf9ef23a4d7f72386071e420cbe267b0ce08d0a82c59abe094b990c85e0d2a813077ab08fe8a147ec
6
+ metadata.gz: 0ae8aa5d0113e226dab9dbb332b42328bfe738c84ce88a21d919c3a0249d2ad3f70d905aa4cbd76c903608846da8a485a6e68c710934e7eac2443517d71d6a96
7
+ data.tar.gz: 4470d0bd47c7c95a616b8513a6b590911a4086e6d6f31330e96c45059774ac4d172a0c98ccf5aad53384208cf40fb02de237c68c6326a430acaf89adfe9a89c2
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,89 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [2.5.13] - 2026-07-31
34
+
35
+ - TAG: [v2.5.13][2.5.13t]
36
+ - COVERAGE: 91.81% -- 5137/5595 lines in 46 files
37
+ - BRANCH COVERAGE: 76.06% -- 1944/2556 branches in 46 files
38
+ - 50.41% documented
39
+
40
+ ### Fixed
41
+
42
+ - `kettle-release` now treats `origin` as the default required parity remote
43
+ while allowing optional mirror remotes to be skipped after fetch retries,
44
+ so a transient Codeberg/GitLab mirror outage does not block publishing.
45
+
46
+ ### Added
47
+
48
+ - `kettle-release --events` now emits `remote_parity` events for remote parity
49
+ fetch attempts, skipped optional remotes, required failures, and parity
50
+ completion.
51
+ - `kettle-release --events` now emits `ci_monitor` events around CI monitoring
52
+ start, per-workflow GitHub Actions progress, GitLab pipeline outcomes,
53
+ success, and failure.
54
+ - `kettle-release --events` now emits `pre_release` events around
55
+ `kettle-pre-release` checks and selected check summaries.
56
+ - `kettle-changelog --events` now emits `changelog` events around release plan,
57
+ coverage generation, and changelog update activity, and `kettle-release
58
+ --events` forwards those child events.
59
+ - `kettle-release --events` now emits `release_lockfile` and `release_probe`
60
+ events for release lockfile reset/validation and published-gem availability
61
+ probes.
62
+ - `kettle-dev` now requires `kettle-ndjson` 0.1.9 or newer so release event
63
+ streams resolve against the protocol that includes remote parity and CI
64
+ monitor, pre-release, changelog, release lockfile, and release probe events.
65
+
66
+ - `kettle-release --events` now emits GitHub CI wait/start/tick monitor events
67
+ while waiting for workflow runs to appear and complete.
68
+
69
+ ### Changed
70
+
71
+ - `kettle-release --events` now emits clearer `command_step` names and compact
72
+ per-command summaries for common release commands instead of shell-derived
73
+ labels such as `env__u`.
74
+
75
+ ## [2.5.12] - 2026-07-31
76
+
77
+ - TAG: [v2.5.12][2.5.12t]
78
+ - COVERAGE: 91.73% -- 4961/5408 lines in 46 files
79
+ - BRANCH COVERAGE: 75.98% -- 1850/2435 branches in 46 files
80
+ - 50.68% documented
81
+
82
+ ### Added
83
+
84
+ - `kettle-release-secrets-doctor` can probe 1Password release secret
85
+ authorization across same-process, child-process, and threaded shapes.
86
+
87
+ - `kettle-release-secrets-doctor` now probes additional 1Password
88
+ authorization boundaries, including parent-child, Bundler child, and
89
+ Bundler-env-reset child process shapes.
90
+
91
+ - `kettle-release --events` now emits `secret_provider` events around release
92
+ secret keepalives and provider-backed prompt responses.
93
+
94
+ ### Fixed
95
+
96
+ - `kettle-release` now keeps configured release secrets providers warm during
97
+ CI waits and alerts before provider lookups, reducing missed 1Password
98
+ authorization prompts during long release runs.
99
+
100
+ - `kettle-release` keepalive alerts now include the elapsed release time so
101
+ delayed 1Password prompts are easier to correlate with the run.
102
+
103
+ - `kettle-dev` now requires `kettle-ndjson` 0.1.4 or newer so
104
+ `kettle-release --events=secret_provider` cannot resolve against an older
105
+ event protocol.
106
+ - `kettle-release` lockfile reset now preserves existing bundle platforms and
107
+ adds the current host platform, preventing release-prep lockfiles from losing
108
+ Linux support during full rebuilds.
109
+ - `kettle-pre-release` Markdown image URL normalization now keeps encoded query
110
+ values stable, avoiding release-time churn in shields URLs with nested URL
111
+ parameters.
112
+ - `kettle-release` now runs `bin/setup` with inherited Bundler bootstrap
113
+ variables unset, so a release lockfile reset can install newly selected gems
114
+ before later release tasks run.
115
+
33
116
  ## [2.5.11] - 2026-07-30
34
117
 
35
118
  - TAG: [v2.5.11][2.5.11t]
@@ -3077,7 +3160,11 @@ Please file a bug if you notice a violation of semantic versioning.
3077
3160
  - Selecting will run the selected workflow via `act`
3078
3161
  - This may move to its own gem in the future.
3079
3162
 
3080
- [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.11...HEAD
3163
+ [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.13...HEAD
3164
+ [2.5.13]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.12...v2.5.13
3165
+ [2.5.13t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.5.13
3166
+ [2.5.12]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.11...v2.5.12
3167
+ [2.5.12t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.5.12
3081
3168
  [2.5.11]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.10...v2.5.11
3082
3169
  [2.5.11t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.5.11
3083
3170
  [2.5.10]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.9...v2.5.10
data/README.md CHANGED
@@ -1005,7 +1005,7 @@ Thanks for RTFM. ☺️
1005
1005
  [📌gitmoji]: https://gitmoji.dev
1006
1006
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1007
1007
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1008
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-5.189-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1008
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-5.595-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1009
1009
  [🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
1010
1010
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1011
1011
  [📄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
@@ -50,9 +50,11 @@ begin
50
50
 
51
51
  # This library
52
52
  require "kettle/dev"
53
+ require "kettle/ndjson"
53
54
  require "kettle/dev/executable_version"
54
55
  Kettle::Dev::ExecutableVersion.print_and_exit!(script_basename, ARGV, value_option: true)
55
- Kettle::Dev::ExecutableVersion.print_header(script_basename) unless ARGV.include?("--json")
56
+ machine_output = ARGV.any? { |arg| arg == "--json" || arg.start_with?("--events") }
57
+ Kettle::Dev::ExecutableVersion.print_header(script_basename) unless machine_output
56
58
  rescue LoadError => e
57
59
  warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
58
60
  warn("Hint: Ensure the host project has kettle-dev as a dependency and run bundle install.")
@@ -61,7 +63,7 @@ end
61
63
 
62
64
  if ARGV.include?("-h") || ARGV.include?("--help")
63
65
  puts <<~USAGE
64
- Usage: kettle-changelog [--version VERSION] [--yes] [--update-prep] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--json] [--no-strict] [--no-coverage-threshold]
66
+ Usage: kettle-changelog [--version VERSION] [--yes] [--update-prep] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--json|--events[=TYPES]] [--no-strict] [--no-coverage-threshold]
65
67
 
66
68
  Detects the current version from lib/**/version.rb, the latest live release, and
67
69
  the most recent CHANGELOG.md release section, then prompts to confirm the selected plan:
@@ -82,6 +84,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
82
84
  --section SECTION Unreleased section to receive the entry (Added, Changed, Deprecated, Removed, Fixed, Security)
83
85
  --entry TEXT Changelog entry text; "- " is added when omitted
84
86
  --json Print query output as JSON
87
+ --events[=TYPES] Print newline-delimited JSON changelog events
85
88
  --no-strict Allow missing coverage and yard data (warnings only, no errors)
86
89
  --no-coverage-threshold Generate coverage without hard-failing below configured thresholds
87
90
 
@@ -137,6 +140,22 @@ begin
137
140
  Kettle::Dev::Versioning.normalize_explicit_version(version)
138
141
  end
139
142
 
143
+ def extract_events_arg!(argv)
144
+ events = false
145
+ event_types = nil
146
+ argv.delete_if do |arg|
147
+ if arg == "--events"
148
+ events = true
149
+ elsif arg.start_with?("--events=")
150
+ events = true
151
+ event_types = arg.split("=", 2)[1]
152
+ else
153
+ false
154
+ end
155
+ end
156
+ [events, event_types]
157
+ end
158
+
140
159
  # Determine if strict mode is enabled (default: true)
141
160
  strict_mode = !ARGV.include?("--no-strict") && ENV.fetch("K_CHANGELOG_STRICT", "true").downcase != "false"
142
161
  update_prep = ARGV.delete("--update-prep")
@@ -146,6 +165,8 @@ begin
146
165
  refresh_cache = ARGV.delete("--refresh-cache")
147
166
  add_unreleased_entry = ARGV.delete("--add-unreleased-entry") || ARGV.delete("--add-changelog-entry")
148
167
  json_output = ARGV.delete("--json")
168
+ events_output, event_types = extract_events_arg!(ARGV)
169
+ Kettle::Dev::ExitAdapter.abort("--events cannot be combined with --json") if events_output && json_output
149
170
  coverage_threshold_disabled = ARGV.include?("--no-coverage-threshold") || ARGV.include?("--no-coverage-thresholds")
150
171
  coverage_hard = !coverage_threshold_disabled && ENV.fetch("K_CHANGELOG_COVERAGE_HARD", "true").downcase != "false"
151
172
  version_override = extract_version_arg!(ARGV)
@@ -180,7 +201,8 @@ begin
180
201
  exit(0)
181
202
  end
182
203
 
183
- cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, version: version_override, refresh_cache: refresh_cache, yes: yes)
204
+ event_stream = events_output ? Kettle::Ndjson.event_stream($stdout, types: event_types) : nil
205
+ cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, version: version_override, refresh_cache: refresh_cache, yes: yes, event_stream: event_stream)
184
206
  if release_state_query
185
207
  state = cli.release_state
186
208
  puts(json_output ? JSON.pretty_generate(state) : cli.release_state_table(state))
data/exe/kettle-release CHANGED
@@ -49,7 +49,7 @@ end
49
49
  # Do not guard with __FILE__ == $PROGRAM_NAME because binstubs use Kernel.load.
50
50
  if ARGV.include?("-h") || ARGV.include?("--help")
51
51
  puts <<~USAGE
52
- Usage: kettle-release [--version VERSION] [--local-ci] [--yes] [--appraisal-update] [--skip-bundle-audit] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [--skip-remotes REMOTES] [--secrets-provider NAME] [--json|--events[=TYPES]] [--report PATH] [start_step=<0-19>]
52
+ Usage: kettle-release [--version VERSION] [--local-ci] [--yes] [--appraisal-update] [--skip-bundle-audit] [--skip-steps STEPS] [--ci-workflows WORKFLOWS] [--skip-remotes REMOTES] [--required-remotes REMOTES] [--secrets-provider NAME] [--json|--events[=TYPES]] [--report PATH] [start_step=<0-19>]
53
53
 
54
54
  Automates the release flow for a Ruby gem in the host project.
55
55
 
@@ -90,6 +90,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
90
90
  --skip-bundle-audit # Skip bundle:audit/update during release rake checks
91
91
  --ci-workflows WORKFLOWS # Comma-separated workflow files or stems to monitor at CI step 10
92
92
  --skip-remotes REMOTES # Comma-separated git remote names to exclude from release fetch/push parity
93
+ --required-remotes REMOTES # Comma-separated remote names that must fetch for release parity; defaults to origin
93
94
  --secrets-provider NAME # Release secrets provider: interactive, 1password
94
95
  --json # Print final machine-readable release report as JSON
95
96
  --events[=TYPES] # Print newline-delimited JSON release events, optionally filtered by type
@@ -104,6 +105,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
104
105
  K_RELEASE_LOCAL_CI_WORKFLOW # Name of workflow (without .yml) for local CI; defaults to locked_deps or first
105
106
  K_RELEASE_CI_WORKFLOWS # Comma-separated workflow files or stems to monitor at CI step 10
106
107
  K_RELEASE_SKIP_REMOTES # Comma-separated git remote names to exclude from release fetch/push parity
108
+ K_RELEASE_REQUIRED_REMOTES # Comma-separated remotes that must fetch for release parity; defaults to origin
107
109
  KETTLE_RELEASE_SECRETS_PROVIDER=1password
108
110
  # Load release secrets through the configured provider
109
111
  KETTLE_RELEASE_1PASSWORD_ITEM=Rubygems
@@ -215,6 +217,27 @@ def extract_skip_remotes_arg!(argv)
215
217
  remotes
216
218
  end
217
219
 
220
+ def extract_required_remotes_arg!(argv)
221
+ remotes = nil
222
+ if (idx = argv.index("--required-remotes"))
223
+ remotes = argv[idx + 1]
224
+ if remotes.to_s.empty? || remotes.to_s.start_with?("-")
225
+ Kettle::Dev::ExitAdapter.abort("--required-remotes requires a comma-separated remote list")
226
+ end
227
+ argv.slice!(idx, 2)
228
+ end
229
+ argv.delete_if do |arg|
230
+ if arg.start_with?("--required-remotes=", "required_remotes=")
231
+ remotes = arg.split("=", 2)[1]
232
+ Kettle::Dev::ExitAdapter.abort("--required-remotes requires a comma-separated remote list") if remotes.to_s.empty?
233
+ true
234
+ else
235
+ false
236
+ end
237
+ end
238
+ remotes
239
+ end
240
+
218
241
  def extract_report_arg!(argv)
219
242
  report_path = nil
220
243
  if (idx = argv.index("--report"))
@@ -286,12 +309,13 @@ version_override = extract_version_arg!(ARGV)
286
309
  skip_steps = extract_skip_steps_arg!(ARGV)
287
310
  ci_workflows = extract_ci_workflows_arg!(ARGV)
288
311
  skip_remotes = extract_skip_remotes_arg!(ARGV)
312
+ required_remotes = extract_required_remotes_arg!(ARGV)
289
313
  report_path = extract_report_arg!(ARGV)
290
314
  secrets_provider = extract_secrets_provider_arg!(ARGV)
291
315
 
292
316
  begin
293
317
  event_stream = events_output ? Kettle::Ndjson.event_stream($stdout, types: event_types) : nil
294
- 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, secrets_provider_name: secrets_provider, yes: yes, event_stream: event_stream, json_output: json_output, json_io: $stdout, report_path: report_path).run
318
+ 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, required_remotes: required_remotes, secrets_provider_name: secrets_provider, yes: yes, event_stream: event_stream, json_output: json_output, json_io: $stdout, report_path: report_path).run
295
319
  rescue LoadError => e
296
320
  warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
297
321
  warn(Array(e.backtrace).join("\n")) if ENV["DEBUG"]
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+
6
+ $stdout.sync = true
7
+ $stderr.sync = true
8
+
9
+ script_basename = File.basename(__FILE__)
10
+ repo_lib = File.expand_path("../lib", __dir__)
11
+ $LOAD_PATH.unshift(repo_lib) if File.directory?(repo_lib) && !$LOAD_PATH.include?(repo_lib)
12
+
13
+ begin
14
+ require "kettle/dev"
15
+ require "kettle/dev/executable_version"
16
+ Kettle::Dev::ExecutableVersion.print_and_exit!(script_basename, ARGV, value_option: true)
17
+ Kettle::Dev::ExecutableVersion.print_header(script_basename)
18
+ rescue LoadError
19
+ warn "kettle-release-secrets-doctor must run with kettle-dev on the load path"
20
+ exit 1
21
+ end
22
+
23
+ options = Kettle::Dev::ReleaseSecretsDoctor.options_from_env
24
+
25
+ OptionParser.new do |parser|
26
+ parser.banner = "Usage: kettle-release-secrets-doctor [options]"
27
+ parser.on("--provider NAME", "Secrets provider name, default: op") { |value| options[:provider] = value }
28
+ parser.on("--sleep SECONDS", Float, "Seconds to wait between probes") { |value| options[:sleep_seconds] = value }
29
+ parser.on("--keepalive SECONDS", Float, "Run keepalive every N seconds while waiting; 0 disables") { |value| options[:keepalive_seconds] = value }
30
+ parser.on("--shape SHAPE", "Shape: #{Kettle::Dev::ReleaseSecretsDoctor::SHAPES.join(", ")}") { |value| options[:shape] = value }
31
+ parser.on("--otp", "Fetch RubyGems OTP after the wait") { options[:otp] = true }
32
+ end.parse!
33
+
34
+ begin
35
+ Kettle::Dev::ReleaseSecretsDoctor.new(options: options, program_name: $PROGRAM_NAME).run
36
+ rescue Kettle::Dev::Error => e
37
+ warn e.message
38
+ exit 1
39
+ end
@@ -19,6 +19,7 @@ module Kettle
19
19
  BUNDLE_SILENCE_ROOT_WARNING
20
20
  BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES
21
21
  BUNDLE_VERBOSE
22
+ BUNDLER_DEBUG
22
23
  ].freeze
23
24
 
24
25
  INERT_ENV_PREFIXES = %w[
@@ -5,6 +5,7 @@ require "json"
5
5
  require "net/http"
6
6
  require "uri"
7
7
  require "fileutils"
8
+ require "kettle/ndjson"
8
9
 
9
10
  module Kettle
10
11
  module Dev
@@ -28,7 +29,7 @@ module Kettle
28
29
  # @param update_prep [Boolean] when true, update the most recent prepared release section in place
29
30
  # @param version [String, nil] explicit version override for gems without a literal VERSION constant
30
31
  # @param yes [Boolean] when true, approve the selected release plan without prompting
31
- def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, version: nil, root: Kettle::Dev::CIHelpers.project_root, refresh_cache: false, yes: false)
32
+ def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, version: nil, root: Kettle::Dev::CIHelpers.project_root, refresh_cache: false, yes: false, event_stream: nil)
32
33
  @root = root
33
34
  @changelog_path = resolved_changelog_path
34
35
  @coverage_root = resolved_coverage_root
@@ -39,6 +40,7 @@ module Kettle
39
40
  @version_override = Kettle::Dev::Versioning.normalize_explicit_version(version)
40
41
  @refresh_cache = refresh_cache
41
42
  @yes = !!yes
43
+ @event_recorder = Kettle::Ndjson.event_recorder(event_stream, phase_timings: [])
42
44
  end
43
45
 
44
46
  # Main entry point to update CHANGELOG.md
@@ -70,6 +72,7 @@ module Kettle
70
72
 
71
73
  if plan.fetch(:action) == :update_prepared_release
72
74
  update_prepared_release!(changelog, today, owner, repo, line_cov_line, branch_cov_line, yard_line)
75
+ emit_changelog_event(action: "update", status: "ok", version: version, plan: plan.fetch(:action).to_s)
73
76
  return
74
77
  end
75
78
 
@@ -140,6 +143,7 @@ module Kettle
140
143
  updated = updated.rstrip + "\n"
141
144
 
142
145
  File.write(@changelog_path, updated)
146
+ emit_changelog_event(action: "update", status: "ok", version: version, plan: plan.fetch(:action).to_s)
143
147
  puts "CHANGELOG.md updated with v#{version} section."
144
148
  end
145
149
 
@@ -348,6 +352,16 @@ module Kettle
348
352
  puts " latest released for current series: #{plan.fetch(:latest_for_series) || "unknown"}"
349
353
  puts " latest CHANGELOG.md release: #{plan.fetch(:latest_changelog_version) || "none"}"
350
354
  puts " gem: #{plan.fetch(:gem_name) || "unknown"}"
355
+ emit_changelog_event(
356
+ action: "plan",
357
+ status: "ok",
358
+ plan: plan.fetch(:action).to_s,
359
+ version: plan.fetch(:version),
360
+ gem_name: plan.fetch(:gem_name),
361
+ latest_overall: plan.fetch(:latest_overall),
362
+ latest_for_series: plan.fetch(:latest_for_series),
363
+ latest_changelog_version: plan.fetch(:latest_changelog_version)
364
+ )
351
365
  if @yes
352
366
  puts("Continue with this plan? [y/N]: y")
353
367
  return
@@ -665,20 +679,25 @@ module Kettle
665
679
  coverage_dir = File.dirname(@coverage_path)
666
680
  if Dir.exist?(coverage_dir)
667
681
  puts "Cleaning old coverage data from #{Kettle::Dev.display_path(coverage_dir)}..."
682
+ emit_changelog_event(action: "coverage_clean", status: "started", path: Kettle::Dev.display_path(coverage_dir))
668
683
  Dir.glob(File.join(coverage_dir, "*")).each do |file|
669
684
  File.delete(file) if File.file?(file)
670
685
  end
686
+ emit_changelog_event(action: "coverage_clean", status: "ok", path: Kettle::Dev.display_path(coverage_dir))
671
687
  end
672
688
 
673
689
  puts "Generating fresh coverage data by running: bundle exec kettle-test"
690
+ emit_changelog_event(action: "coverage", status: "started", command: "bundle exec kettle-test", root: Kettle::Dev.display_path(@coverage_root))
674
691
 
675
692
  success = system(changelog_coverage_env, "bundle", "exec", "kettle-test", chdir: @coverage_root)
676
693
 
677
694
  unless success
695
+ emit_changelog_event(action: "coverage", status: "failed", command: "bundle exec kettle-test", root: Kettle::Dev.display_path(@coverage_root), reason: "exit status #{$?.exitstatus || "unknown"}")
678
696
  raise "bundle exec kettle-test failed with exit status #{$?.exitstatus || "unknown"}"
679
697
  end
680
698
 
681
699
  puts "Coverage generation complete."
700
+ emit_changelog_event(action: "coverage", status: "ok", command: "bundle exec kettle-test", root: Kettle::Dev.display_path(@coverage_root))
682
701
 
683
702
  ensure_changelog_coverage_json!
684
703
  else
@@ -739,6 +758,29 @@ module Kettle
739
758
  end
740
759
  end
741
760
 
761
+ def emit_changelog_event(action:, status:, **payload)
762
+ mark = case status.to_s
763
+ when "started"
764
+ ">"
765
+ when "ok", "skipped"
766
+ "."
767
+ when "failed"
768
+ "!"
769
+ else
770
+ "?"
771
+ end
772
+ Kettle::Ndjson.emit_event(
773
+ @event_recorder,
774
+ "changelog",
775
+ payload.merge(
776
+ action: action,
777
+ phase: "release",
778
+ status: status,
779
+ mark: mark
780
+ )
781
+ )
782
+ end
783
+
742
784
  def changelog_coverage_env
743
785
  env = BundlerEnvGuard.unbundled_env.merge(
744
786
  "PATH" => ENV.fetch("PATH", ""),