kettle-gha-pins 0.2.1 → 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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +28 -1
- data/README.md +38 -7
- data/lib/kettle/gha/pins/action_resolver.rb +1 -0
- data/lib/kettle/gha/pins/cache_progress.rb +25 -12
- data/lib/kettle/gha/pins/cli.rb +76 -2
- data/lib/kettle/gha/pins/github_client.rb +15 -2
- data/lib/kettle/gha/pins/persistent_action_cache.rb +12 -5
- data/lib/kettle/gha/pins/version.rb +1 -1
- data/lib/kettle/gha/pins/version_rubric.rb +6 -5
- data.tar.gz.sig +0 -0
- metadata +20 -6
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b35d635c79b4982830fe80cda9f003e3cb5b6ad146fe1f35a2d9826852953ef
|
|
4
|
+
data.tar.gz: 2ca635cc1ee80d6bbb1511d0371b880b199dfe8bf1a0f0265a0ec465d60345c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 623fd4769e4597bff617136531ffbc403c9f8252ba67a7cbf8fa86a7304aa0e3e4d1e20a77020ca07dfdc72207b59c67f11fb4480291a59e163c2389cf8c5f93
|
|
7
|
+
data.tar.gz: 4dec38a332e0ed5a447f585f3d0cf41338aba8e1d6a0af5beb51a5116ceb12bb6ee4b9e43d02979d189b14fc1c1cb3cc3737c09160f931d999b0ed66c3fe01be
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,31 @@ 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
|
+
|
|
33
58
|
## [0.2.1] - 2026-07-22
|
|
34
59
|
|
|
35
60
|
- TAG: [v0.2.1][0.2.1t]
|
|
@@ -91,7 +116,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
91
116
|
- kettle-jem-template-20260720-005 - Generated README Support & Community rows
|
|
92
117
|
now include a RubyForum help badge.
|
|
93
118
|
|
|
94
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.
|
|
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
|
|
95
122
|
[0.2.1]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.2.0...v0.2.1
|
|
96
123
|
[0.2.1t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.2.1
|
|
97
124
|
[0.2.0]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.1.0...v0.2.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
|
|
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] |
|
|
@@ -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 `
|
|
151
|
-
|
|
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.
|
|
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
|
|
@@ -1,48 +1,61 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "tty-progressbar"
|
|
4
4
|
|
|
5
5
|
module Kettle
|
|
6
6
|
module Gha
|
|
7
7
|
module Pins
|
|
8
8
|
class CacheProgress
|
|
9
|
-
FORMAT = "
|
|
10
|
-
|
|
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
|
-
@
|
|
18
|
-
@
|
|
19
|
-
@
|
|
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&.
|
|
27
|
+
@cached_bar&.advance
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def live
|
|
30
31
|
@live_count += 1
|
|
31
|
-
@live_bar&.
|
|
32
|
+
@live_bar&.advance
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def skipped
|
|
35
36
|
@skipped_count += 1
|
|
36
|
-
@skipped_bar&.
|
|
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
|
|
42
|
-
|
|
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
|
-
|
|
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
|
data/lib/kettle/gha/pins/cli.rb
CHANGED
|
@@ -5,8 +5,10 @@ require "open3"
|
|
|
5
5
|
require "optparse"
|
|
6
6
|
require "pathname"
|
|
7
7
|
require "set"
|
|
8
|
+
require "time"
|
|
8
9
|
|
|
9
10
|
require "psych"
|
|
11
|
+
require "ruby-progressbar"
|
|
10
12
|
require "kettle/gha/pins"
|
|
11
13
|
require_relative "cache_progress"
|
|
12
14
|
|
|
@@ -43,7 +45,13 @@ module Kettle
|
|
|
43
45
|
Kettle::Gha::Pins::VersionRubric.specificity(entry)
|
|
44
46
|
end
|
|
45
47
|
|
|
46
|
-
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?
|
|
47
55
|
@argv = argv
|
|
48
56
|
@err = err
|
|
49
57
|
@options = {
|
|
@@ -58,6 +66,8 @@ module Kettle
|
|
|
58
66
|
user_agent: "kettle-gha-pins",
|
|
59
67
|
upgrade: DEFAULT_UPGRADE_LEVEL,
|
|
60
68
|
cache_path: ENV["KETTLE_GHA_SHA_PINS_CACHE"] || PersistentActionCache.default_path,
|
|
69
|
+
cooldown_days: cooldown_days,
|
|
70
|
+
clock: clock,
|
|
61
71
|
refresh_cache: false,
|
|
62
72
|
reject_patterns: Set.new,
|
|
63
73
|
progress: nil
|
|
@@ -89,6 +99,7 @@ module Kettle
|
|
|
89
99
|
errors: [],
|
|
90
100
|
changed_files: [],
|
|
91
101
|
planned_changes: [],
|
|
102
|
+
cooldown_changes: [],
|
|
92
103
|
outdated_pins: []
|
|
93
104
|
}
|
|
94
105
|
|
|
@@ -136,6 +147,7 @@ module Kettle
|
|
|
136
147
|
updates = compute_updates(old_ref, upgrade_plan[:updates][:sha], upgrade_plan[:updates][:reason], repo_ref)
|
|
137
148
|
updates[:new_version] = upgrade_plan[:updates][:version]
|
|
138
149
|
updates[:old_version] = upgrade_plan[:current_version]
|
|
150
|
+
updates[:released_at] = upgrade_plan[:updates][:released_at]
|
|
139
151
|
end
|
|
140
152
|
if updates.nil? && upgrade_plan[:current_version]
|
|
141
153
|
comment_version = version_comment_from_line(text, node[:line], node[:col], parsed_ref[:value])
|
|
@@ -166,6 +178,22 @@ module Kettle
|
|
|
166
178
|
|
|
167
179
|
next unless updates
|
|
168
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
|
+
|
|
169
197
|
replacement = build_replacement_from_line(text, node[:line], node[:col], parsed_ref[:value], updates[:new_ref], updates[:new_version])
|
|
170
198
|
unless replacement
|
|
171
199
|
record_failure(
|
|
@@ -225,6 +253,7 @@ module Kettle
|
|
|
225
253
|
end
|
|
226
254
|
end
|
|
227
255
|
end
|
|
256
|
+
action_progress.stop
|
|
228
257
|
progress_message("Action resolution checks: #{action_progress.cached_count} cached, #{action_progress.live_count} live.") if action_count.positive?
|
|
229
258
|
|
|
230
259
|
print_report(state)
|
|
@@ -258,6 +287,11 @@ module Kettle
|
|
|
258
287
|
end
|
|
259
288
|
@options[:upgrade] = normalized
|
|
260
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
|
|
261
295
|
opt.on("--token VALUE", "GitHub token to increase API rate-limit") do |token|
|
|
262
296
|
@options[:token] = token
|
|
263
297
|
end
|
|
@@ -333,7 +367,7 @@ module Kettle
|
|
|
333
367
|
old_ref: old_ref,
|
|
334
368
|
upgrade_level: @options[:upgrade]
|
|
335
369
|
)
|
|
336
|
-
if cached
|
|
370
|
+
if cached || (client.respond_to?(:last_versions_cache_hit) && client.last_versions_cache_hit)
|
|
337
371
|
progress.cached
|
|
338
372
|
else
|
|
339
373
|
progress.live
|
|
@@ -541,6 +575,31 @@ module Kettle
|
|
|
541
575
|
}
|
|
542
576
|
end
|
|
543
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
|
+
|
|
544
603
|
def extract_scalar_token(raw_text)
|
|
545
604
|
return nil if raw_text.nil? || raw_text.empty?
|
|
546
605
|
|
|
@@ -695,6 +754,7 @@ module Kettle
|
|
|
695
754
|
|
|
696
755
|
def print_report(state)
|
|
697
756
|
mode = @options[:write] ? "write" : "dry-run"
|
|
757
|
+
cooldown_changes = state.fetch(:cooldown_changes, [])
|
|
698
758
|
if @options[:json]
|
|
699
759
|
payload = {
|
|
700
760
|
mode: mode,
|
|
@@ -706,6 +766,7 @@ module Kettle
|
|
|
706
766
|
failures: state[:failures],
|
|
707
767
|
outdated_pins: state[:outdated_pins],
|
|
708
768
|
changed_files: state[:changed_files].sort,
|
|
769
|
+
cooldown_changes: cooldown_changes.sort_by { |c| [c[:path], c[:line], c[:new_ref]] },
|
|
709
770
|
planned_changes: state[:planned_changes].sort_by { |c| [c[:path], c[:line], c[:new_ref]] },
|
|
710
771
|
errors: state[:errors]
|
|
711
772
|
}
|
|
@@ -722,6 +783,7 @@ module Kettle
|
|
|
722
783
|
lines << " changed_files: #{state[:changed_files].length}"
|
|
723
784
|
lines << " planned_updates: #{state[:updates]}"
|
|
724
785
|
lines << " outdated_pins: #{state[:outdated_pins].length}"
|
|
786
|
+
lines << " cooldown_warnings: #{cooldown_changes.length}"
|
|
725
787
|
lines << " failures: #{state[:failures]}"
|
|
726
788
|
lines << ""
|
|
727
789
|
|
|
@@ -749,6 +811,18 @@ module Kettle
|
|
|
749
811
|
lines << ""
|
|
750
812
|
end
|
|
751
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
|
+
|
|
752
826
|
if state[:planned_changes].empty?
|
|
753
827
|
lines << "Outdated actions: none"
|
|
754
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
|
-
|
|
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)
|
|
108
|
+
release_dates = {}
|
|
99
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
|
|
|
115
|
+
release_dates[tag] = release["published_at"].to_s
|
|
116
|
+
release_dates[tag] = release["created_at"].to_s if release_dates[tag].empty?
|
|
105
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 =
|
|
12
|
+
VERSION = 3
|
|
13
13
|
|
|
14
14
|
def self.default_path
|
|
15
15
|
state_home = ENV["XDG_STATE_HOME"]
|
|
@@ -45,7 +45,7 @@ module Kettle
|
|
|
45
45
|
deserialized = deserialize_version_entry(entry)
|
|
46
46
|
memo << deserialized if deserialized
|
|
47
47
|
end
|
|
48
|
-
.sort_by { |entry| entry
|
|
48
|
+
.sort_by { |entry| VersionRubric.sort_key(entry) }
|
|
49
49
|
.reverse
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -55,18 +55,23 @@ module Kettle
|
|
|
55
55
|
|
|
56
56
|
action = data.fetch("actions")[repo_ref] ||= {}
|
|
57
57
|
stored_versions = action["versions"] ||= {}
|
|
58
|
+
refs = action["refs"] ||= {}
|
|
58
59
|
timestamp = @clock.call.utc.iso8601
|
|
59
60
|
|
|
60
61
|
versions.each do |entry|
|
|
61
62
|
version = entry[:version].to_s
|
|
62
63
|
next if version.empty?
|
|
64
|
+
tag = entry[:tag].to_s
|
|
65
|
+
sha = entry[:sha].to_s
|
|
63
66
|
|
|
64
67
|
stored_versions[version] = {
|
|
65
|
-
"tag" =>
|
|
68
|
+
"tag" => tag,
|
|
66
69
|
"version" => version,
|
|
67
|
-
"sha" =>
|
|
70
|
+
"sha" => sha,
|
|
71
|
+
"released_at" => entry[:released_at].to_s,
|
|
68
72
|
"cached_at" => timestamp
|
|
69
73
|
}
|
|
74
|
+
refs[tag] = {"sha" => sha[0, 40], "cached_at" => timestamp} unless tag.empty? || sha.empty?
|
|
70
75
|
end
|
|
71
76
|
|
|
72
77
|
action["targets"] = target_cache(stored_versions.values)
|
|
@@ -145,7 +150,8 @@ module Kettle
|
|
|
145
150
|
tag: entry["tag"].to_s,
|
|
146
151
|
version_obj: parsed,
|
|
147
152
|
version: version,
|
|
148
|
-
sha: entry["sha"].to_s
|
|
153
|
+
sha: entry["sha"].to_s,
|
|
154
|
+
released_at: entry["released_at"].to_s
|
|
149
155
|
}
|
|
150
156
|
end
|
|
151
157
|
|
|
@@ -173,6 +179,7 @@ module Kettle
|
|
|
173
179
|
"tag" => entry[:tag],
|
|
174
180
|
"version" => entry[:version],
|
|
175
181
|
"sha" => entry[:sha],
|
|
182
|
+
"released_at" => entry[:released_at],
|
|
176
183
|
"cached_at" => entry[:cached_at]
|
|
177
184
|
}
|
|
178
185
|
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 = "
|
|
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,14 +32,15 @@ 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
42
|
releases = release_tags.each_with_object([]) do |tag, memo|
|
|
42
|
-
release_entry = entry(tag: tag, sha: tag_shas[tag.to_s])
|
|
43
|
+
release_entry = entry(tag: tag, sha: tag_shas[tag.to_s], released_at: release_dates[tag.to_s])
|
|
43
44
|
memo << release_entry if release_entry
|
|
44
45
|
end
|
|
45
46
|
tag_versions = tag_shas.each_with_object([]) do |(tag, sha), memo|
|
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.
|
|
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.
|
|
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.
|
|
195
|
+
version: 2.0.14
|
|
182
196
|
- !ruby/object:Gem::Dependency
|
|
183
197
|
name: turbo_tests2
|
|
184
198
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -267,10 +281,10 @@ licenses:
|
|
|
267
281
|
- MIT
|
|
268
282
|
metadata:
|
|
269
283
|
homepage_uri: https://github.com/kettle-dev/kettle-gha-pins
|
|
270
|
-
source_code_uri: https://github.com/kettle-dev/kettle-gha-pins/tree/v0.
|
|
271
|
-
changelog_uri: https://github.com/kettle-dev/kettle-gha-pins/blob/v0.
|
|
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
|
|
272
286
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-gha-pins/issues
|
|
273
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.
|
|
287
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.3.0
|
|
274
288
|
funding_uri: https://github.com/sponsors/pboling
|
|
275
289
|
wiki_uri: https://github.com/kettle-dev/kettle-gha-pins/wiki
|
|
276
290
|
news_uri: https://www.railsbling.com/tags/kettle-gha-pins
|
metadata.gz.sig
CHANGED
|
Binary file
|