kettle-dev 2.3.8 → 2.3.10

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: fb500cfcc82a5cb325eb83ae9a5464e8333cce1c3ec03814b597063d41ec0e8a
4
- data.tar.gz: 124a6c4ad4a3dd9172c131d6d5276a685d7e15f2be8f5fc51722c028fc83f136
3
+ metadata.gz: 043a73e1a2fb35f02748261dc4d5f81865e233a982c10b73d6b957ef7a7ce66d
4
+ data.tar.gz: 4695065e3a9a9ce34f586fb4baeb76064b59048914a7c12e5e1590435dfe5862
5
5
  SHA512:
6
- metadata.gz: 1dd9b51023413bc9301d87dfc989dcec0844190b0cccfa90d8344bcd428403c70837f3972fdedc0cf2b4dee103af88923cc41ec43f80a869098a8a20da8a579d
7
- data.tar.gz: 94084430abf75c714d2b5be26a25f1a9c1be691fc9c4f2fa760fc767e7ce1698e01ab71f5b978ec744f9981eeedc7b474a663b51504c6d842c0569ffe7e79cb0
6
+ metadata.gz: 555075c8b24c72b0a3ae955d790d0af6b97f265a5fcd00dd3aad16bf3c46ca315081b64d6a39553039e70c438774553826eb7e2a7ecc358e441fa1a066cadeb1
7
+ data.tar.gz: 896b19afe31b86aaed964605cf63767ec1d8bc4d8c214e1599835781c2f86919d1510d1601a7597cb53cf57713812779b07de550d710e0d7ac40c3c4f7d28d4b
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,51 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [2.3.10] - 2026-07-23
34
+
35
+ - TAG: [v2.3.10][2.3.10t]
36
+ - COVERAGE: 92.35% -- 3790/4104 lines in 37 files
37
+ - BRANCH COVERAGE: 75.95% -- 1500/1975 branches in 37 files
38
+ - 60.00% documented
39
+
40
+ ### Changed
41
+
42
+ - Local development tooling now uses `kettle-gha-pins` 0.2.0 or newer on
43
+ Ruby 3.2+, while keeping older supported Ruby bundles solvable.
44
+ - `kettle-gha-sha-pins` now delegates GitHub Actions release-tag parsing,
45
+ canonicalization, cache persistence, GitHub release/tag/commit resolution,
46
+ and upgrade-target selection to the shared
47
+ `kettle-gha-pins` gem.
48
+ - `kettle-gha-sha-pins` is now a compatibility wrapper around the
49
+ `kettle-gha-pins` executable.
50
+
51
+ ### Fixed
52
+
53
+ - Restored `kettle-gha-pins` as a normal runtime dependency now that 0.2.1
54
+ supports the full `kettle-dev` Ruby floor.
55
+
56
+ - Appraisal rake task specs now rely on kettle-test's `silent_stream`
57
+ integration instead of stubbing `Object#warn`, which is not supported under
58
+ JRuby 9.4.
59
+
60
+ ## [2.3.9] - 2026-07-21
61
+
62
+ - TAG: [v2.3.9][2.3.9t]
63
+ - COVERAGE: 92.06% -- 4443/4826 lines in 37 files
64
+ - BRANCH COVERAGE: 74.09% -- 1733/2339 branches in 37 files
65
+ - 54.64% documented
66
+
67
+ ### Fixed
68
+
69
+ - `kettle-release` now records a short-lived gem.coop release-state cache-bust
70
+ marker after publishing, and `kettle-changelog --release-state` uses it to
71
+ refresh just-published gem version lookups.
72
+
73
+ - `dep-heads.yml` now runs each engine directly from
74
+ `gemfiles/dep_heads.gemfile`, avoiding `Appraisal.root.gemfile` bootstrap
75
+ failures with RubyGems/Bundler and gem.coop before the dep-heads appraisal is
76
+ selected.
77
+
33
78
  ## [2.3.8] - 2026-07-21
34
79
 
35
80
  - TAG: [v2.3.8][2.3.8t]
