kettle-gha-pins 0.2.0 → 0.2.1

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: 5ee4e694080aa9ce9e860fdd085015793830efc399a8ff295dd3fb6fa9dcf4dc
4
- data.tar.gz: d9b39c2192b77c942ffcf055abc30efd7a6ae6a78c8baf274776c1473bab179f
3
+ metadata.gz: 133a74b904371a5a48a6fe9bc0483ae4c3925b99167f394b452386b0a69664fd
4
+ data.tar.gz: d43ede8e0f4a3f3a1400b489e46d077d5744a6c4b0b5231b46b2de4c60951814
5
5
  SHA512:
6
- metadata.gz: 90f1f8ccee7b91f7fb72d4459577f0e145565a8897463ac64afa765213fb90e90af4ba2b945148e61a3a4f67e0c4718fb5a2c751ddbfa8d74d13a8b5b0082f61
7
- data.tar.gz: a8a087d884e121de676bb70eff44f5524368fe2890afab896ff18fcd775f78887d64b482a3e395cf576e4d39023f7daef93a02412012da8788669492f16c01d9
6
+ metadata.gz: c59c539a7107ea0b9f69464c06656ad317c1515099861e995c022036af1c6cf0c3e0e4227c58f97262f32d2b1b4fd2f4a536eec305c0f5ca93623116ad56eceb
7
+ data.tar.gz: 278b5a77866045c05a677a5e426359a7c34afe2556523a92ae042680a27e6e013e928124e42b0b26a8acc55d446370020e445114eb145c6bc77084cef1045a42
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,21 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [0.2.1] - 2026-07-22
34
+
35
+ - TAG: [v0.2.1][0.2.1t]
36
+ - COVERAGE: 99.08% -- 858/866 lines in 8 files
37
+ - BRANCH COVERAGE: 92.11% -- 350/380 branches in 8 files
38
+ - 22.22% documented
39
+
40
+ ### Fixed
41
+
42
+ - Lowered the generated gemspec Ruby requirement to `>= 2.4.0` to match
43
+ `kettle-dev`, restored Ruby 2.4-compatible runtime syntax, and explicitly
44
+ required the `set` stdlib used by the CLI.
45
+ - Made CLI line rewrites tolerate Psych scalar column differences across Ruby
46
+ versions.
47
+
33
48
  ## [0.2.0] - 2026-07-22
34
49
 
35
50
  - TAG: [v0.2.0][0.2.0t]
@@ -76,7 +91,9 @@ Please file a bug if you notice a violation of semantic versioning.
76
91
  - kettle-jem-template-20260720-005 - Generated README Support & Community rows
77
92
  now include a RubyForum help badge.
78
93
 
