kettle-dev 2.2.8 → 2.2.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: 20e42b5ed4a5e7b52a046fa7cfdfe63c08963947c2414aa4cf22251419ffb853
4
- data.tar.gz: e578b54456e497e115ccdb9bb54efc4d32be13e2b294d2b0857964b75bc5c7ff
3
+ metadata.gz: 9216115bc5448b3e08cfcd393c22029d70e7b6d31bcb80617b44b7d97644b649
4
+ data.tar.gz: e86c38ac97fbac63e1258bf15d6454f408e62ffe927af628bc5f7090472f98fd
5
5
  SHA512:
6
- metadata.gz: 676baf6a4ecffe8745d94c3016b402e8c002f98ca61f9ea478b9e629d42c7c57a82795682d49c853c19fff74982e8c70af5cf75b1e0eb6b28ce2fb904995762e
7
- data.tar.gz: a000e3b43e2ea900fcdb03cb2471db4a8c312113c753524ca7be658119c81bba1cfb7113e0ccb576a2cf6b471c472fc01c6b71afc4eebf7322b3760e3a848c4c
6
+ metadata.gz: 52d369f7c70a48bf3c2fde5898cc7f99ac9eda0b6e09319d7166976e20057d95360a56f29bf00dbd226976a853dd212e915fd31026b7c647df1f68bee6331cd4
7
+ data.tar.gz: 7ab61300d6f489ddc25636e10dcea667998a20c5ef3755a9b7773e2ccb0fde69871c8ba5607303a618501a55c2c05289dc3585905c6243e1ca524a7de1cb7c8a
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
+ ## [2.2.10] - 2026-06-16
34
+
35
+ - TAG: [v2.2.10][2.2.10t]
36
+ - COVERAGE: 92.24% -- 3911/4240 lines in 28 files
37
+ - BRANCH COVERAGE: 73.87% -- 1552/2101 branches in 28 files
38
+ - 64.35% documented
39
+
40
+ ### Changed
41
+
42
+ - `kettle-pre-release` now caches successful Markdown image URL validations in
43
+ the global kettle-dev state cache for seven days to avoid repeated network
44
+ HEAD requests during release readiness checks.
45
+
46
+ ## [2.2.9] - 2026-06-14
47
+
48
+ - TAG: [v2.2.9][2.2.9t]
49
+ - COVERAGE: 92.57% -- 3863/4173 lines in 28 files
50
+ - BRANCH COVERAGE: 74.00% -- 1534/2073 branches in 28 files
51
+ - 65.38% documented
52
+
53
+ ### Fixed
54
+
55
+ - `kettle-gha-sha-pins --check` no longer fails solely because releases outside
56
+ the selected `--upgrade` policy exist, and `kettle-pre-release` now validates
57
+ workflow pins with the inclusive `major` policy used for release readiness.
58
+
59
+ - `kettle-gha-sha-pins --write` now handles major-line adjacent version comments
60
+ such as `# v7` idempotently instead of repeatedly planning the same
61
+ `update_version_comment` change.
62
+
33
63
  ## [2.2.8] - 2026-06-13
34
64
 
35
65
  - TAG: [v2.2.8][2.2.8t]
@@ -2109,7 +2139,11 @@ Please file a bug if you notice a violation of semantic versioning.
2109
2139
  - Selecting will run the selected workflow via `act`
2110
2140
  - This may move to its own gem in the future.
2111
2141
 
