kettle-gha-pins 0.2.0 → 0.3.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: 5ee4e694080aa9ce9e860fdd085015793830efc399a8ff295dd3fb6fa9dcf4dc
4
- data.tar.gz: d9b39c2192b77c942ffcf055abc30efd7a6ae6a78c8baf274776c1473bab179f
3
+ metadata.gz: 4b35d635c79b4982830fe80cda9f003e3cb5b6ad146fe1f35a2d9826852953ef
4
+ data.tar.gz: 2ca635cc1ee80d6bbb1511d0371b880b199dfe8bf1a0f0265a0ec465d60345c7
5
5
  SHA512:
6
- metadata.gz: 90f1f8ccee7b91f7fb72d4459577f0e145565a8897463ac64afa765213fb90e90af4ba2b945148e61a3a4f67e0c4718fb5a2c751ddbfa8d74d13a8b5b0082f61
7
- data.tar.gz: a8a087d884e121de676bb70eff44f5524368fe2890afab896ff18fcd775f78887d64b482a3e395cf576e4d39023f7daef93a02412012da8788669492f16c01d9
6
+ metadata.gz: 623fd4769e4597bff617136531ffbc403c9f8252ba67a7cbf8fa86a7304aa0e3e4d1e20a77020ca07dfdc72207b59c67f11fb4480291a59e163c2389cf8c5f93
7
+ data.tar.gz: 4dec38a332e0ed5a447f585f3d0cf41338aba8e1d6a0af5beb51a5116ceb12bb6ee4b9e43d02979d189b14fc1c1cb3cc3737c09160f931d999b0ed66c3fe01be
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,46 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [0.3.0] - 2026-07-23
34
+
35
+ - TAG: [v0.3.0][0.3.0t]
36
+ - COVERAGE: 98.92% -- 915/925 lines in 8 files
37
+ - BRANCH COVERAGE: 90.93% -- 371/408 branches in 8 files
38
+ - 22.77% documented
39
+
40
+ ### Changed
41
+
42
+ - `kettle-gha-pins` now defaults `--upgrade` to `major`, making
43
+ `kettle-gha-pins --check` fail for any unapplied GitHub Actions pin update
44
+ unless callers choose a narrower upgrade level.
45
+ - `kettle-gha-pins --check` now supports `--cooldown-days` and
46
+ `KETTLE_GHA_PINS_COOLDOWN_DAYS` so projects can warn on freshly released
47
+ action version upgrades before enforcing them.
48
+
49
+ - Action-resolution progress now uses `tty-progressbar` multi-line bars so
50
+ cached, live, and skipped counters no longer overwrite each other on TTYs.
51
+
52
+ ### Fixed
53
+
54
+ - GitHub Actions pin cache writes now persist tag SHA refs from release version
55
+ metadata, so repeated runs can reuse cached action resolution instead of
56
+ rechecking freshly cached actions live.
57
+
58
+ ## [0.2.1] - 2026-07-22
59
+
60
+ - TAG: [v0.2.1][0.2.1t]
61
+ - COVERAGE: 99.08% -- 858/866 lines in 8 files
62
+ - BRANCH COVERAGE: 92.11% -- 350/380 branches in 8 files
63
+ - 22.22% documented
64
+
65
+ ### Fixed
66
+
67
+ - Lowered the generated gemspec Ruby requirement to `>= 2.4.0` to match
68
+ `kettle-dev`, restored Ruby 2.4-compatible runtime syntax, and explicitly
69
+ required the `set` stdlib used by the CLI.
70
+ - Made CLI line rewrites tolerate Psych scalar column differences across Ruby
71
+ versions.
72
+
33
73
  ## [0.2.0] - 2026-07-22
34
74
 
35
75
  - TAG: [v0.2.0][0.2.0t]
@@ -76,7 +116,11 @@ Please file a bug if you notice a violation of semantic versioning.
76
116
  - kettle-jem-template-20260720-005 - Generated README Support & Community rows
77
117
  now include a RubyForum help badge.
78
118
 
