kettle-dev 2.5.27 → 3.0.0

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: 3ec35425bc4a7f6e3ee067f5e92c80f9b81559945d464bde2966e5667eb94979
4
- data.tar.gz: 1d6c6fc458b3667f2a2d6aff255da8f4cd6ff1503020d36cba21e8a3199a21e6
3
+ metadata.gz: c7cdd1aace2e06deda5e6bb04aa3b2e6ef67408f0521c8d0ed6a8fa27fad23be
4
+ data.tar.gz: e23d202928cd64875dffdb94bb426e0925e2033a7b07ba36b154eba7c83175d8
5
5
  SHA512:
6
- metadata.gz: 8a6b8bd1a2e8fb4c5a2c73f3791378f793d31ef9eb05643621c720175508147c791660453b1341f62d5fc6aedb4befb37f0caecb0615244a1f3e5ce5af70989e
7
- data.tar.gz: 0e1f22f3cd86b4544ff272dc78d3a24f7b6b1edc915be470d8760137cf91227482530899fc399c6398ed2ac44b30b675a1d3d2297fca03486f66f0bed76b5959
6
+ metadata.gz: a6c052d533011b2e59762247ee8cb7d8f794e2383720dc7d589beb1643e8086451f5f2922f5b6f77b3d46cebb4f657c07d290ab3c0146e99e2a9196149ce1002
7
+ data.tar.gz: 6a38aaf07275a14e588d8e03ab35f8c208755a78c5ac222328688fea61eafde8770bd826abedc8ae91742723dcf69ae09b97782a9eec97e75ebeb380422e6ba2
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,36 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [3.0.0] - 2026-08-09
34
+
35
+ - TAG: [v3.0.0][3.0.0t]
36
+ - COVERAGE: 91.48% -- 5552/6069 lines in 47 files
37
+ - BRANCH COVERAGE: 76.26% -- 2107/2763 branches in 47 files
38
+ - 50.77% documented
39
+
40
+ ### Changed
41
+
42
+ - Move the kettle-changelog executable to the standalone kettle-changelog gem while retaining kettle-dev support APIs for the transition.
43
+
44
+ - Resolve standalone kettle-changelog through the generated Ruby-4-only modular release-tool Gemfile, with local path wiring for development.
45
+
46
+ ### Fixed
47
+
48
+ - Use a dedicated standalone changelog bundle during bootstrap releases when the project bundle does not include kettle-changelog.
49
+
50
+ - Keep standalone changelog bootstrap paths out of target release tests and documentation.
51
+
52
+ ## [2.5.28] - 2026-08-09
53
+
54
+ - TAG: [v2.5.28][2.5.28t]
55
+ - COVERAGE: 91.71% -- 5544/6045 lines in 47 files
56
+ - BRANCH COVERAGE: 76.48% -- 2104/2751 branches in 47 files
57
+ - 50.77% documented
58
+
59
+ ### Fixed
60
+
61
+ - Retry an existing gem publication once with a fresh RubyGems MFA OTP when the release task reports an invalid OTP.
62
+
33
63
  ## [2.5.27] - 2026-08-09
34
64
 
35
65
  - TAG: [v2.5.27][2.5.27t]
@@ -3371,7 +3401,11 @@ Please file a bug if you notice a violation of semantic versioning.
3371
3401
  - Selecting will run the selected workflow via `act`
3372
3402
  - This may move to its own gem in the future.
3373
3403
 