79
- [Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.0...HEAD
94
+ [Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.1...HEAD
95
+ [0.2.1]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.0...v0.2.1
96
+ [0.2.1t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.2.1
80
97
  [0.2.0]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.1.0...v0.2.0
81
98
  [0.2.0t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.2.0
82
99
  [0.1.0]: https://github.com/kettle-dev/kettle-gha-pins/compare/c633526495c7db0a8721a94a71c3def0f3cc71bb...v0.1.0
data/README.md CHANGED
@@ -56,8 +56,8 @@ kettle-gha-pins --write --upgrade patch
56
56
 
57
57
  ### Compatibility
58
58
 
59
- Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRuby.
60
- CI workflows and Appraisals are generated for MRI Ruby 3.2.0+.
59
+ Compatible with MRI Ruby 2.4.0+, and concordant releases of JRuby, and TruffleRuby.
60
+ CI workflows and Appraisals are generated for MRI Ruby 2.4.0+.
61
61
  This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
62
62
  may be higher than the gem's runtime compatibility floor when legacy Rubies are
63
63
  not practical for the current toolchain.
@@ -532,7 +532,7 @@ Thanks for RTFM. ☺️
532
532
  [📌gitmoji]: https://gitmoji.dev
533
533
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
534
534
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
535
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.857-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
535
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.866-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
536
536
  [🔐security]: https://github.com/kettle-dev/kettle-gha-pins/blob/main/SECURITY.md
537
537
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
538
538
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -558,7 +558,7 @@ Thanks for RTFM. ☺️
558
558
  | Field | Value |
559
559
  |---|---|
560
560
  | Package | kettle-gha-pins |
561
- | Description | 📌 Shared, deterministic GitHub Actions release-tag parsing, canonicalization, and upgrade selection logic for kettle-dev and kettle-jem workflow pin maintenance. |
561
+ | Description | 📌 GitHub Actions workflow SHA pin maintenance, including deterministic release-tag parsing, canonicalization, cache-backed GitHub ref resolution, and upgrade planning. |
562
562
  | Homepage | https://github.com/kettle-dev/kettle-gha-pins |
563
563
  | Source | https://github.com/kettle-dev/kettle-gha-pins |
564
564
  | License | `MIT` |
@@ -167,32 +167,32 @@ module Kettle
167
167
  end
168
168
 
169
169
  class << self
170
- def resolve_action_plan(...)
171
- ActionResolver.resolve_action_plan(...)
170
+ def resolve_action_plan(**kwargs)
171
+ ActionResolver.resolve_action_plan(**kwargs)
172
172
  end
173
173
 
174
- def determine_upgrade_plan(...)
175
- ActionResolver.determine_upgrade_plan(...)
174
+ def determine_upgrade_plan(**kwargs)
175
+ ActionResolver.determine_upgrade_plan(**kwargs)
176
176
  end
177
177
 
178
- def matching_version_entry(...)
179
- ActionResolver.matching_version_entry(...)
178
+ def matching_version_entry(versions, current_ref, current_sha, client, repo_ref)
179
+ ActionResolver.matching_version_entry(versions, current_ref, current_sha, client, repo_ref)
180
180
  end
181
181
 
182
- def version_entry_sha(...)
183
- ActionResolver.version_entry_sha(...)
182
+ def version_entry_sha(entry, client, repo_ref)
183
+ ActionResolver.version_entry_sha(entry, client, repo_ref)
184
184
  end
185
185
 
186
- def short_sha?(...)
187
- ActionResolver.short_sha?(...)
186
+ def short_sha?(candidate)
187
+ ActionResolver.short_sha?(candidate)
188
188
  end
189
189
 
190
- def non_sha?(...)
191
- ActionResolver.non_sha?(...)
190
+ def non_sha?(candidate)
191
+ ActionResolver.non_sha?(candidate)
192
192
  end
193
193
 
194
- def stale_sha?(...)
195
- ActionResolver.stale_sha?(...)
194
+ def stale_sha?(current, latest)
195
+ ActionResolver.stale_sha?(current, latest)
196
196
  end
197
197
  end
198
198
  end
@@ -4,6 +4,7 @@ require "json"
4
4
  require "open3"
5
5
  require "optparse"
6
6
  require "pathname"
7
+ require "set"
7
8
 
8
9
  require "psych"
9
10
  require "kettle/gha/pins"
@@ -273,9 +274,11 @@ module Kettle
273
274
  @options[:progress] = bool
274
275
  end
275
276
  opt.on("--skip-pattern PATTERN", "Skip workflow paths matching pattern (repeatable)") do |pattern|
276
- @options[:reject_patterns] << Regexp.new(pattern)
277
- rescue RegexpError => e
278
- Kernel.abort("Invalid --skip-pattern #{pattern.inspect}: #{e.message}")
277
+ begin
278
+ @options[:reject_patterns] << Regexp.new(pattern)
279
+ rescue RegexpError => e
280
+ Kernel.abort("Invalid --skip-pattern #{pattern.inspect}: #{e.message}")
281
+ end
279
282
  end
280
283
  opt.on("--[no-]validate", "Validate YAML after editing") do |bool|
281
284
  @options[:validate] = bool
@@ -297,25 +300,27 @@ module Kettle
297
300
  def load_workflows(paths, state)
298
301
  file_progress = progress_bar(title: "Files", total: paths.length)
299
302
  paths.each_with_object([]) do |path, workflows|
300
- state[:files_scanned] += 1
301
- text = begin
302
- File.read(path)
303
- rescue Errno::EACCES => e
304
- record_failure(state, path: path, error: "read_error: #{e.message}")
305
- next
306
- end
303
+ begin
304
+ state[:files_scanned] += 1
305
+ begin
306
+ text = File.read(path)
307
+ rescue Errno::EACCES => e
308
+ record_failure(state, path: path, error: "read_error: #{e.message}")
309
+ next
310
+ end
307
311
 
308
- parsed = begin
309
- Psych.parse_stream(text)
310
- rescue Psych::Exception => e
311
- record_failure(state, path: path, error: "yaml_parse_error: #{e.message}")
312
- next
313
- end
312
+ begin
313
+ parsed = Psych.parse_stream(text)
314
+ rescue Psych::Exception => e
315
+ record_failure(state, path: path, error: "yaml_parse_error: #{e.message}")
316
+ next
317
+ end
314
318
 
315
- uses_nodes = extract_uses_nodes(parsed, text)
316
- workflows << {path: path, text: text, uses_nodes: uses_nodes} unless uses_nodes.empty?
317
- ensure
318
- file_progress&.increment
319
+ uses_nodes = extract_uses_nodes(parsed, text)
320
+ workflows << {path: path, text: text, uses_nodes: uses_nodes} unless uses_nodes.empty?
321
+ ensure
322
+ file_progress&.increment
323
+ end
319
324
  end
320
325
  end
321
326
 
@@ -469,7 +474,7 @@ module Kettle
469
474
  action: {
470
475
  owner: parts[0],
471
476
  repo: parts[1],
472
- path: (parts.length > 2) ? parts[2..].join("/") : nil,
477
+ path: (parts.length > 2) ? parts[2..-1].join("/") : nil,
473
478
  ref: ref
474
479
  }
475
480
  }
@@ -594,14 +599,10 @@ module Kettle
594
599
  line_text = text.lines[line]
595
600
  return nil if line_text.nil?
596
601
 
597
- raw = line_text[col..]
598
- return nil if raw.nil?
599
-
600
- token_info = extract_scalar_token(raw)
602
+ token_info = locate_scalar_token(line_text, col, old_token)
601
603
  return nil unless token_info
602
- return nil unless token_info[:token] == old_token
603
604
 
604
- suffix = raw[token_info[:span]..].to_s
605
+ suffix = line_text[token_info[:end]..-1].to_s
605
606
  match = suffix.match(VERSION_COMMENT_SUFFIX_RE)
606
607
  match && match[:version]
607
608
  end
@@ -610,20 +611,16 @@ module Kettle
610
611
  line_text = text.lines[line]
611
612
  return nil if line_text.nil?
612
613
 
613
- raw = line_text[col..]
614
- return nil if raw.nil?
615
-
616
- token_info = extract_scalar_token(raw)
614
+ token_info = locate_scalar_token(line_text, col, old_token)
617
615
  return nil unless token_info
618
- return nil unless token_info[:token] == old_token
619
616
 
620
617
  rendered = render_replacement(old_token, new_ref, token_info[:quote])
621
618
  return nil unless rendered
622
619
 
623
- span = token_info[:span]
620
+ span = token_info[:end] - token_info[:start]
624
621
  new_scalar = rendered[:quoted]
625
622
  if new_version && token_info[:quote] == :plain
626
- suffix = raw[span..].to_s
623
+ suffix = line_text[token_info[:end]..-1].to_s
627
624
  comment = suffix.match(VERSION_COMMENT_REPLACEMENT_RE)
628
625
  if comment
629
626
  span += comment[0].length
@@ -632,14 +629,31 @@ module Kettle
632
629
  end
633
630
 
634
631
  {
635
- start: col,
636
- end: col + span,
632
+ start: token_info[:start],
633
+ end: token_info[:start] + span,
637
634
  new_scalar: new_scalar,
638
635
  new_ref: new_ref,
639
636
  old_token: old_token
640
637
  }
641
638
  end
642
639
 
640
+ def locate_scalar_token(line_text, col, old_token)
641
+ return nil if col.nil? || col.negative? || col >= line_text.length
642
+
643
+ matches = []
644
+ 0.upto(line_text.length - 1) do |index|
645
+ token_info = extract_scalar_token(line_text[index..-1])
646
+ next unless token_info
647
+ next unless token_info[:token] == old_token
648
+
649
+ matches << token_info.merge(
650
+ start: index,
651
+ end: index + token_info[:span]
652
+ )
653
+ end
654
+ matches.min_by { |match| [(match[:start] - col).abs, match[:start]] }
655
+ end
656
+
643
657
  def apply_edits(original_text, edits)
644
658
  lines = original_text.lines
645
659
  grouped = edits.group_by { |entry| entry[:line] }
@@ -652,7 +666,7 @@ module Kettle
652
666
  next if line.nil?
653
667
 
654
668
  entries.each do |entry|
655
- line = line[0...entry[:start]].to_s + entry[:new_scalar] + line[entry[:end]..].to_s
669
+ line = line[0...entry[:start]].to_s + entry[:new_scalar] + line[entry[:end]..-1].to_s
656
670
  end
657
671
  updated[line_num] = line
658
672
  end
@@ -96,13 +96,13 @@ module Kettle
96
96
  end
97
97
 
98
98
  def build_release_versions(data, tag_shas)
99
- release_tags = data.filter_map do |release|
99
+ release_tags = data.each_with_object([]) do |release, memo|
100
100
  next unless release.is_a?(Hash)
101
101
 
102
102
  tag = release["tag_name"].to_s
103
103
  next unless VersionRubric.parse(tag)
104
104
 
105
- tag
105
+ memo << tag
106
106
  end
107
107
 
108
108
  VersionRubric.build_release_versions(
@@ -41,7 +41,10 @@ module Kettle
41
41
  return nil if entries.empty?
42
42
  return nil if fresh && entries.length != versions.length
43
43
 
44
- entries.filter_map { |entry| deserialize_version_entry(entry) }
44
+ entries.each_with_object([]) do |entry, memo|
45
+ deserialized = deserialize_version_entry(entry)
46
+ memo << deserialized if deserialized
47
+ end
45
48
  .sort_by { |entry| entry[:version_obj] }
46
49
  .reverse
47
50
  end
@@ -147,11 +150,11 @@ module Kettle
147
150
  end
148
151
 
149
152
  def target_cache(version_entries)
150
- entries = version_entries.filter_map do |entry|
153
+ entries = version_entries.each_with_object([]) do |entry, memo|
151
154
  deserialized = deserialize_version_entry(entry)
152
155
  next unless deserialized
153
156
 
154
- deserialized.merge(cached_at: entry["cached_at"].to_s)
157
+ memo << deserialized.merge(cached_at: entry["cached_at"].to_s)
155
158
  end
156
159
  return {} if entries.empty?
157
160
 
@@ -4,7 +4,7 @@ module Kettle
4
4
  module Gha
5
5
  module Pins
6
6
  module Version
7
- VERSION = "0.2.0"
7
+ VERSION = "0.2.1"
8
8
  end
9
9
  VERSION = Version::VERSION # Traditional Constant Location
10
10
  end
@@ -38,11 +38,15 @@ module Kettle
38
38
 
39
39
  def build_release_versions(release_tags:, tag_shas:)
40
40
  released_tags = release_tags.each_with_object({}) { |tag, memo| memo[tag.to_s] = true }
41
- releases = release_tags.filter_map { |tag| entry(tag: tag, sha: tag_shas[tag.to_s]) }
42
- tag_versions = tag_shas.filter_map do |tag, sha|
41
+ releases = release_tags.each_with_object([]) do |tag, memo|
42
+ release_entry = entry(tag: tag, sha: tag_shas[tag.to_s])
43
+ memo << release_entry if release_entry
44
+ end
45
+ tag_versions = tag_shas.each_with_object([]) do |(tag, sha), memo|
43
46
  next if released_tags[tag.to_s]
44
47
 
45
- entry(tag: tag, sha: sha)
48
+ tag_entry = entry(tag: tag, sha: sha)
49
+ memo << tag_entry if tag_entry
46
50
  end
47
51
 
48
52
  sort_versions(canonicalize_equivalent_release_versions(releases + tag_versions)).reverse
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-gha-pins
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -199,20 +199,6 @@ dependencies:
199
199
  - - ">="
200
200
  - !ruby/object:Gem::Version
201
201
  version: 3.2.0
202
- - !ruby/object:Gem::Dependency
203
- name: ruby-progressbar
204
- requirement: !ruby/object:Gem::Requirement
205
- requirements:
206
- - - "~>"
207
- - !ruby/object:Gem::Version
208
- version: '1.13'
209
- type: :development
210
- prerelease: false
211
- version_requirements: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - "~>"
214
- - !ruby/object:Gem::Version
215
- version: '1.13'
216
202
  - !ruby/object:Gem::Dependency
217
203
  name: stone_checksums
218
204
  requirement: !ruby/object:Gem::Requirement
@@ -281,10 +267,10 @@ licenses:
281
267
  - MIT
282
268
  metadata:
283
269
  homepage_uri: https://github.com/kettle-dev/kettle-gha-pins
284
- source_code_uri: https://github.com/kettle-dev/kettle-gha-pins/tree/v0.2.0
285
- changelog_uri: https://github.com/kettle-dev/kettle-gha-pins/blob/v0.2.0/CHANGELOG.md
270
+ source_code_uri: https://github.com/kettle-dev/kettle-gha-pins/tree/v0.2.1
271
+ changelog_uri: https://github.com/kettle-dev/kettle-gha-pins/blob/v0.2.1/CHANGELOG.md
286
272
  bug_tracker_uri: https://github.com/kettle-dev/kettle-gha-pins/issues
287
- documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.2.0
273
+ documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.2.1
288
274
  funding_uri: https://github.com/sponsors/pboling
289
275
  wiki_uri: https://github.com/kettle-dev/kettle-gha-pins/wiki
290
276
  news_uri: https://www.railsbling.com/tags/kettle-gha-pins
@@ -307,7 +293,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
307
293
  requirements:
308
294
  - - ">="
309
295
  - !ruby/object:Gem::Version
310
- version: 3.2.0
296
+ version: 2.4.0
311
297
  required_rubygems_version: !ruby/object:Gem::Requirement
312
298
  requirements:
313
299
  - - ">="
metadata.gz.sig CHANGED
Binary file