79
- [Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.0...HEAD
119
+ [Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.0...HEAD
120
+ [0.3.0]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.1...v0.3.0
121
+ [0.3.0t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.3.0
122
+ [0.2.1]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.0...v0.2.1
123
+ [0.2.1t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.2.1
80
124
  [0.2.0]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.1.0...v0.2.0
81
125
  [0.2.0t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.2.0
82
126
  [0.1.0]: https://github.com/kettle-dev/kettle-gha-pins/compare/c633526495c7db0a8721a94a71c3def0f3cc71bb...v0.1.0
data/README.md CHANGED
@@ -35,17 +35,18 @@ For ordinary workflow pin maintenance, install this gem and run:
35
35
 
36
36
  ```console
37
37
  kettle-gha-pins --check
38
- kettle-gha-pins --write --upgrade patch
38
+ kettle-gha-pins --write --upgrade major
39
39
  ```
40
40
 
41
41
  ## 💡 Info you can shake a stick at
42
42
 
43
43
  | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
44
44
  |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
45
- | Works with JRuby | [![JRuby 10.0 Compat][💎jruby-10.0i]][🚎jruby-10.0-wf] [![JRuby current Compat][💎jruby-c-i]][🚎10-j-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf]|
46
- | Works with Truffle Ruby | [![Truffle Ruby 24.2 Compat][💎truby-24.2i]][🚎truby-24.2-wf] [![Truffle Ruby 25.0 Compat][💎truby-25.0i]][🚎truby-25.0-wf] [![Truffle Ruby 33.0 Compat][💎truby-33.0i]][🚎truby-33.0-wf] [![Truffle Ruby current Compat][💎truby-c-i]][🚎9-t-wf] [![Truffle Ruby HEAD Compat][💎truby-headi]][🚎3-hd-wf]|
45
+ | Works with JRuby | [![JRuby 9.2 Compat][💎jruby-9.2i]][🚎jruby-9.2-wf] [![JRuby 9.3 Compat][💎jruby-9.3i]][🚎jruby-9.3-wf] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎jruby-9.4-wf] [![JRuby 10.0 Compat][💎jruby-10.0i]][🚎jruby-10.0-wf] [![JRuby current Compat][💎jruby-c-i]][🚎10-j-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf]|
46
+ | Works with Truffle Ruby | [![Truffle Ruby 22.3 Compat][💎truby-22.3i]][🚎truby-22.3-wf] [![Truffle Ruby 23.0 Compat][💎truby-23.0i]][🚎truby-23.0-wf] [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎truby-23.1-wf] <br/> [![Truffle Ruby 24.2 Compat][💎truby-24.2i]][🚎truby-24.2-wf] [![Truffle Ruby 25.0 Compat][💎truby-25.0i]][🚎truby-25.0-wf] [![Truffle Ruby 33.0 Compat][💎truby-33.0i]][🚎truby-33.0-wf] [![Truffle Ruby current Compat][💎truby-c-i]][🚎9-t-wf] [![Truffle Ruby HEAD Compat][💎truby-headi]][🚎3-hd-wf]|
47
47
  | Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]|
48
- | Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎ruby-3.2-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎ruby-3.3-wf] [![Ruby 3.4 Compat][💎ruby-3.4i]][🚎ruby-3.4-wf]|
48
+ | Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎ruby-3.0-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎ruby-3.1-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎ruby-3.2-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎ruby-3.3-wf] [![Ruby 3.4 Compat][💎ruby-3.4i]][🚎ruby-3.4-wf]|
49
+ | Works with MRI Ruby 2 | [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎ruby-2.4-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎ruby-2.5-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎ruby-2.6-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎ruby-2.7-wf]|
49
50
  | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Get help from RubyForum][✉️ruby-forum-img]][✉️ruby-forum] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
50
51
  | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on GitHub.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
51
52
  | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
@@ -56,8 +57,8 @@ kettle-gha-pins --write --upgrade patch
56
57
 
57
58
  ### Compatibility
58
59
 
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+.
60
+ Compatible with MRI Ruby 2.4.0+, and concordant releases of JRuby, and TruffleRuby.
61
+ CI workflows and Appraisals are generated for MRI Ruby 2.4.0+.
61
62
  This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
62
63
  may be higher than the gem's runtime compatibility floor when legacy Rubies are
63
64
  not practical for the current toolchain.
@@ -147,8 +148,14 @@ The shared upgrade policies are:
147
148
  - `major` - upgrade to the latest higher release, including major-line tags such
148
149
  as `v2` to `v3`.
149
150
 
150
- Invalid upgrade levels normalize to `patch`, matching the historical
151
- `kettle-gha-sha-pins` default.
151
+ Invalid upgrade levels normalize to `major`, matching the strict default used
152
+ by release checks.
153
+
154
+ `kettle-gha-pins --check --cooldown-days DAYS` reports newly released action
155
+ version upgrades without failing until the selected release has aged past the
156
+ cooldown window. The same default can be supplied with
157
+ `KETTLE_GHA_PINS_COOLDOWN_DAYS`. The default is `0`, so checks remain strict
158
+ unless a project opts into a warning window.
152
159
 
153
160
  `Kettle::Gha::Pins::PersistentActionCache.default_path` intentionally preserves
154
161
  the historical `kettle-gha-sha-pins` cache location so command-line tools can
@@ -466,10 +473,22 @@ Thanks for RTFM. ☺️
466
473
  [🏀codecovi]: https://codecov.io/gh/kettle-dev/kettle-gha-pins/graph/badge.svg
467
474
  [🏀coveralls]: https://coveralls.io/github/kettle-dev/kettle-gha-pins?branch=main
468
475
  [🏀coveralls-img]: https://coveralls.io/repos/github/kettle-dev/kettle-gha-pins/badge.svg?branch=main