3374
- [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.27...HEAD
3404
+ [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.0...HEAD
3405
+ [3.0.0]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.28...v3.0.0
3406
+ [3.0.0t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.0
3407
+ [2.5.28]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.27...v2.5.28
3408
+ [2.5.28t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.5.28
3375
3409
  [2.5.27]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.26...v2.5.27
3376
3410
  [2.5.27t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.5.27
3377
3411
  [2.5.26]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.25...v2.5.26
data/README.md CHANGED
@@ -1007,7 +1007,7 @@ Thanks for RTFM. ☺️
1007
1007
  [📌gitmoji]: https://gitmoji.dev
1008
1008
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1009
1009
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1010
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.026-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1010
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.069-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1011
1011
  [🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
1012
1012
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1013
1013
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -25,6 +25,7 @@ require_relative "release_secrets"
25
25
  module Kettle
26
26
  module Dev
27
27
  class ReleaseCLI
28
+ RUBYGEMS_INVALID_OTP = /Your OTP code is incorrect\. Please check it and retry\./.freeze
28
29
  QUIET_ENV = {
29
30
  "KETTLE_JEM_QUIET" => "true",
30
31
  "KETTLE_JEM_DEBUG" => "false",
@@ -116,7 +117,8 @@ module Kettle
116
117
  words = effective_command_words(cmd)
117
118
  words == ["bin/setup"] ||
118
119
  words.first(1) == ["bin/rake"] ||
119
- words.first(3) == ["bundle", "exec", "rake"]
120
+ words.first(3) == ["bundle", "exec", "rake"] ||
121
+ words.first(3) == ["bundle", "exec", "kettle-changelog"]
120
122
  rescue ArgumentError
121
123
  false
122
124
  end
@@ -827,7 +829,7 @@ module Kettle
827
829
  end
828
830
 
829
831
  def run_changelog!
830
- cmd = "bundle exec kettle-changelog"
832
+ cmd = release_changelog_command
831
833
  cmd = "#{cmd} --version #{Shellwords.escape(@version_override)}" if @version_override
832
834
  cmd = "#{cmd} --yes" if @yes
833
835
  cmd = "#{cmd} --events=changelog" if @event_stream
@@ -835,6 +837,51 @@ module Kettle
835
837
  @changelog_generated_coverage = true
836
838
  end
837
839
 
840
+ # The changelog executable is owned by kettle-changelog, not kettle-dev.
841
+ # During the bootstrap release of kettle-dev it is therefore intentionally
842
+ # absent from kettle-dev's bundle. When a local kettle-dev workspace is
843
+ # supplied, run the standalone tool through that project's bundle instead
844
+ # of relying on an ambient installed executable.
845
+ def release_changelog_command
846
+ gemfile = release_changelog_gemfile
847
+ return "bundle exec kettle-changelog" unless gemfile
848
+
849
+ escaped_gemfile = Shellwords.escape(gemfile)
850
+ command = +"env -u BUNDLE_GEMFILE -u BUNDLE_LOCKFILE"
851
+ if (local_root = release_changelog_local_root)
852
+ command << " KETTLE_DEV_DEV=false KETTLE_CHANGELOG_DEV_ROOT=#{Shellwords.escape(local_root)}"
853
+ end
854
+ command << " BUNDLE_GEMFILE=#{escaped_gemfile} bundle exec kettle-changelog"
855
+ command
856
+ end
857
+
858
+ def release_changelog_gemfile
859
+ configured = ENV["K_RELEASE_CHANGELOG_GEMFILE"].to_s.strip
860
+ unless configured.empty?
861
+ path = File.expand_path(configured)
862
+ return path if File.file?(path)
863
+
864
+ abort("Configured K_RELEASE_CHANGELOG_GEMFILE does not exist: #{path}")
865
+ end
866
+
867
+ local_root = release_changelog_local_root
868
+ return nil unless local_root
869
+
870
+ changelog_root = File.join(File.expand_path(local_root), "kettle-changelog")
871
+ candidates = [
872
+ File.join(changelog_root, "gemfiles", "release.gemfile"),
873
+ File.join(changelog_root, "Gemfile")
874
+ ]
875
+ candidates.find { |path| File.file?(path) }
876
+ end
877
+
878
+ def release_changelog_local_root
879
+ local_root = ENV["KETTLE_DEV_DEV"].to_s.strip
880
+ return nil if local_root.empty? || %w[false 0 no off].include?(local_root.downcase)
881
+
882
+ File.expand_path(local_root)
883
+ end
884
+
838
885
  # Monorepo subprojects commonly disable their local hard coverage gate
839
886
  # because their narrow test suite cannot satisfy the aggregate root
840
887
  # thresholds. Preserve an explicit changelog policy, otherwise derive
@@ -1237,7 +1284,7 @@ module Kettle
1237
1284
 
1238
1285
  keep_release_secrets_alive!("prompt-bearing command")
1239
1286
  puts "$ #{cmd}"
1240
- _stdout_str, stderr_str, status = Kettle::Dev::InteractiveReleaseCommand.new(
1287
+ stdout_str, stderr_str, status = Kettle::Dev::InteractiveReleaseCommand.new(
1241
1288
  secrets_provider: @secrets_provider,
1242
1289
  secret_event_handler: ->(payload) { emit_secret_event(payload) }
1243
1290
  ).call(
@@ -1246,6 +1293,11 @@ module Kettle
1246
1293
  )
1247
1294
  return if status.success?
1248
1295
 
1296
+ if gem_release_command?(cmd) && rubygems_invalid_otp?(stdout_str, stderr_str)
1297
+ retry_gem_push_after_invalid_otp!
1298
+ return
1299
+ end
1300
+
1249
1301
  exit_code = status.respond_to?(:exitstatus) ? status.exitstatus : 1
1250
1302
  diag = stderr_str.to_s.empty? ? "" : "\n--- STDERR (last 20 lines) ---\n#{stderr_str.lines.last(20).join}".rstrip
1251
1303
  abort("Command failed: #{cmd} (exit #{exit_code})#{diag}")
@@ -1261,6 +1313,45 @@ module Kettle
1261
1313
  release_secrets_configured? ? @secrets_provider.class.name.to_s.split("::").last : "interactive"
1262
1314
  end
1263
1315
 
1316
+ def gem_release_command?(cmd)
1317
+ effective_command_words(cmd).first(4) == ["bundle", "exec", "rake", "release"]
1318
+ end
1319
+
1320
+ def rubygems_invalid_otp?(*output)
1321
+ output.any? { |chunk| chunk.to_s.match?(RUBYGEMS_INVALID_OTP) }
1322
+ end
1323
+
1324
+ def retry_gem_push_after_invalid_otp!
1325
+ version = @release_candidate&.version || detect_version
1326
+ gem_path = gem_file_for_version(version)
1327
+ unless gem_path && File.file?(gem_path)
1328
+ abort("RubyGems rejected the OTP, but the built gem for version #{version} could not be found in pkg/.")
1329
+ end
1330
+
1331
+ puts "RubyGems rejected the OTP after the release task completed; retrying the existing gem publication with a fresh OTP..."
1332
+ emit_secret_event(
1333
+ source: release_secrets_provider_label,
1334
+ action: "otp_retry",
1335
+ status: "started",
1336
+ reason: "RubyGems rejected the first OTP"
1337
+ )
1338
+ run_cmd!("gem push #{Shellwords.escape(gem_path)}")
1339
+ emit_secret_event(
1340
+ source: release_secrets_provider_label,
1341
+ action: "otp_retry",
1342
+ status: "ok",
1343
+ reason: "existing gem artifact published after OTP retry"
1344
+ )
1345
+ rescue SystemExit, Kettle::Dev::Error
1346
+ emit_secret_event(
1347
+ source: release_secrets_provider_label,
1348
+ action: "otp_retry",
1349
+ status: "failed",
1350
+ reason: "fresh OTP retry failed"
1351
+ )
1352
+ raise
1353
+ end
1354
+
1264
1355
  def signing_enabled?
1265
1356
  ENV.fetch("SKIP_GEM_SIGNING", "false").casecmp("false").zero?
1266
1357
  end
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "2.5.27"
8
+ VERSION = "3.0.0"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
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.5.27
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -236,7 +236,7 @@ dependencies:
236
236
  version: '3.2'
237
237
  - - ">="
238
238
  - !ruby/object:Gem::Version
239
- version: 3.2.0
239
+ version: 3.2.2
240
240
  type: :development
241
241
  prerelease: false
242
242
  version_requirements: !ruby/object:Gem::Requirement
@@ -246,7 +246,7 @@ dependencies:
246
246
  version: '3.2'
247
247
  - - ">="
248
248
  - !ruby/object:Gem::Version
249
- version: 3.2.0
249
+ version: 3.2.2
250
250
  - !ruby/object:Gem::Dependency
251
251
  name: turbo_tests2
252
252
  requirement: !ruby/object:Gem::Requirement
@@ -256,7 +256,7 @@ dependencies:
256
256
  version: '3.2'
257
257
  - - ">="
258
258
  - !ruby/object:Gem::Version
259
- version: 3.2.4
259
+ version: 3.2.5
260
260
  type: :development
261
261
  prerelease: false
262
262
  version_requirements: !ruby/object:Gem::Requirement
@@ -266,7 +266,7 @@ dependencies:
266
266
  version: '3.2'
267
267
  - - ">="
268
268
  - !ruby/object:Gem::Version
269
- version: 3.2.4
269
+ version: 3.2.5
270
270
  - !ruby/object:Gem::Dependency
271
271
  name: stone_checksums
272
272
  requirement: !ruby/object:Gem::Requirement
@@ -317,7 +317,6 @@ email:
317
317
  - floss@galtzo.com
318
318
  executables:
319
319
  - kettle-bump
320
- - kettle-changelog
321
320
  - kettle-check-eof
322
321
  - kettle-commit-msg
323
322
  - kettle-dev-setup
@@ -336,7 +335,6 @@ files:
336
335
  - LICENSE.md
337
336
  - README.md
338
337
  - exe/kettle-bump
339
- - exe/kettle-changelog
340
338
  - exe/kettle-check-eof
341
339
  - exe/kettle-check-eof.sh
342
340
  - exe/kettle-commit-msg
@@ -402,10 +400,10 @@ licenses:
402
400
  - AGPL-3.0-only
403
401
  metadata:
404
402
  homepage_uri: https://kettle-dev.galtzo.com
405
- source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.5.27
406
- changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.5.27/CHANGELOG.md
403
+ source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.0
404
+ changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.0/CHANGELOG.md
407
405
  bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
408
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.5.27
406
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.0
409
407
  funding_uri: https://github.com/sponsors/pboling
410
408
  wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
411
409
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file
data/exe/kettle-changelog DELETED
@@ -1,244 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # vim: set syntax=ruby
5
-
6
- # kettle-changelog: Generate a CHANGELOG.md entry for the current VERSION.
7
- # - Reads VERSION from lib/**/version.rb (must be unique across files)
8
- # - Set K_CHANGELOG_PATH to read/write a changelog outside the current gem root
9
- # - Set K_CHANGELOG_COVERAGE_ROOT to collect coverage from another bundle root
10
- # - Set K_CHANGELOG_VERSION_FILE to use a specific version.rb for monorepo roots
11
- # - Set K_CHANGELOG_GEM_NAME to use a specific gem name for root changelogs
12
- # - Moves entries from the "Unreleased" section into a new versioned section
13
- # - Prepends 4 heading lines:
14
- # - TAG
15
- # - COVERAGE (line coverage)
16
- # - BRANCH COVERAGE (branch coverage)
17
- # - percent documented (parsed from `bin/rake yard` output)
18
- # - Updates bottom link references to GitHub style, converts any existing
19
- # GitLab links to GitHub links, and appends the new [X.Y.Z] and [X.Y.Zt] links.
20
- #
21
- # Notes:
22
- # - Strict mode (default) removes stale coverage output, runs `bundle exec
23
- # kettle-test` with JSON coverage enabled, and reads the freshly generated
24
- # coverage/coverage.json.
25
- # - Non-strict mode reads existing coverage JSON when present and leaves
26
- # unavailable coverage or documentation stats blank with a warning.
27
- # - Expects `bin/rake yard` or `bin/yard` to be available for documentation stats
28
- # when strict mode is enabled.
29
-
30
- $stdout.sync = true
31
-
32
- # Do not rely on Bundler; allow running in repos that do not depend on kettle-dev
33
- # Ensure RubyGems is available for 'require' lookups
34
- begin
35
- require "rubygems"
36
- rescue LoadError
37
- # Older Rubies always have rubygems; continue anyway
38
- end
39
-
40
- script_basename = File.basename(__FILE__)
41
- repo_lib = File.expand_path("../lib", __dir__)
42
- $LOAD_PATH.unshift(repo_lib) if File.directory?(repo_lib) && !$LOAD_PATH.include?(repo_lib)
43
-
44
- begin
45
- # Standard library
46
- require "json"
47
- require "time"
48
- require "open3"
49
- require "shellwords"
50
-
51
- # This library
52
- require "kettle/dev"
53
- require "kettle/ndjson"
54
- require "kettle/dev/executable_version"
55
- Kettle::Dev::ExecutableVersion.print_and_exit!(script_basename, ARGV, value_option: true)
56
- machine_output = ARGV.any? { |arg| arg == "--json" || arg.start_with?("--events") }
57
- Kettle::Dev::ExecutableVersion.print_header(script_basename) unless machine_output
58
- rescue LoadError => e
59
- warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
60
- warn("Hint: Ensure the host project has kettle-dev as a dependency and run bundle install.")
61
- exit(1)
62
- end
63
-
64
- if ARGV.include?("-h") || ARGV.include?("--help")
65
- puts <<~USAGE
66
- Usage: kettle-changelog [--version VERSION] [--yes] [--update-prep|--reformat|--backfill-released-version VERSION] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--json|--events[=TYPES]] [--no-strict] [--no-coverage-threshold]
67
-
68
- Detects the current version from lib/**/version.rb, the latest live release, and
69
- the most recent CHANGELOG.md release section, then prompts to confirm the selected plan:
70
- create a new release section, update the prepared release section in place, or reformat only.
71
-
72
- Release plans add coverage and documentation stats, move entries from [Unreleased],
73
- and update bottom link references to GitHub style.
74
-
75
- Options:
76
- --version VERSION Use this version instead of detecting VERSION from lib/**/version.rb
77
- --yes Auto-approve the selected release plan
78
- --update-prep Force updating the most recent prepared release section in place
79
- --reformat Normalize CHANGELOG.md structure without release-state planning
80
- --backfill-released-version VERSION
81
- Add a tagged historical section without moving Unreleased entries
82
- --pending-release Query whether the changelog has release work pending; exits 0 for yes, 1 for no
83
- --release-state,
84
- --release-status Print changelog release state, including latest published release and pending sources
85
- --refresh-cache Bypass cached gem.coop version data for this invocation
86
- --add-unreleased-entry Add one entry to an existing section under ## [Unreleased]
87
- --section SECTION Unreleased section to receive the entry (Added, Changed, Deprecated, Removed, Fixed, Security)
88
- --entry TEXT Changelog entry text; "- " is added when omitted
89
- --json Print query output as JSON
90
- --events[=TYPES] Print newline-delimited JSON changelog events
91
- --no-strict Allow missing coverage and yard data (warnings only, no errors)
92
- --no-coverage-threshold Generate coverage without hard-failing below configured thresholds
93
-
94
- Environment:
95
- K_CHANGELOG_STRICT=false Disable strict mode (equivalent to --no-strict flag)
96
- K_CHANGELOG_COVERAGE_HARD=false Disable coverage threshold hard-failure
97
- K_CHANGELOG_PATH=path Read and write this changelog instead of ./CHANGELOG.md
98
- K_CHANGELOG_COVERAGE_ROOT=path Run/read coverage from this root instead of the gem root
99
- K_CHANGELOG_VERSION_FILE=path Read VERSION from this file instead of lib/**/version.rb
100
- K_CHANGELOG_GEM_NAME=name Read live release data as this gem instead of root .gemspec
101
- KETTLE_GEM_COOP_REFRESH=true Bypass cached gem.coop version data
102
-
103
- Data generation:
104
- Strict mode is the default. Release plans remove stale coverage output,
105
- run bundle exec kettle-test with JSON coverage enabled, read the resulting
106
- coverage/coverage.json, and collect documentation stats from bin/rake yard
107
- or bin/yard.
108
-
109
- Strict mode fails if specs fail, coverage JSON is not produced, coverage
110
- is below the configured project thresholds, or documentation stats cannot
111
- be collected.
112
-
113
- Non-strict mode does not generate missing coverage or documentation data;
114
- it warns and leaves unavailable stats blank.
115
-
116
- Runtime requirements:
117
- - bundle exec kettle-test available for strict coverage generation
118
- - bin/rake yard or bin/yard available for strict documentation stats
119
-
120
- Use --no-strict or K_CHANGELOG_STRICT=false to allow missing data.
121
- Use --no-coverage-threshold or K_CHANGELOG_COVERAGE_HARD=false to run fresh
122
- coverage without hard-failing below configured thresholds.
123
- USAGE
124
- exit(0)
125
- end
126
-
127
- begin
128
- def extract_version_arg!(argv)
129
- version = nil
130
- if (idx = argv.index("--version"))
131
- version = argv[idx + 1]
132
- Kettle::Dev::ExitAdapter.abort("--version requires a VERSION") if version.to_s.empty?
133
- argv.slice!(idx, 2)
134
- end
135
- argv.delete_if do |arg|
136
- if arg.start_with?("--version=", "version=")
137
- version = arg.split("=", 2)[1]
138
- true
139
- else
140
- false
141
- end
142
- end
143
- Kettle::Dev::Versioning.normalize_explicit_version(version)
144
- end
145
-
146
- def extract_events_arg!(argv)
147
- events = false
148
- event_types = nil
149
- argv.delete_if do |arg|
150
- if arg == "--events"
151
- events = true
152
- elsif arg.start_with?("--events=")
153
- events = true
154
- event_types = arg.split("=", 2)[1]
155
- else
156
- false
157
- end
158
- end
159
- [events, event_types]
160
- end
161
-
162
- # Determine if strict mode is enabled (default: true)
163
- strict_mode = !ARGV.include?("--no-strict") && ENV.fetch("K_CHANGELOG_STRICT", "true").downcase != "false"
164
- update_prep = ARGV.delete("--update-prep")
165
- reformat_only = ARGV.delete("--reformat")
166
- yes = !!ARGV.delete("--yes")
167
- pending_release_query = ARGV.delete("--pending-release")
168
- release_state_query = ARGV.delete("--release-state") || ARGV.delete("--release-status")
169
- refresh_cache = ARGV.delete("--refresh-cache")
170
- add_unreleased_entry = ARGV.delete("--add-unreleased-entry") || ARGV.delete("--add-changelog-entry")
171
- json_output = ARGV.delete("--json")
172
- events_output, event_types = extract_events_arg!(ARGV)
173
- Kettle::Dev::ExitAdapter.abort("--events cannot be combined with --json") if events_output && json_output
174
- coverage_threshold_disabled = ARGV.include?("--no-coverage-threshold") || ARGV.include?("--no-coverage-thresholds")
175
- coverage_hard = !coverage_threshold_disabled && ENV.fetch("K_CHANGELOG_COVERAGE_HARD", "true").downcase != "false"
176
- version_override = extract_version_arg!(ARGV)
177
-
178
- def extract_option_arg!(argv, name)
179
- value = nil
180
- if (idx = argv.index(name))
181
- value = argv[idx + 1]
182
- Kettle::Dev::ExitAdapter.abort("#{name} requires a value") if value.to_s.empty?
183
- argv.slice!(idx, 2)
184
- end
185
- argv.delete_if do |arg|
186
- if arg.start_with?("#{name}=")
187
- value = arg.split("=", 2)[1]
188
- true
189
- else
190
- false
191
- end
192
- end
193
- value
194
- end
195
-
196
- historical_backfill_version = extract_option_arg!(ARGV, "--backfill-released-version")
197
-
198
- if add_unreleased_entry
199
- section = extract_option_arg!(ARGV, "--section")
200
- entry = extract_option_arg!(ARGV, "--entry")
201
- Kettle::Dev::ExitAdapter.abort("--section is required with --add-unreleased-entry") if section.to_s.empty?
202
- Kettle::Dev::ExitAdapter.abort("--entry is required with --add-unreleased-entry") if entry.to_s.empty?
203
-
204
- result = Kettle::Dev::ChangelogEntryAdder.new(section: section, entry: entry).run
205
- message = (result == :changed) ? "CHANGELOG.md updated under Unreleased #{section}." : "CHANGELOG.md already contains that Unreleased #{section} entry."
206
- puts(json_output ? JSON.pretty_generate({changed: result == :changed, section: section, entry: entry}) : message)
207
- exit(0)
208
- end
209
-
210
- event_stream = events_output ? Kettle::Ndjson.event_stream($stdout, types: event_types) : nil
211
- cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, reformat_only: reformat_only, historical_backfill_version: historical_backfill_version, version: version_override, refresh_cache: refresh_cache, yes: yes, event_stream: event_stream)
212
- if release_state_query
213
- state = cli.release_state
214
- puts(json_output ? JSON.pretty_generate(state) : cli.release_state_table(state))
215
- exit(0)
216
- end
217
-
218
- if pending_release_query
219
- state = cli.pending_release_status
220
- output = if json_output
221
- JSON.pretty_generate(state)
222
- elsif state.fetch(:pending_release)
223
- "true"
224
- else
225
- "false"
226
- end
227
- puts(output)
228
- exit(state.fetch(:pending_release) ? 0 : 1)
229
- end
230
-
231
- cli.run
232
- rescue LoadError => e
233
- warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
234
- warn(e.backtrace.join("\n")) if ENV["DEBUG"]
235
- exit(1)
236
- rescue SystemExit => e
237
- # Preserve exit status, but ensure at least a newline so shells don't show an empty line only.
238
- warn("#{script_basename}: exited (status=#{e.status}, msg=#{e.message})") if e.status != 0
239
- raise
240
- rescue => e
241
- warn("#{script_basename}: unexpected error: #{e.class}: #{e.message}")
242
- warn(e.backtrace.join("\n"))
243
- exit(1)
244
- end