2112
- [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.8...HEAD
2142
+ [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.10...HEAD
2143
+ [2.2.10]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.9...v2.2.10
2144
+ [2.2.10t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.2.10
2145
+ [2.2.9]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.8...v2.2.9
2146
+ [2.2.9t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.2.9
2113
2147
  [2.2.8]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.7...v2.2.8
2114
2148
  [2.2.8t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v2.2.8
2115
2149
  [2.2.7]: https://github.com/kettle-dev/kettle-dev/compare/v2.2.6...v2.2.7
data/README.md CHANGED
@@ -854,7 +854,7 @@ Thanks for RTFM. ☺️
854
854
  [📌gitmoji]: https://gitmoji.dev
855
855
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
856
856
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
857
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.171-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
857
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.240-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
858
858
  [🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
859
859
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
860
860
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -33,6 +33,8 @@ module Kettle
33
33
  DEFAULT_UPGRADE_LEVEL = "patch"
34
34
  DEFAULT_CACHE_TTL_SECONDS = 24 * 60 * 60
35
35
  VALID_UPGRADE_LEVELS = %w[major minor patch].freeze
36
+ VERSION_COMMENT_SUFFIX_RE = /\A\s+#\s*v?(?<version>\d+(?:\.\d+\.\d+(?:[-.]?[0-9A-Za-z.-]+)?)?)/
37
+ VERSION_COMMENT_REPLACEMENT_RE = /\A(?<prefix>\s+#\s*)v?\d+(?:\.\d+\.\d+(?:[-.]?[0-9A-Za-z.-]+)?)?/
36
38
 
37
39
  def initialize(argv, err: $stderr)
38
40
  @argv = argv
@@ -215,7 +217,7 @@ module Kettle
215
217
 
216
218
  print_report(state)
217
219
  return 2 unless state[:failures].zero?
218
- return 3 if @options[:check] && (state[:updates].positive? || state[:outdated_pins].any?)
220
+ return 3 if @options[:check] && state[:updates].positive?
219
221
 
220
222
  0
221
223
  end
@@ -745,8 +747,8 @@ module Kettle
745
747
  return nil unless token_info[:token] == old_token
746
748
 
747
749
  suffix = raw[token_info[:span]..-1].to_s
748
- match = suffix.match(/\A\s+#\s*v?(\d+\.\d+\.\d+(?:[-.]?[0-9A-Za-z.-]+)?)/)
749
- match && match[1]
750
+ match = suffix.match(VERSION_COMMENT_SUFFIX_RE)
751
+ match && match[:version]
750
752
  end
751
753
 
752
754
  def build_replacement_from_line(text, line, col, old_token, new_ref, new_version = nil)
@@ -767,7 +769,7 @@ module Kettle
767
769
  new_scalar = rendered[:quoted]
768
770
  if new_version && token_info[:quote] == :plain
769
771
  suffix = raw[span..-1].to_s
770
- comment = suffix.match(/\A(?<prefix>\s+#\s*)v?\d+\.\d+\.\d+(?:[-.]?[0-9A-Za-z.-]+)?/)
772
+ comment = suffix.match(VERSION_COMMENT_REPLACEMENT_RE)
771
773
  if comment
772
774
  span += comment[0].length
773
775
  new_scalar += "#{comment[:prefix]}v#{new_version}"
@@ -902,7 +904,7 @@ module Kettle
902
904
  lines << "- #{change[:path]}:#{change[:line]} #{from} -> #{to} #{change[:reason]}"
903
905
  end
904
906
  end
905
- if @options[:check] && (state[:planned_changes].any? || state[:outdated_pins].any?)
907
+ if @options[:check] && state[:planned_changes].any?
906
908
  lines << ""
907
909
  lines << "Recommended fix: kettle-gha-sha-pins --write --upgrade #{@options[:upgrade]}"
908
910
  end
@@ -2,9 +2,12 @@
2
2
 
3
3
  require "optparse"
4
4
  require "English"
5
+ require "json"
6
+ require "fileutils"
5
7
  require "uri"
6
8
  require "net/http"
7
9
  require "openssl"
10
+ require "time"
8
11
  begin
9
12
  require "addressable/uri"
10
13
  rescue LoadError
@@ -21,6 +24,8 @@ module Kettle
21
24
  #
22
25
  # Usage: Kettle::Dev::PreReleaseCLI.new(check_num: 1).run
23
26
  class PreReleaseCLI
27
+ IMAGE_URL_CACHE_TTL_SECONDS = 7 * 24 * 60 * 60
28
+
24
29
  # Simple HTTP helpers for link validation
25
30
  module HTTP
26
31
  module_function
@@ -93,6 +98,85 @@ module Kettle
93
98
  end
94
99
  end
95
100
 
101
+ # Persistent cache of successfully validated Markdown image URLs.
102
+ class ImageUrlCache
103
+ VERSION = 1
104
+
105
+ def self.default_path
106
+ state_home = ENV["XDG_STATE_HOME"]
107
+ state_home = File.join(Dir.home, ".local", "state") if state_home.to_s.empty?
108
+ File.join(state_home, "kettle-dev", "image-url-cache.json")
109
+ rescue ArgumentError
110
+ nil
111
+ end
112
+
113
+ def initialize(path:, ttl_seconds: IMAGE_URL_CACHE_TTL_SECONDS, clock: -> { Time.now })
114
+ @path = path
115
+ @ttl_seconds = ttl_seconds
116
+ @clock = clock
117
+ @data = nil
118
+ end
119
+
120
+ def fresh_success?(url)
121
+ entry = data.fetch("images")[url.to_s]
122
+ return false unless entry.is_a?(Hash)
123
+ return false unless entry["ok"] == true
124
+
125
+ fresh_entry?(entry)
126
+ end
127
+
128
+ def write_success(url)
129
+ return if @path.to_s.empty?
130
+ return if url.to_s.empty?
131
+
132
+ data.fetch("images")[url.to_s] = {
133
+ "ok" => true,
134
+ "cached_at" => @clock.call.utc.iso8601
135
+ }
136
+ save!
137
+ end
138
+
139
+ def to_h
140
+ data
141
+ end
142
+
143
+ private
144
+
145
+ def data
146
+ @data ||= load_data
147
+ end
148
+
149
+ def load_data
150
+ parsed = if @path && File.file?(@path)
151
+ JSON.parse(File.read(@path))
152
+ end
153
+ return empty_data unless parsed.is_a?(Hash)
154
+ return empty_data unless parsed["version"].to_i == VERSION
155
+
156
+ parsed["version"] ||= VERSION
157
+ parsed["images"] = {} unless parsed["images"].is_a?(Hash)
158
+ parsed
159
+ rescue JSON::ParserError, Errno::EACCES
160
+ empty_data
161
+ end
162
+
163
+ def empty_data
164
+ {"version" => VERSION, "images" => {}}
165
+ end
166
+
167
+ def save!
168
+ FileUtils.mkdir_p(File.dirname(@path))
169
+ File.write(@path, JSON.pretty_generate(data) + "\n")
170
+ end
171
+
172
+ def fresh_entry?(entry)
173
+ cached_at = Time.iso8601(entry["cached_at"].to_s)
174
+ cached_at >= @clock.call - @ttl_seconds
175
+ rescue ArgumentError
176
+ false
177
+ end
178
+ end
179
+
96
180
  # Markdown parsing helpers
97
181
  module Markdown
98
182
  module_function
@@ -184,6 +268,8 @@ module Kettle
184
268
  def initialize(check_num: 1)
185
269
  @check_num = (check_num || 1).to_i
186
270
  @check_num = 1 if @check_num < 1
271
+ @image_url_cache_path = configured_image_url_cache_path
272
+ @refresh_image_url_cache = env_truthy?(ENV["KETTLE_IMAGE_URL_CACHE_REFRESH"])
187
273
  end
188
274
 
189
275
  # Execute configured checks starting from @check_num.
@@ -210,7 +296,7 @@ module Kettle
210
296
  # @return [void]
211
297
  def check_github_actions_sha_pins!
212
298
  puts "[kettle-pre-release] Check 1: Validate GitHub Actions SHA pins"
213
- status = Kettle::Dev::GhaShaPinsCLI.new(["--root", Dir.pwd, "--check"]).run!
299
+ status = Kettle::Dev::GhaShaPinsCLI.new(["--root", Dir.pwd, "--check", "--upgrade", "major"]).run!
214
300
  return nil if status.zero?
215
301
 
216
302
  Kettle::Dev::ExitAdapter.abort("GitHub Actions SHA pin validation failed")
@@ -272,12 +358,19 @@ module Kettle
272
358
  puts "[kettle-pre-release] Check 3: Validate Markdown image links (HTTP HEAD)"
273
359
  urls = Markdown.extract_image_urls_from_files
274
360
  puts "[kettle-pre-release] Found #{urls.size} unique image URL(s)."
361
+ cache = image_url_cache
275
362
  failures = []
276
363
  urls.each do |url|
277
364
  print(" -> #{url} … ")
365
+ if cache && !@refresh_image_url_cache && cache.fresh_success?(url)
366
+ puts "OK (cached)"
367
+ next
368
+ end
369
+
278
370
  ok = HTTP.head_ok?(url)
279
371
  if ok
280
372
  puts "OK"
373
+ cache&.write_success(url)
281
374
  else
282
375
  puts "FAIL"
283
376
  failures << url
@@ -292,6 +385,26 @@ module Kettle
292
385
  end
293
386
  nil
294
387
  end
388
+
389
+ private
390
+
391
+ def image_url_cache
392
+ return nil if @image_url_cache_path.to_s.empty?
393
+
394
+ @image_url_cache ||= ImageUrlCache.new(path: @image_url_cache_path)
395
+ end
396
+
397
+ def configured_image_url_cache_path
398
+ value = ENV.fetch("KETTLE_IMAGE_URL_CACHE", nil)
399
+ return ImageUrlCache.default_path if value.nil? || value.to_s.strip.empty?
400
+ return nil if value.to_s.strip.match?(Kettle::Dev::ENV_FALSE_RE)
401
+
402
+ value
403
+ end
404
+
405
+ def env_truthy?(value)
406
+ !!value.to_s.strip.match?(/\A(true|y|yes|1|on)\z/i)
407
+ end
295
408
  end
296
409
  end
297
410
  end
@@ -3,7 +3,7 @@
3
3
  module Kettle
4
4
  module Dev
5
5
  module Version
6
- VERSION = "2.2.8"
6
+ VERSION = "2.2.10"
7
7
  end
8
8
  VERSION = Version::VERSION # Traditional Constant Location
9
9
  end
@@ -11,6 +11,16 @@ module Kettle
11
11
  def self.extract_image_urls_from_files: (?String glob_pattern) -> Array[String]
12
12
  end
13
13
 
14
+ class ImageUrlCache
15
+ VERSION: Integer
16
+
17
+ def self.default_path: () -> String?
18
+ def initialize: (path: String?, ?ttl_seconds: Integer, ?clock: ^() -> Time) -> void
19
+ def fresh_success?: (String url) -> bool
20
+ def write_success: (String url) -> void
21
+ def to_h: () -> Hash[String, untyped]
22
+ end
23
+
14
24
  def initialize: (?check_num: Integer) -> void
15
25
  def run: () -> void
16
26
  def check_markdown_uri_normalization!: () -> void
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.2.8
4
+ version: 2.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -338,10 +338,10 @@ licenses:
338
338
  - AGPL-3.0-only
339
339
  metadata:
340
340
  homepage_uri: https://kettle-dev.galtzo.com
341
- source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.2.8
342
- changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.2.8/CHANGELOG.md
341
+ source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v2.2.10
342
+ changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v2.2.10/CHANGELOG.md
343
343
  bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
344
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.2.8
344
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.2.10
345
345
  funding_uri: https://github.com/sponsors/pboling
346
346
  wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
347
347
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file