476
+ [🚎ruby-2.4-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-2.4.yml
477
+ [🚎ruby-2.5-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-2.5.yml
478
+ [🚎ruby-2.6-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-2.6.yml
479
+ [🚎ruby-2.7-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-2.7.yml
480
+ [🚎ruby-3.0-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-3.0.yml
481
+ [🚎ruby-3.1-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-3.1.yml
469
482
  [🚎ruby-3.2-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-3.2.yml
470
483
  [🚎ruby-3.3-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-3.3.yml
471
484
  [🚎ruby-3.4-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/ruby-3.4.yml
485
+ [🚎jruby-9.2-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/jruby-9.2.yml
486
+ [🚎jruby-9.3-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/jruby-9.3.yml
487
+ [🚎jruby-9.4-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/jruby-9.4.yml
472
488
  [🚎jruby-10.0-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/jruby-10.0.yml
489
+ [🚎truby-22.3-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/truffleruby-22.3.yml
490
+ [🚎truby-23.0-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/truffleruby-23.0.yml
491
+ [🚎truby-23.1-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/truffleruby-23.1.yml
473
492
  [🚎truby-24.2-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/truffleruby-24.2.yml
474
493
  [🚎truby-25.0-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/truffleruby-25.0.yml
475
494
  [🚎truby-33.0-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/truffleruby-33.0.yml
@@ -493,16 +512,28 @@ Thanks for RTFM. ☺️
493
512
  [🚎14-🔓️-wfi]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/unlocked_deps.yml/badge.svg
494
513
  [🚎15-🪪-wf]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/license-eye.yml
495
514
  [🚎15-🪪-wfi]: https://github.com/kettle-dev/kettle-gha-pins/actions/workflows/license-eye.yml/badge.svg
515
+ [💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
516
+ [💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
517
+ [💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
518
+ [💎ruby-2.7i]: https://img.shields.io/badge/Ruby-2.7-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
519
+ [💎ruby-3.0i]: https://img.shields.io/badge/Ruby-3.0-CC342D?style=for-the-badge&logo=ruby&logoColor=white
520
+ [💎ruby-3.1i]: https://img.shields.io/badge/Ruby-3.1-CC342D?style=for-the-badge&logo=ruby&logoColor=white
496
521
  [💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
497
522
  [💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
498
523
  [💎ruby-3.4i]: https://img.shields.io/badge/Ruby-3.4-CC342D?style=for-the-badge&logo=ruby&logoColor=white
499
524
  [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
500
525
  [💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue
526
+ [💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
527
+ [💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
528
+ [💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
501
529
  [💎truby-24.2i]: https://img.shields.io/badge/Truffle_Ruby-24.2-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
502
530
  [💎truby-25.0i]: https://img.shields.io/badge/Truffle_Ruby-25.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
503
531
  [💎truby-33.0i]: https://img.shields.io/badge/Truffle_Ruby-33.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
504
532
  [💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green
505
533
  [💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge&logo=ruby&logoColor=blue
534
+ [💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2-FBE742?style=for-the-badge&logo=ruby&logoColor=red
535
+ [💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3-FBE742?style=for-the-badge&logo=ruby&logoColor=red
536
+ [💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge&logo=ruby&logoColor=red
506
537
  [💎jruby-10.0i]: https://img.shields.io/badge/JRuby-10.0-FBE742?style=for-the-badge&logo=ruby&logoColor=red
507
538
  [💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
508
539
  [💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
@@ -532,7 +563,7 @@ Thanks for RTFM. ☺️
532
563
  [📌gitmoji]: https://gitmoji.dev
533
564
  [📌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
565
  [🧮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
566
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.925-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
536
567
  [🔐security]: https://github.com/kettle-dev/kettle-gha-pins/blob/main/SECURITY.md
537
568
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
538
569
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -558,7 +589,7 @@ Thanks for RTFM. ☺️
558
589
  | Field | Value |
559
590
  |---|---|
560
591
  | 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. |
592
+ | Description | 📌 GitHub Actions workflow SHA pin maintenance, including deterministic release-tag parsing, canonicalization, cache-backed GitHub ref resolution, and upgrade planning. |
562
593
  | Homepage | https://github.com/kettle-dev/kettle-gha-pins |
563
594
  | Source | https://github.com/kettle-dev/kettle-gha-pins |
564
595
  | License | `MIT` |
@@ -68,6 +68,7 @@ module Kettle
68
68
  updates = {
69
69
  sha: target_sha,
70
70
  version: target[:version],
71
+ released_at: target[:released_at],
71
72
  reason: UPGRADE_REASON
72
73
  }
73
74
  reason ||= UPGRADE_REASON
@@ -167,32 +168,32 @@ module Kettle
167
168
  end
168
169
 
169
170
  class << self
170
- def resolve_action_plan(...)
171
- ActionResolver.resolve_action_plan(...)
171
+ def resolve_action_plan(**kwargs)
172
+ ActionResolver.resolve_action_plan(**kwargs)
172
173
  end
173
174
 
174
- def determine_upgrade_plan(...)
175
- ActionResolver.determine_upgrade_plan(...)
175
+ def determine_upgrade_plan(**kwargs)
176
+ ActionResolver.determine_upgrade_plan(**kwargs)
176
177
  end
177
178
 
178
- def matching_version_entry(...)
179
- ActionResolver.matching_version_entry(...)
179
+ def matching_version_entry(versions, current_ref, current_sha, client, repo_ref)
180
+ ActionResolver.matching_version_entry(versions, current_ref, current_sha, client, repo_ref)
180
181
  end
181
182
 
182
- def version_entry_sha(...)
183
- ActionResolver.version_entry_sha(...)
183
+ def version_entry_sha(entry, client, repo_ref)
184
+ ActionResolver.version_entry_sha(entry, client, repo_ref)
184
185
  end
185
186
 
186
- def short_sha?(...)
187
- ActionResolver.short_sha?(...)
187
+ def short_sha?(candidate)
188
+ ActionResolver.short_sha?(candidate)
188
189
  end
189
190
 
190
- def non_sha?(...)
191
- ActionResolver.non_sha?(...)
191
+ def non_sha?(candidate)
192
+ ActionResolver.non_sha?(candidate)
192
193
  end
193
194
 
194
- def stale_sha?(...)
195
- ActionResolver.stale_sha?(...)
195
+ def stale_sha?(current, latest)
196
+ ActionResolver.stale_sha?(current, latest)
196
197
  end
197
198
  end
198
199
  end
@@ -1,48 +1,61 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ruby-progressbar"
3
+ require "tty-progressbar"
4
4
 
5
5
  module Kettle
6
6
  module Gha
7
7
  module Pins
8
8
  class CacheProgress
9
- FORMAT = "%t %b %c/%C"
10
- LENGTH = 30
9
+ FORMAT = "%<title>s [:bar] :current/:total"
10
+ WIDTH = 30
11
11
 
12
12
  def initialize(total:, cached_title:, live_title:, output:, enabled: true, skipped_title: nil)
13
13
  @total = total.to_i
14
14
  @cached_count = 0
15
15
  @live_count = 0
16
16
  @skipped_count = 0
17
- @cached_bar = progress_bar(cached_title, output, enabled)
18
- @live_bar = progress_bar(live_title, output, enabled)
19
- @skipped_bar = skipped_title ? progress_bar(skipped_title, output, enabled) : nil
17
+ @multibar = progress_enabled?(enabled, output) ? TTY::ProgressBar::Multi.new(output: output, width: WIDTH) : nil
18
+ @cached_bar = progress_bar(cached_title)
19
+ @live_bar = progress_bar(live_title)
20
+ @skipped_bar = skipped_title ? progress_bar(skipped_title) : nil
20
21
  end
21
22
 
22
23
  attr_reader :cached_count, :live_count, :skipped_count
23
24
 
24
25
  def cached
25
26
  @cached_count += 1
26
- @cached_bar&.increment
27
+ @cached_bar&.advance
27
28
  end
28
29
 
29
30
  def live
30
31
  @live_count += 1
31
- @live_bar&.increment
32
+ @live_bar&.advance
32
33
  end
33
34
 
34
35
  def skipped
35
36
  @skipped_count += 1
36
- @skipped_bar&.increment
37
+ @skipped_bar&.advance
38
+ end
39
+
40
+ def stop
41
+ @multibar&.stop
37
42
  end
38
43
 
39
44
  private
40
45
 
41
- def progress_bar(title, output, enabled)
42
- return unless enabled
46
+ def progress_enabled?(enabled, output)
47
+ enabled && @total.positive? && output.respond_to?(:tty?) && output.tty?
48
+ end
49
+
50
+ def progress_bar(title)
51
+ return unless @multibar
43
52
  return unless @total.positive?
44
53
 
45
- ProgressBar.create(title: title, total: @total, format: FORMAT, length: LENGTH, output: output)
54
+ @multibar.register(bar_format(title), total: @total)
55
+ end
56
+
57
+ def bar_format(title)
58
+ Kernel.format(FORMAT, title: title)
46
59
  end
47
60
  end
48
61
  end
@@ -4,8 +4,11 @@ require "json"
4
4
  require "open3"
5
5
  require "optparse"
6
6
  require "pathname"
7
+ require "set"
8
+ require "time"
7
9
 
8
10
  require "psych"
11
+ require "ruby-progressbar"
9
12
  require "kettle/gha/pins"
10
13
  require_relative "cache_progress"
11
14
 
@@ -42,7 +45,13 @@ module Kettle
42
45
  Kettle::Gha::Pins::VersionRubric.specificity(entry)
43
46
  end
44
47
 
45
- def initialize(argv, err: $stderr)
48
+ def initialize(argv, err: $stderr, clock: -> { Time.now })
49
+ cooldown_days = begin
50
+ Integer(ENV.fetch("KETTLE_GHA_PINS_COOLDOWN_DAYS", "0"))
51
+ rescue ArgumentError, TypeError
52
+ 0
53
+ end
54
+ cooldown_days = 0 if cooldown_days.negative?
46
55
  @argv = argv
47
56
  @err = err
48
57
  @options = {
@@ -57,6 +66,8 @@ module Kettle
57
66
  user_agent: "kettle-gha-pins",
58
67
  upgrade: DEFAULT_UPGRADE_LEVEL,
59
68
  cache_path: ENV["KETTLE_GHA_SHA_PINS_CACHE"] || PersistentActionCache.default_path,
69
+ cooldown_days: cooldown_days,
70
+ clock: clock,
60
71
  refresh_cache: false,
61
72
  reject_patterns: Set.new,
62
73
  progress: nil
@@ -88,6 +99,7 @@ module Kettle
88
99
  errors: [],
89
100
  changed_files: [],
90
101
  planned_changes: [],
102
+ cooldown_changes: [],
91
103
  outdated_pins: []
92
104
  }
93
105
 
@@ -135,6 +147,7 @@ module Kettle
135
147
  updates = compute_updates(old_ref, upgrade_plan[:updates][:sha], upgrade_plan[:updates][:reason], repo_ref)
136
148
  updates[:new_version] = upgrade_plan[:updates][:version]
137
149
  updates[:old_version] = upgrade_plan[:current_version]
150
+ updates[:released_at] = upgrade_plan[:updates][:released_at]
138
151
  end
139
152
  if updates.nil? && upgrade_plan[:current_version]
140
153
  comment_version = version_comment_from_line(text, node[:line], node[:col], parsed_ref[:value])
@@ -165,6 +178,22 @@ module Kettle
165
178
 
166
179
  next unless updates
167
180
 
181
+ if (cooldown = cooldown_details(updates))
182
+ state[:cooldown_changes] << {
183
+ path: path,
184
+ line: node[:line] + 1,
185
+ old_ref: old_ref,
186
+ old_version: updates[:old_version],
187
+ new_ref: updates[:new_ref],
188
+ new_version: updates[:new_version],
189
+ reason: updates[:reason],
190
+ released_at: cooldown[:released_at],
191
+ cooldown_until: cooldown[:cooldown_until],
192
+ action: repo_ref
193
+ }
194
+ next
195
+ end
196
+
168
197
  replacement = build_replacement_from_line(text, node[:line], node[:col], parsed_ref[:value], updates[:new_ref], updates[:new_version])
169
198
  unless replacement
170
199
  record_failure(
@@ -224,6 +253,7 @@ module Kettle
224
253
  end
225
254
  end
226
255
  end
256
+ action_progress.stop
227
257
  progress_message("Action resolution checks: #{action_progress.cached_count} cached, #{action_progress.live_count} live.") if action_count.positive?
228
258
 
229
259
  print_report(state)
@@ -257,6 +287,11 @@ module Kettle
257
287
  end
258
288
  @options[:upgrade] = normalized
259
289
  end
290
+ opt.on("--cooldown-days DAYS", Integer, "Warn instead of failing --check for new release upgrades newer than DAYS days (default: #{@options[:cooldown_days]})") do |days|
291
+ Kernel.abort("Invalid --cooldown-days value #{days.inspect}; use a non-negative integer") if days.negative?
292
+
293
+ @options[:cooldown_days] = days
294
+ end
260
295
  opt.on("--token VALUE", "GitHub token to increase API rate-limit") do |token|
261
296
  @options[:token] = token
262
297
  end
@@ -273,9 +308,11 @@ module Kettle
273
308
  @options[:progress] = bool
274
309
  end
275
310
  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}")
311
+ begin
312
+ @options[:reject_patterns] << Regexp.new(pattern)
313
+ rescue RegexpError => e
314
+ Kernel.abort("Invalid --skip-pattern #{pattern.inspect}: #{e.message}")
315
+ end
279
316
  end
280
317
  opt.on("--[no-]validate", "Validate YAML after editing") do |bool|
281
318
  @options[:validate] = bool
@@ -297,25 +334,27 @@ module Kettle
297
334
  def load_workflows(paths, state)
298
335
  file_progress = progress_bar(title: "Files", total: paths.length)
299
336
  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
337
+ begin
338
+ state[:files_scanned] += 1
339
+ begin
340
+ text = File.read(path)
341
+ rescue Errno::EACCES => e
342
+ record_failure(state, path: path, error: "read_error: #{e.message}")
343
+ next
344
+ end
307
345
 
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
346
+ begin
347
+ parsed = Psych.parse_stream(text)
348
+ rescue Psych::Exception => e
349
+ record_failure(state, path: path, error: "yaml_parse_error: #{e.message}")
350
+ next
351
+ end
314
352
 
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
353
+ uses_nodes = extract_uses_nodes(parsed, text)
354
+ workflows << {path: path, text: text, uses_nodes: uses_nodes} unless uses_nodes.empty?
355
+ ensure
356
+ file_progress&.increment
357
+ end
319
358
  end
320
359
  end
321
360
 
@@ -328,7 +367,7 @@ module Kettle
328
367
  old_ref: old_ref,
329
368
  upgrade_level: @options[:upgrade]
330
369
  )
331
- if cached
370
+ if cached || (client.respond_to?(:last_versions_cache_hit) && client.last_versions_cache_hit)
332
371
  progress.cached
333
372
  else
334
373
  progress.live
@@ -469,7 +508,7 @@ module Kettle
469
508
  action: {
470
509
  owner: parts[0],
471
510
  repo: parts[1],
472
- path: (parts.length > 2) ? parts[2..].join("/") : nil,
511
+ path: (parts.length > 2) ? parts[2..-1].join("/") : nil,
473
512
  ref: ref
474
513
  }
475
514
  }
@@ -536,6 +575,31 @@ module Kettle
536
575
  }
537
576
  end
538
577
 
578
+ def cooldown_details(updates)
579
+ return nil unless @options[:check]
580
+ return nil unless @options[:cooldown_days].positive?
581
+ return nil unless updates[:reason] == UPGRADE_REASON
582
+
583
+ released_at = parse_time(updates[:released_at])
584
+ return nil unless released_at
585
+
586
+ cooldown_until = released_at + (@options[:cooldown_days] * 24 * 60 * 60)
587
+ return nil unless @options[:clock].call < cooldown_until
588
+
589
+ {
590
+ released_at: released_at.utc.iso8601,
591
+ cooldown_until: cooldown_until.utc.iso8601
592
+ }
593
+ end
594
+
595
+ def parse_time(value)
596
+ return nil if value.to_s.empty?
597
+
598
+ Time.iso8601(value.to_s)
599
+ rescue ArgumentError
600
+ nil
601
+ end
602
+
539
603
  def extract_scalar_token(raw_text)
540
604
  return nil if raw_text.nil? || raw_text.empty?
541
605
 
@@ -594,14 +658,10 @@ module Kettle
594
658
  line_text = text.lines[line]
595
659
  return nil if line_text.nil?
596
660
 
597
- raw = line_text[col..]
598
- return nil if raw.nil?
599
-
600
- token_info = extract_scalar_token(raw)
661
+ token_info = locate_scalar_token(line_text, col, old_token)
601
662
  return nil unless token_info
602
- return nil unless token_info[:token] == old_token
603
663
 
604
- suffix = raw[token_info[:span]..].to_s
664
+ suffix = line_text[token_info[:end]..-1].to_s
605
665
  match = suffix.match(VERSION_COMMENT_SUFFIX_RE)
606
666
  match && match[:version]
607
667
  end
@@ -610,20 +670,16 @@ module Kettle
610
670
  line_text = text.lines[line]
611
671
  return nil if line_text.nil?
612
672
 
613
- raw = line_text[col..]
614
- return nil if raw.nil?
615
-
616
- token_info = extract_scalar_token(raw)
673
+ token_info = locate_scalar_token(line_text, col, old_token)
617
674
  return nil unless token_info
618
- return nil unless token_info[:token] == old_token
619
675
 
620
676
  rendered = render_replacement(old_token, new_ref, token_info[:quote])
621
677
  return nil unless rendered
622
678
 
623
- span = token_info[:span]
679
+ span = token_info[:end] - token_info[:start]
624
680
  new_scalar = rendered[:quoted]
625
681
  if new_version && token_info[:quote] == :plain
626
- suffix = raw[span..].to_s
682
+ suffix = line_text[token_info[:end]..-1].to_s
627
683
  comment = suffix.match(VERSION_COMMENT_REPLACEMENT_RE)
628
684
  if comment
629
685
  span += comment[0].length
@@ -632,14 +688,31 @@ module Kettle
632
688
  end
633
689
 
634
690
  {
635
- start: col,
636
- end: col + span,
691
+ start: token_info[:start],
692
+ end: token_info[:start] + span,
637
693
  new_scalar: new_scalar,
638
694
  new_ref: new_ref,
639
695
  old_token: old_token
640
696
  }
641
697
  end
642
698
 
699
+ def locate_scalar_token(line_text, col, old_token)
700
+ return nil if col.nil? || col.negative? || col >= line_text.length
701
+
702
+ matches = []
703
+ 0.upto(line_text.length - 1) do |index|
704
+ token_info = extract_scalar_token(line_text[index..-1])
705
+ next unless token_info
706
+ next unless token_info[:token] == old_token
707
+
708
+ matches << token_info.merge(
709
+ start: index,
710
+ end: index + token_info[:span]
711
+ )
712
+ end
713
+ matches.min_by { |match| [(match[:start] - col).abs, match[:start]] }
714
+ end
715
+
643
716
  def apply_edits(original_text, edits)
644
717
  lines = original_text.lines
645
718
  grouped = edits.group_by { |entry| entry[:line] }
@@ -652,7 +725,7 @@ module Kettle
652
725
  next if line.nil?
653
726
 
654
727
  entries.each do |entry|
655
- line = line[0...entry[:start]].to_s + entry[:new_scalar] + line[entry[:end]..].to_s
728
+ line = line[0...entry[:start]].to_s + entry[:new_scalar] + line[entry[:end]..-1].to_s
656
729
  end
657
730
  updated[line_num] = line
658
731
  end
@@ -681,6 +754,7 @@ module Kettle
681
754
 
682
755
  def print_report(state)
683
756
  mode = @options[:write] ? "write" : "dry-run"
757
+ cooldown_changes = state.fetch(:cooldown_changes, [])
684
758
  if @options[:json]
685
759
  payload = {
686
760
  mode: mode,
@@ -692,6 +766,7 @@ module Kettle
692
766
  failures: state[:failures],
693
767
  outdated_pins: state[:outdated_pins],
694
768
  changed_files: state[:changed_files].sort,
769
+ cooldown_changes: cooldown_changes.sort_by { |c| [c[:path], c[:line], c[:new_ref]] },
695
770
  planned_changes: state[:planned_changes].sort_by { |c| [c[:path], c[:line], c[:new_ref]] },
696
771
  errors: state[:errors]
697
772
  }
@@ -708,6 +783,7 @@ module Kettle
708
783
  lines << " changed_files: #{state[:changed_files].length}"
709
784
  lines << " planned_updates: #{state[:updates]}"
710
785
  lines << " outdated_pins: #{state[:outdated_pins].length}"
786
+ lines << " cooldown_warnings: #{cooldown_changes.length}"
711
787
  lines << " failures: #{state[:failures]}"
712
788
  lines << ""
713
789
 
@@ -735,6 +811,18 @@ module Kettle
735
811
  lines << ""
736
812
  end
737
813
 
814
+ if cooldown_changes.any?
815
+ lines << "Cooldown warnings (#{cooldown_changes.length}):"
816
+ lines << "Action Current Latest Location Reason CooldownUntil"
817
+ cooldown_changes.sort_by { |c| [c[:action], c[:path], c[:line]] }.each do |change|
818
+ current = change[:old_version] || change[:old_ref]
819
+ latest = change[:new_version] || change[:new_ref]
820
+ location = "#{change[:path]}:#{change[:line]}"
821
+ lines << "#{change[:action]} #{current} #{latest} #{location} #{change[:reason]} #{change[:cooldown_until]}"
822
+ end
823
+ lines << ""
824
+ end
825
+
738
826
  if state[:planned_changes].empty?
739
827
  lines << "Outdated actions: none"
740
828
  else
@@ -21,16 +21,24 @@ module Kettle
21
21
  @refresh_timeout = refresh_timeout
22
22
  @commit_cache = {}
23
23
  @release_cache = {}
24
+ @last_versions_cache_hit = false
24
25
  end
25
26
 
27
+ attr_reader :last_versions_cache_hit
28
+
26
29
  def versions_for_repo(repo_ref)
30
+ @last_versions_cache_hit = false
27
31
  return [] if repo_ref.to_s.empty?
28
- return @release_cache[repo_ref] if @release_cache.key?(repo_ref)
32
+ if @release_cache.key?(repo_ref)
33
+ @last_versions_cache_hit = true
34
+ return @release_cache[repo_ref]
35
+ end
29
36
 
30
37
  stale = nil
31
38
  unless @refresh_cache
32
39
  cached = @persistent_cache&.versions_for_repo(repo_ref, fresh: true)
33
40
  if cached
41
+ @last_versions_cache_hit = true
34
42
  @release_cache[repo_ref] = cached
35
43
  return cached
36
44
  end
@@ -91,23 +99,28 @@ module Kettle
91
99
 
92
100
  def cached_versions(repo_ref, stale)
93
101
  versions = stale || []
102
+ @last_versions_cache_hit = !stale.nil?
94
103
  @release_cache[repo_ref] = versions
95
104
  versions
96
105
  end
97
106
 
98
107
  def build_release_versions(data, tag_shas)
99
- release_tags = data.filter_map do |release|
108
+ release_dates = {}
109
+ release_tags = data.each_with_object([]) do |release, memo|
100
110
  next unless release.is_a?(Hash)
101
111
 
102
112
  tag = release["tag_name"].to_s
103
113
  next unless VersionRubric.parse(tag)
104
114
 
105
- tag
115
+ release_dates[tag] = release["published_at"].to_s
116
+ release_dates[tag] = release["created_at"].to_s if release_dates[tag].empty?
117
+ memo << tag
106
118
  end
107
119
 
108
120
  VersionRubric.build_release_versions(
109
121
  release_tags: release_tags,
110
- tag_shas: tag_shas
122
+ tag_shas: tag_shas,
123
+ release_dates: release_dates
111
124
  )
112
125
  end
113
126
 
@@ -9,7 +9,7 @@ module Kettle
9
9
  module Pins
10
10
  # Persistent cache of GitHub Action release versions and target SHAs.
11
11
  class PersistentActionCache
12
- VERSION = 2
12
+ VERSION = 3
13
13
 
14
14
  def self.default_path
15
15
  state_home = ENV["XDG_STATE_HOME"]
@@ -41,8 +41,11 @@ 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) }
45
- .sort_by { |entry| entry[:version_obj] }
44
+ entries.each_with_object([]) do |entry, memo|
45
+ deserialized = deserialize_version_entry(entry)
46
+ memo << deserialized if deserialized
47
+ end
48
+ .sort_by { |entry| VersionRubric.sort_key(entry) }
46
49
  .reverse
47
50
  end
48
51
 
@@ -52,18 +55,23 @@ module Kettle
52
55
 
53
56
  action = data.fetch("actions")[repo_ref] ||= {}
54
57
  stored_versions = action["versions"] ||= {}
58
+ refs = action["refs"] ||= {}
55
59
  timestamp = @clock.call.utc.iso8601
56
60
 
57
61
  versions.each do |entry|
58
62
  version = entry[:version].to_s
59
63
  next if version.empty?
64
+ tag = entry[:tag].to_s
65
+ sha = entry[:sha].to_s
60
66
 
61
67
  stored_versions[version] = {
62
- "tag" => entry[:tag].to_s,
68
+ "tag" => tag,
63
69
  "version" => version,
64
- "sha" => entry[:sha].to_s,
70
+ "sha" => sha,
71
+ "released_at" => entry[:released_at].to_s,
65
72
  "cached_at" => timestamp
66
73
  }
74
+ refs[tag] = {"sha" => sha[0, 40], "cached_at" => timestamp} unless tag.empty? || sha.empty?
67
75
  end
68
76
 
69
77
  action["targets"] = target_cache(stored_versions.values)
@@ -142,16 +150,17 @@ module Kettle
142
150
  tag: entry["tag"].to_s,
143
151
  version_obj: parsed,
144
152
  version: version,
145
- sha: entry["sha"].to_s
153
+ sha: entry["sha"].to_s,
154
+ released_at: entry["released_at"].to_s
146
155
  }
147
156
  end
148
157
 
149
158
  def target_cache(version_entries)
150
- entries = version_entries.filter_map do |entry|
159
+ entries = version_entries.each_with_object([]) do |entry, memo|
151
160
  deserialized = deserialize_version_entry(entry)
152
161
  next unless deserialized
153
162
 
154
- deserialized.merge(cached_at: entry["cached_at"].to_s)
163
+ memo << deserialized.merge(cached_at: entry["cached_at"].to_s)
155
164
  end
156
165
  return {} if entries.empty?
157
166
 
@@ -170,6 +179,7 @@ module Kettle
170
179
  "tag" => entry[:tag],
171
180
  "version" => entry[:version],
172
181
  "sha" => entry[:sha],
182
+ "released_at" => entry[:released_at],
173
183
  "cached_at" => entry[:cached_at]
174
184
  }
175
185
  end
@@ -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.3.0"
8
8
  end
9
9
  VERSION = Version::VERSION # Traditional Constant Location
10
10
  end
@@ -6,7 +6,7 @@ module Kettle
6
6
  # Shared version ordering and upgrade selection rules for GitHub Action pins.
7
7
  module VersionRubric
8
8
  VALID_UPGRADE_LEVELS = %w[major minor patch].freeze
9
- DEFAULT_UPGRADE_LEVEL = "patch"
9
+ DEFAULT_UPGRADE_LEVEL = "major"
10
10
  RELEASE_TAG_RE = /\A(?:\d+|\d+\.\d+\.\d+(?:[-.]?[0-9A-Za-z.-]+)?)\z/
11
11
 
12
12
  module_function
@@ -24,7 +24,7 @@ module Kettle
24
24
  value.to_s.match?(/\A\d+\z/)
25
25
  end
26
26
 
27
- def entry(tag:, sha: nil)
27
+ def entry(tag:, sha: nil, released_at: nil)
28
28
  version_obj = parse(tag)
29
29
  return nil unless version_obj
30
30
 
@@ -32,17 +32,22 @@ module Kettle
32
32
  tag: tag.to_s,
33
33
  version_obj: version_obj,
34
34
  version: version_obj.to_s,
35
- sha: sha
35
+ sha: sha,
36
+ released_at: released_at.to_s
36
37
  }
37
38
  end
38
39
 
39
- def build_release_versions(release_tags:, tag_shas:)
40
+ def build_release_versions(release_tags:, tag_shas:, release_dates: {})
40
41
  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|
42
+ releases = release_tags.each_with_object([]) do |tag, memo|
43
+ release_entry = entry(tag: tag, sha: tag_shas[tag.to_s], released_at: release_dates[tag.to_s])
44
+ memo << release_entry if release_entry
45
+ end
46
+ tag_versions = tag_shas.each_with_object([]) do |(tag, sha), memo|
43
47
  next if released_tags[tag.to_s]
44
48
 
45
- entry(tag: tag, sha: sha)
49
+ tag_entry = entry(tag: tag, sha: sha)
50
+ memo << tag_entry if tag_entry
46
51
  end
47
52
 
48
53
  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.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '1.13'
54
+ - !ruby/object:Gem::Dependency
55
+ name: tty-progressbar
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.18'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.18'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: version_gem
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -168,7 +182,7 @@ dependencies:
168
182
  version: '2.0'
169
183
  - - ">="
170
184
  - !ruby/object:Gem::Version
171
- version: 2.0.12
185
+ version: 2.0.14
172
186
  type: :development
173
187
  prerelease: false
174
188
  version_requirements: !ruby/object:Gem::Requirement
@@ -178,7 +192,7 @@ dependencies:
178
192
  version: '2.0'
179
193
  - - ">="
180
194
  - !ruby/object:Gem::Version
181
- version: 2.0.12
195
+ version: 2.0.14
182
196
  - !ruby/object:Gem::Dependency
183
197
  name: turbo_tests2
184
198
  requirement: !ruby/object:Gem::Requirement
@@ -199,20 +213,6 @@ dependencies:
199
213
  - - ">="
200
214
  - !ruby/object:Gem::Version
201
215
  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
216
  - !ruby/object:Gem::Dependency
217
217
  name: stone_checksums
218
218
  requirement: !ruby/object:Gem::Requirement
@@ -281,10 +281,10 @@ licenses:
281
281
  - MIT
282
282
  metadata:
283
283
  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
284
+ source_code_uri: https://github.com/kettle-dev/kettle-gha-pins/tree/v0.3.0
285
+ changelog_uri: https://github.com/kettle-dev/kettle-gha-pins/blob/v0.3.0/CHANGELOG.md
286
286
  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
287
+ documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.3.0
288
288
  funding_uri: https://github.com/sponsors/pboling
289
289
  wiki_uri: https://github.com/kettle-dev/kettle-gha-pins/wiki
290
290
  news_uri: https://www.railsbling.com/tags/kettle-gha-pins
@@ -307,7 +307,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
307
307
  requirements:
308
308
  - - ">="
309
309
  - !ruby/object:Gem::Version
310
- version: 3.2.0
310
+ version: 2.4.0
311
311
  required_rubygems_version: !ruby/object:Gem::Requirement
312
312
  requirements:
313
313
  - - ">="
metadata.gz.sig CHANGED
Binary file