@@ -2568,7 +2613,11 @@ Please file a bug if you notice a violation of semantic versioning.
2568
2613
  - Selecting will run the selected workflow via `act`
2569
2614
  - This may move to its own gem in the future.
2570
2615
 
2571
- [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.8...HEAD
2616
+ [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.10...HEAD
2617
+ [2.3.10]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.9...v2.3.10
2618
+ [2.3.10t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.10
2619
+ [2.3.9]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.8...v2.3.9
2620
+ [2.3.9t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.9
2572
2621
  [2.3.8]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.7...v2.3.8
2573
2622
  [2.3.8t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.3.8
2574
2623
  [2.3.7]: https://github.com/kettle-dev/kettle-dev/compare/v2.3.6...v2.3.7
data/README.md CHANGED
@@ -470,6 +470,8 @@ What it does:
470
470
  ### Pre-release checks
471
471
 
472
472
  - Script: `exe/kettle-gha-sha-pins` (run as `kettle-gha-sha-pins`)
473
+ - New projects should install `kettle-gha-pins` and run `kettle-gha-pins`
474
+ directly; this `kettle-dev` command remains as a compatibility wrapper.
473
475
  - Purpose: Validate and optionally update GitHub Actions `uses:` refs to pinned
474
476
  SHAs and current allowed release versions.
475
477
  - Usage:
@@ -889,7 +891,7 @@ Thanks for RTFM. ☺️
889
891
  [📌gitmoji]: https://gitmoji.dev
890
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
891
893
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
892
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.761-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
894
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.104-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
893
895
  [🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
894
896
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
895
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-changelog CHANGED
@@ -55,7 +55,7 @@ end
55
55
 
56
56
  if ARGV.include?("-h") || ARGV.include?("--help")
57
57
  puts <<~USAGE
58
- Usage: kettle-changelog [--version VERSION] [--update-prep] [--pending-release] [--release-state] [--add-unreleased-entry --section SECTION --entry TEXT] [--json] [--no-strict] [--no-coverage-threshold]
58
+ Usage: kettle-changelog [--version VERSION] [--update-prep] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--json] [--no-strict] [--no-coverage-threshold]
59
59
 
60
60
  Detects the current version from lib/**/version.rb, the latest live release, and
61
61
  the most recent CHANGELOG.md release section, then prompts to confirm the selected plan:
@@ -70,6 +70,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
70
70
  --pending-release Query whether the changelog has release work pending; exits 0 for yes, 1 for no
71
71
  --release-state,
72
72
  --release-status Print changelog release state, including latest published release and pending sources
73
+ --refresh-cache Bypass cached gem.coop version data for this invocation
73
74
  --add-unreleased-entry Add one entry to an existing section under ## [Unreleased]
74
75
  --section SECTION Unreleased section to receive the entry (Added, Changed, Deprecated, Removed, Fixed, Security)
75
76
  --entry TEXT Changelog entry text; "- " is added when omitted
@@ -82,6 +83,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
82
83
  K_CHANGELOG_COVERAGE_HARD=false Disable coverage threshold hard-failure
83
84
  K_CHANGELOG_VERSION_FILE=path Read VERSION from this file instead of lib/**/version.rb
84
85
  K_CHANGELOG_GEM_NAME=name Read live release data as this gem instead of root .gemspec
86
+ KETTLE_GEM_COOP_REFRESH=true Bypass cached gem.coop version data
85
87
 
86
88
  Data generation:
87
89
  Strict mode is the default. Release plans remove stale coverage output,
@@ -131,6 +133,7 @@ begin
131
133
  update_prep = ARGV.delete("--update-prep")
132
134
  pending_release_query = ARGV.delete("--pending-release")
133
135
  release_state_query = ARGV.delete("--release-state") || ARGV.delete("--release-status")
136
+ refresh_cache = ARGV.delete("--refresh-cache")
134
137
  add_unreleased_entry = ARGV.delete("--add-unreleased-entry") || ARGV.delete("--add-changelog-entry")
135
138
  json_output = ARGV.delete("--json")
136
139
  coverage_threshold_disabled = ARGV.include?("--no-coverage-threshold") || ARGV.include?("--no-coverage-thresholds")
@@ -167,7 +170,7 @@ begin
167
170
  exit(0)
168
171
  end
169
172
 
170
- cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, version: version_override)
173
+ cli = Kettle::Dev::ChangelogCLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, version: version_override, refresh_cache: refresh_cache)
171
174
  if release_state_query
172
175
  state = cli.release_state
173
176
  puts(json_output ? JSON.pretty_generate(state) : cli.release_state_table(state))
@@ -3,38 +3,12 @@
3
3
 
4
4
  # vim: set syntax=ruby
5
5
 
6
- $stdout.sync = true
7
- $stderr.sync = true
8
-
9
- # Do not rely on Bundler; allow running in repos that do not depend on kettle-dev
10
- begin
11
- require "rubygems"
12
- rescue LoadError
13
- # Older Rubies always have rubygems; continue anyway
14
- end
15
-
16
6
  script_basename = File.basename(__FILE__)
17
7
 
18
8
  begin
19
- require "kettle/dev"
20
- rescue LoadError
21
- repo_lib = File.expand_path("../lib", __dir__)
22
- $LOAD_PATH.unshift(repo_lib) unless $LOAD_PATH.include?(repo_lib)
23
- begin
24
- require "kettle/dev"
25
- rescue LoadError => e
26
- warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
27
- warn("Hint: Install the kettle-dev gem (`gem install kettle-dev`) or add it to your Gemfile. Bundler is not required for this script.")
28
- exit(1)
29
- end
30
- end
31
-
32
- begin
33
- cli = Kettle::Dev::GhaShaPinsCLI.new(ARGV)
34
- status = cli.run!
35
- Kettle::Dev::ExitAdapter.exit(status)
36
- rescue => e
37
- warn("#{script_basename}: unexpected error: #{e.class}: #{e.message}")
38
- warn(e.backtrace.join("\n"))
39
- Kettle::Dev::ExitAdapter.exit(1)
9
+ Kernel.exec(Gem.ruby, Gem.bin_path("kettle-gha-pins", "kettle-gha-pins"), *ARGV)
10
+ rescue Gem::Exception, LoadError => e
11
+ warn("#{script_basename}: could not load kettle-gha-pins executable: #{e.class}: #{e.message}")
12
+ warn("Hint: Install the kettle-gha-pins gem (`gem install kettle-gha-pins`) or add it to your Gemfile.")
13
+ exit(1)
40
14
  end
@@ -27,7 +27,7 @@ module Kettle
27
27
  # @param enforce_coverage_thresholds [Boolean] when true, fail strict coverage generation below project thresholds
28
28
  # @param update_prep [Boolean] when true, update the most recent prepared release section in place
29
29
  # @param version [String, nil] explicit version override for gems without a literal VERSION constant
30
- def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, version: nil, root: Kettle::Dev::CIHelpers.project_root)
30
+ def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, version: nil, root: Kettle::Dev::CIHelpers.project_root, refresh_cache: false)
31
31
  @root = root
32
32
  @changelog_path = File.join(@root, "CHANGELOG.md")
33
33
  @coverage_path = File.join(@root, "coverage", "coverage.json")
@@ -35,6 +35,7 @@ module Kettle
35
35
  @enforce_coverage_thresholds = enforce_coverage_thresholds
36
36
  @update_prep = update_prep
37
37
  @version_override = Kettle::Dev::Versioning.normalize_explicit_version(version)
38
+ @refresh_cache = refresh_cache
38
39
  end
39
40
 
40
41
  # Main entry point to update CHANGELOG.md
@@ -399,11 +400,9 @@ module Kettle
399
400
  end
400
401
 
401
402
  def latest_released_versions(gem_name, current_version)
402
- uri = URI("https://gem.coop/api/v1/versions/#{gem_name}.json")
403
- res = Net::HTTP.get_response(uri)
404
- return [nil, nil] unless res.is_a?(Net::HTTPSuccess)
403
+ data = Kettle::Dev::GemCoopVersions.fetch(gem_name, version_hint: current_version, refresh: @refresh_cache)
404
+ return [nil, nil, nil] unless data.is_a?(Array)
405
405
 
406
- data = JSON.parse(res.body)
407
406
  versions = data.map { |h| h["number"] }.compact
408
407
  versions.reject! { |v| v.to_s.include?("-pre") || v.to_s.include?(".pre") || v.to_s.match?(/[a-zA-Z]/) }
409
408
  gversions = versions.map { |s| Gem::Version.new(s) }.sort
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "net/http"
6
+ require "time"
7
+ require "uri"
8
+
9
+ module Kettle
10
+ module Dev
11
+ module GemCoopVersions
12
+ CACHE_BUST_TTL_SECONDS = 15 * 60
13
+ ENV_REFRESH = "KETTLE_GEM_COOP_REFRESH"
14
+ ENV_MARKER_PATH = "KETTLE_GEM_COOP_CACHE_BUST_PATH"
15
+
16
+ class << self
17
+ def fetch(gem_name, version_hint: nil, refresh: false)
18
+ cache_bust = refresh || env_refresh? || fresh_release_marker?(gem_name, version_hint)
19
+ uri = versions_uri(gem_name, cache_bust: cache_bust)
20
+ request = Net::HTTP::Get.new(uri)
21
+ if cache_bust
22
+ request["Cache-Control"] = "no-cache"
23
+ request["Pragma"] = "no-cache"
24
+ end
25
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
26
+ http.request(request)
27
+ end
28
+ return nil unless response.is_a?(Net::HTTPSuccess)
29
+
30
+ JSON.parse(response.body)
31
+ end
32
+
33
+ def mark_released(gem_name, version)
34
+ return if gem_name.to_s.empty? || version.to_s.empty?
35
+
36
+ path = marker_path
37
+ data = read_marker(path)
38
+ data["releases"] ||= {}
39
+ data["releases"][gem_name.to_s] = {
40
+ "version" => version.to_s,
41
+ "released_at" => Time.now.utc.iso8601
42
+ }
43
+ FileUtils.mkdir_p(File.dirname(path))
44
+ File.write(path, JSON.pretty_generate(data) << "\n")
45
+ rescue => error
46
+ warn("[kettle-dev] could not update gem.coop cache-bust marker: #{error.class}: #{error.message}") if Kettle::Dev::DEBUGGING
47
+ end
48
+
49
+ def marker_path
50
+ configured = ENV.fetch(ENV_MARKER_PATH, "").to_s
51
+ return configured unless configured.empty?
52
+
53
+ state_home = ENV["XDG_STATE_HOME"]
54
+ state_home = File.join(Dir.home, ".local", "state") if state_home.to_s.empty?
55
+ File.join(state_home, "kettle-dev", "gem-coop-cache-bust.json")
56
+ end
57
+
58
+ private
59
+
60
+ def versions_uri(gem_name, cache_bust:)
61
+ uri = URI("https://gem.coop/api/v1/versions/#{gem_name}.json")
62
+ uri.query = "_kettle_cache_bust=#{Time.now.to_i}" if cache_bust
63
+ uri
64
+ end
65
+
66
+ def fresh_release_marker?(gem_name, version_hint)
67
+ entry = read_marker(marker_path).fetch("releases", {})[gem_name.to_s]
68
+ return false unless entry
69
+ return false if version_hint && entry["version"].to_s != version_hint.to_s
70
+
71
+ released_at = Time.iso8601(entry["released_at"].to_s)
72
+ released_at >= Time.now.utc - CACHE_BUST_TTL_SECONDS
73
+ rescue ArgumentError
74
+ false
75
+ end
76
+
77
+ def read_marker(path)
78
+ return {} unless File.file?(path)
79
+
80
+ parsed = JSON.parse(File.read(path))
81
+ parsed.is_a?(Hash) ? parsed : {}
82
+ rescue JSON::ParserError
83
+ {}
84
+ end
85
+
86
+ def env_refresh?
87
+ ENV.fetch(ENV_REFRESH, "").match?(Kettle::Dev::ENV_TRUE_RE)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end