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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +35 -1
- data/README.md +1 -1
- data/lib/kettle/dev/release_cli.rb +94 -3
- data/lib/kettle/dev/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +8 -10
- metadata.gz.sig +0 -0
- data/exe/kettle-changelog +0 -244
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7cdd1aace2e06deda5e6bb04aa3b2e6ef67408f0521c8d0ed6a8fa27fad23be
|
|
4
|
+
data.tar.gz: e23d202928cd64875dffdb94bb426e0925e2033a7b07ba36b154eba7c83175d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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/
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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
|
data/lib/kettle/dev/version.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Kettle
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = "
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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/
|
|
406
|
-
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/
|
|
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/
|
|
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
|