kettle-gha-pins 0.3.9 → 0.3.11

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: 40a359f61e73b1b17ee62829c001c9690844f8ccfb944b776ecb21cbd402b382
4
- data.tar.gz: c5f15b58492474aefb12423903e34e3b4107f8e01dc92d964a0f0ad9c8320408
3
+ metadata.gz: e6eef00386a5fa9f57acdf295331f68f11293cf9d90ef09adbb666aac6a97d0c
4
+ data.tar.gz: 6b433ddcef3d0599c12e8496aa5a80df31f6bcaee358634fb04c167a5c36ac24
5
5
  SHA512:
6
- metadata.gz: 786283e4de7a16524b78e7c30552a8622465d559df4edeb8cbfd1808dda7c3cd7c5ad730c19f5d3c35cc328c11cb9829f0fc056f531e787997961bb442ee97ca
7
- data.tar.gz: c8d901235c8b8ebb5e9aae9fa0918a58965abdd1a61f612a76985df9068ddfa5aaac871e7ace466a2732a0a03821cb3352be232641e692283c4083287b8b4beb
6
+ metadata.gz: 7c6fa524d51f68f4ffb392195d77cb44f782b9ec65e05940915b4ac595e036768a7ed647eca7d7aefbd9da30adebb462fc3e5860f138e783559054257d215982
7
+ data.tar.gz: d7097d6af75ffc23901d1e9fa6208f7704c9f5d12e54aab125438b61088418056ccab1730ad06643945f1fd82052f62d2c315e555769e33535eb7cdd07fbcb50
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,34 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [0.3.11] - 2026-08-10
34
+
35
+ - TAG: [v0.3.11][0.3.11t]
36
+ - COVERAGE: 97.00% -- 1033/1065 lines in 8 files
37
+ - BRANCH COVERAGE: 85.60% -- 416/486 branches in 8 files
38
+ - 25.93% documented
39
+
40
+ ### Fixed
41
+
42
+ - Resolve large GitHub Actions annotated-tag inventories through a single Git exchange so stale cache reviews do not time out on repositories such as codecov/codecov-action.
43
+
44
+ - Replace refreshed GitHub Actions cache inventories instead of retaining obsolete versions and forcing repeated refreshes.
45
+
46
+ ## [0.3.10] - 2026-08-09
47
+
48
+ - TAG: [v0.3.10][0.3.10t]
49
+ - COVERAGE: 98.08% -- 1022/1042 lines in 8 files
50
+ - BRANCH COVERAGE: 88.03% -- 412/468 branches in 8 files
51
+ - 26.17% documented
52
+
53
+ ### Added
54
+
55
+ - Add JSON action inventory and cache review modes, strict offline pin resolution, and configurable action metadata TTL handling.
56
+
57
+ ### Fixed
58
+
59
+ - Keep JSON action inventory and review parsing compatible with Ruby 2.5.
60
+
33
61
  ## [0.3.9] - 2026-08-05
34
62
 
35
63
  - TAG: [v0.3.9][0.3.9t]
@@ -282,7 +310,11 @@ Please file a bug if you notice a violation of semantic versioning.
282
310
  - kettle-jem-template-20260720-005 - Generated README Support & Community rows
283
311
  now include a RubyForum help badge.
284
312
 
285
- [Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.9...HEAD
313
+ [Unreleased]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.11...HEAD
314
+ [0.3.11]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.10...v0.3.11
315
+ [0.3.11t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.3.11
316
+ [0.3.10]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.9...v0.3.10
317
+ [0.3.10t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.3.10
286
318
  [0.3.9]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.8...v0.3.9
287
319
  [0.3.9t]: https://github.com/kettle-dev/kettle-gha-pins/releases/tag/v0.3.9
288
320
  [0.3.8]: https://github.com/kettle-dev/kettle-gha-pins/compare/v0.3.7...v0.3.8
data/README.md CHANGED
@@ -155,6 +155,17 @@ cooldown window. The same default can be supplied with
155
155
  `KETTLE_GHA_PINS_COOLDOWN_DAYS`. The default is `0`, so checks remain strict
156
156
  unless a project opts into a warning window.
157
157
 
158
+ The action metadata cache uses a one-day TTL by default. Set `--ttl DAYS` to
159
+ change it; `--ttl 0` forces a refresh. `--offline` trusts the cache without
160
+ checking TTL or contacting GitHub and fails if required repository metadata is
161
+ missing.
162
+
163
+ For family orchestration, `kettle-gha-pins --list --json` emits the action
164
+ repositories used by a project without network access. A separate
165
+ `kettle-gha-pins --review --input PATH` refreshes the repositories listed in
166
+ that JSON and updates the shared cache. Review refreshes metadata independently
167
+ of the selected major/minor/patch upgrade policy.
168
+
158
169
  `Kettle::Gha::Pins::PersistentActionCache.default_path` intentionally preserves
159
170
  the historical `kettle-gha-sha-pins` cache location so command-line tools can
160
171
  share action metadata without coupling to each other.
@@ -188,6 +199,14 @@ plan[:updates]
188
199
  # => {sha: "...", version: "7.0.0", reason: "upgrade_to_allowed_release"}
189
200
  ```
190
201
 
202
+ The family-oriented cache workflow is:
203
+
204
+ ```console
205
+ kettle-gha-pins --list --json
206
+ kettle-gha-pins --review --input pins.json
207
+ kettle-gha-pins --offline --write --upgrade major
208
+ ```
209
+
191
210
  When two version-equivalent tags point at the same SHA, the rubric keeps the
192
211
  more explicit release spelling. For example, `v7` and `v7.0.0` pointing at the
193
212
  same commit canonicalize to `v7.0.0`; if they point at different SHAs, both
@@ -563,7 +582,7 @@ Thanks for RTFM. ☺️
563
582
  [📌gitmoji]: https://gitmoji.dev
564
583
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
565
584
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
566
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.977-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
585
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.065-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
567
586
  [🔐security]: https://github.com/kettle-dev/kettle-gha-pins/blob/main/SECURITY.md
568
587
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
569
588
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -67,9 +67,13 @@ module Kettle
67
67
  user_agent: "kettle-gha-pins",
68
68
  upgrade: DEFAULT_UPGRADE_LEVEL,
69
69
  cache_path: ENV["KETTLE_GHA_SHA_PINS_CACHE"] || PersistentActionCache.default_path,
70
+ ttl_days: DEFAULT_CACHE_TTL_SECONDS / 86_400.0,
70
71
  cooldown_days: cooldown_days,
71
72
  clock: clock,
72
73
  refresh_cache: false,
74
+ offline: false,
75
+ mode: :project,
76
+ input: nil,
73
77
  reject_patterns: Set.new,
74
78
  progress: nil
75
79
  }
@@ -78,20 +82,43 @@ module Kettle
78
82
  def run!
79
83
  parse!
80
84
 
81
- @options[:token] ||= gh_auth_token if @options[:api_base] == API_BASE
85
+ return list_workflows! if @options[:mode] == :list
86
+
87
+ client, persistent_cache = build_client
88
+ return review_cache!(client: client, persistent_cache: persistent_cache) if @options[:mode] == :review
89
+
90
+ run_project!(client: client)
91
+ rescue Error, GitHubClient::CacheMissError, GitHubClient::RefreshError => error
92
+ @err.puts("kettle-gha-pins: #{error.message}")
93
+ 2
94
+ end
95
+
96
+ private
97
+
98
+ def build_client
99
+ @options[:token] ||= gh_auth_token if @options[:api_base] == API_BASE && !@options[:offline]
82
100
  persistent_cache = if @options[:cache_path].to_s.empty?
83
101
  nil
84
102
  else
85
- PersistentActionCache.new(path: @options[:cache_path])
103
+ PersistentActionCache.new(
104
+ path: @options[:cache_path],
105
+ ttl_seconds: (@options[:ttl_days] * 86_400).to_i,
106
+ clock: @options[:clock]
107
+ )
86
108
  end
87
109
  client = GitHubClient.new(
88
110
  token: @options[:token],
89
111
  api_base: @options[:api_base],
90
112
  user_agent: @options[:user_agent],
91
113
  persistent_cache: persistent_cache,
92
- refresh_cache: @options[:refresh_cache]
114
+ refresh_cache: @options[:refresh_cache],
115
+ offline: @options[:offline],
116
+ fail_on_refresh_error: @options[:mode] == :review
93
117
  )
118
+ [client, persistent_cache]
119
+ end
94
120
 
121
+ def run_project!(client:)
95
122
  state = {
96
123
  files_scanned: 0,
97
124
  files_with_changes: 0,
@@ -293,7 +320,79 @@ module Kettle
293
320
  0
294
321
  end
295
322
 
296
- private
323
+ def list_workflows!
324
+ state = {files_scanned: 0, failures: 0, errors: []}
325
+ workflow_files = discover_workflow_files(@options[:root], @options[:reject_patterns])
326
+ workflows = load_workflows(workflow_files, state)
327
+ pins = workflows.flat_map do |workflow|
328
+ workflow.fetch(:uses_nodes).each_with_object([]) do |node, entries|
329
+ parsed = classify_action_ref(node[:value].to_s)
330
+ next unless parsed
331
+
332
+ action = parsed.fetch(:action)
333
+ entries << {
334
+ "repository" => "#{action[:owner]}/#{action[:repo]}",
335
+ "ref" => action[:ref],
336
+ "value" => parsed[:value],
337
+ "path" => workflow[:path],
338
+ "line" => node[:line].to_i + 1
339
+ }
340
+ end
341
+ end
342
+ repositories = pins.map { |pin| pin.fetch("repository") }.uniq.sort
343
+ puts JSON.pretty_generate(
344
+ "schema_version" => 1,
345
+ "root" => @options[:root],
346
+ "repositories" => repositories,
347
+ "pins" => pins
348
+ )
349
+ state[:failures].zero? ? 0 : 2
350
+ end
351
+
352
+ def review_cache!(client:, persistent_cache:)
353
+ raise Error, "--review requires a writable cache; do not use --cache-path \"\"" unless persistent_cache
354
+
355
+ repositories = review_repositories
356
+ refreshed = repositories.map do |repository|
357
+ versions = client.versions_for_repo(repository)
358
+ {
359
+ "repository" => repository,
360
+ "versions" => versions.length,
361
+ "cache" => client.last_versions_cache_hit ? "hit" : "refreshed"
362
+ }
363
+ end
364
+ puts JSON.pretty_generate(
365
+ "schema_version" => 1,
366
+ "mode" => "review",
367
+ "ttl_days" => @options[:ttl_days],
368
+ "repositories" => refreshed
369
+ )
370
+ 0
371
+ end
372
+
373
+ def review_repositories
374
+ source = @options[:input].to_s
375
+ source = if source.empty? || source == "-"
376
+ $stdin.read
377
+ else
378
+ File.read(source)
379
+ end
380
+ payload = JSON.parse(source)
381
+ entries = if payload.is_a?(Array)
382
+ payload
383
+ elsif payload.is_a?(Hash)
384
+ payload["repositories"] || payload["actions"] || payload["pins"] || []
385
+ else
386
+ []
387
+ end
388
+ entries.each_with_object([]) do |entry, repositories|
389
+ value = entry.is_a?(Hash) ? (entry["repository"] || entry["action"] || entry["value"]) : entry
390
+ repository = value.to_s.split("@", 2).first
391
+ repositories << repository if repository.match?(%r{\A[^/\s]+/[^/\s]+\z})
392
+ end.uniq.sort
393
+ rescue JSON::ParserError => error
394
+ raise Error, "invalid review JSON: #{error.message}"
395
+ end
297
396
 
298
397
  def parse!
299
398
  parser = OptionParser.new do |opt|
@@ -328,6 +427,27 @@ module Kettle
328
427
  opt.on("--refresh-cache", "Bypass cached action release data and refresh discovered actions") do
329
428
  @options[:refresh_cache] = true
330
429
  end
430
+ opt.on("--ttl DAYS", Float, "Cache TTL in days (default: #{@options[:ttl_days]})") do |days|
431
+ Kernel.abort("Invalid --ttl value #{days.inspect}; use a non-negative number") if days.negative?
432
+
433
+ @options[:ttl_days] = days
434
+ end
435
+ opt.on("--offline", "Use only the persistent cache; fail on a cache miss") do
436
+ @options[:offline] = true
437
+ end
438
+ opt.on("--list", "List project action pins as JSON without network access") do
439
+ raise OptionParser::InvalidOption, "--list cannot be combined with --review" if @options[:mode] == :review
440
+
441
+ @options[:mode] = :list
442
+ end
443
+ opt.on("--review", "Refresh action metadata from JSON input and update the cache") do
444
+ raise OptionParser::InvalidOption, "--review cannot be combined with --list" if @options[:mode] == :list
445
+
446
+ @options[:mode] = :review
447
+ end
448
+ opt.on("--input PATH", "Review JSON input path, or - for stdin") do |path|
449
+ @options[:input] = path
450
+ end
331
451
  opt.on("--cache-path PATH", "Action release cache path (default: #{@options[:cache_path]})") do |path|
332
452
  @options[:cache_path] = path
333
453
  end
@@ -356,6 +476,9 @@ module Kettle
356
476
  end
357
477
  end
358
478
  parser.parse!(@argv)
479
+ if @options[:mode] == :review && @options[:offline]
480
+ raise OptionParser::InvalidOption, "--review cannot be combined with --offline"
481
+ end
359
482
  end
360
483
 
361
484
  def display_path(path)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "json"
4
4
  require "net/http"
5
+ require "open3"
5
6
  require "timeout"
6
7
  require "uri"
7
8
 
@@ -10,12 +11,18 @@ module Kettle
10
11
  module Pins
11
12
  # Lightweight GitHub API client for commit and release SHA resolution.
12
13
  class GitHubClient
13
- def initialize(token:, api_base:, user_agent:, persistent_cache: nil, refresh_cache: false, open_timeout: DEFAULT_HTTP_OPEN_TIMEOUT_SECONDS, read_timeout: DEFAULT_HTTP_READ_TIMEOUT_SECONDS, refresh_timeout: DEFAULT_HTTP_REFRESH_TIMEOUT_SECONDS)
14
+ ANNOTATED_TAG_GIT_FALLBACK_THRESHOLD = 4
15
+ class CacheMissError < Error; end
16
+ class RefreshError < Error; end
17
+
18
+ def initialize(token:, api_base:, user_agent:, persistent_cache: nil, refresh_cache: false, offline: false, fail_on_refresh_error: false, open_timeout: DEFAULT_HTTP_OPEN_TIMEOUT_SECONDS, read_timeout: DEFAULT_HTTP_READ_TIMEOUT_SECONDS, refresh_timeout: DEFAULT_HTTP_REFRESH_TIMEOUT_SECONDS)
14
19
  @token = token
15
20
  @api_base = api_base
16
21
  @user_agent = user_agent
17
22
  @persistent_cache = persistent_cache
18
23
  @refresh_cache = refresh_cache
24
+ @offline = offline
25
+ @fail_on_refresh_error = fail_on_refresh_error
19
26
  @open_timeout = open_timeout
20
27
  @read_timeout = read_timeout
21
28
  @refresh_timeout = refresh_timeout
@@ -34,15 +41,22 @@ module Kettle
34
41
  return @release_cache[repo_ref]
35
42
  end
36
43
 
44
+ if @offline
45
+ cached = @persistent_cache&.versions_for_repo(repo_ref, fresh: false)
46
+ raise CacheMissError, "offline cache miss for #{repo_ref}" if cached.nil?
47
+
48
+ @last_versions_cache_hit = true
49
+ @release_cache[repo_ref] = cached
50
+ return cached
51
+ end
52
+
37
53
  stale = nil
38
54
  unless @refresh_cache
39
55
  cached = @persistent_cache&.versions_for_repo(repo_ref, fresh: true)
40
56
  if cached
41
- unless cache_requires_refresh?(repo_ref, cached)
42
- @last_versions_cache_hit = true
43
- @release_cache[repo_ref] = cached
44
- return cached
45
- end
57
+ @last_versions_cache_hit = true
58
+ @release_cache[repo_ref] = cached
59
+ return cached
46
60
  end
47
61
  stale = @persistent_cache&.versions_for_repo(repo_ref, fresh: false)
48
62
  end
@@ -50,10 +64,10 @@ module Kettle
50
64
  releases = nil
51
65
  Timeout.timeout(@refresh_timeout) do
52
66
  data = request_json("/repos/#{repo_ref}/releases?per_page=100")
53
- return cached_versions(repo_ref, stale) unless data.is_a?(Array)
67
+ return refresh_failed!(repo_ref, stale, "invalid releases response") unless data.is_a?(Array)
54
68
 
55
69
  tag_shas = tag_ref_shas(repo_ref)
56
- return cached_versions(repo_ref, stale) unless tag_shas
70
+ return refresh_failed!(repo_ref, stale, "invalid tag response") unless tag_shas
57
71
 
58
72
  releases = build_release_versions(data, tag_shas)
59
73
  end
@@ -61,7 +75,7 @@ module Kettle
61
75
  @release_cache[repo_ref] = releases
62
76
  releases
63
77
  rescue Timeout::Error
64
- cached_versions(repo_ref, stale)
78
+ refresh_failed!(repo_ref, stale, "GitHub refresh timed out")
65
79
  end
66
80
 
67
81
  def commit_sha(repo_ref, ref, refresh: false)
@@ -71,13 +85,15 @@ module Kettle
71
85
  return @commit_cache[cache_key] if @commit_cache.key?(cache_key) && !refresh
72
86
 
73
87
  unless @refresh_cache || refresh
74
- cached = @persistent_cache&.ref_sha(repo_ref, ref, fresh: true)
88
+ cached = @persistent_cache&.ref_sha(repo_ref, ref, fresh: !@offline)
75
89
  if cached
76
90
  @commit_cache[cache_key] = cached
77
91
  return cached
78
92
  end
79
93
  end
80
94
 
95
+ raise CacheMissError, "offline cache miss for #{repo_ref}@#{ref}" if @offline
96
+
81
97
  data = request_json("/repos/#{repo_ref}/commits/#{uri_encode(ref)}")
82
98
  sha = if data.is_a?(Hash)
83
99
  data.fetch("sha", "")[0, 40]
@@ -106,24 +122,10 @@ module Kettle
106
122
  versions
107
123
  end
108
124
 
109
- # Release tags can be added or retargeted inside the cache TTL. Probe
110
- # the latest release before trusting a cached inventory, and refresh
111
- # only when that probe identifies a tag or target the cache lacks.
112
- def cache_requires_refresh?(repo_ref, cached)
113
- latest = request_json("/repos/#{repo_ref}/releases/latest")
114
- return false unless latest.is_a?(Hash)
115
-
116
- tag = latest["tag_name"].to_s
117
- return false unless VersionRubric.parse(tag)
118
-
119
- cached_entry = cached.find { |entry| entry[:tag].to_s == tag }
120
- return true unless cached_entry
125
+ def refresh_failed!(repo_ref, stale, message)
126
+ return cached_versions(repo_ref, stale) unless @fail_on_refresh_error
121
127
 
122
- latest_sha = commit_sha(repo_ref, tag, refresh: true)
123
- cached_sha = cached_entry[:sha].to_s
124
- return !latest_sha.to_s.empty? if cached_sha.empty?
125
-
126
- !latest_sha.to_s.empty? && latest_sha != cached_sha
128
+ raise RefreshError, "#{message} for #{repo_ref}"
127
129
  end
128
130
 
129
131
  def build_release_versions(data, tag_shas)
@@ -150,7 +152,7 @@ module Kettle
150
152
  data = request_json("/repos/#{repo_ref}/git/matching-refs/tags/")
151
153
  return nil unless data.is_a?(Array)
152
154
 
153
- data.each_with_object({}) do |entry, memo|
155
+ entries = data.each_with_object([]) do |entry, memo|
154
156
  ref = entry["ref"].to_s
155
157
  next unless ref.start_with?("refs/tags/")
156
158
 
@@ -160,16 +162,51 @@ module Kettle
160
162
  object = entry["object"]
161
163
  next unless object.is_a?(Hash)
162
164
 
163
- sha = object["sha"].to_s[0, 40]
164
- case object["type"]
165
+ memo << [tag, object["type"].to_s, object["sha"].to_s[0, 40]]
166
+ end
167
+ annotated_tag_shas = git_tag_ref_shas(repo_ref) if entries.count { |_, type, _| type == "tag" } > ANNOTATED_TAG_GIT_FALLBACK_THRESHOLD
168
+
169
+ entries.each_with_object({}) do |(tag, type, sha), memo|
170
+ case type
165
171
  when "commit"
166
172
  memo[tag] = sha
167
173
  when "tag"
168
- memo[tag] = annotated_tag_commit_sha(repo_ref, sha)
174
+ memo[tag] = annotated_tag_shas&.fetch(tag, nil) || annotated_tag_commit_sha(repo_ref, sha)
169
175
  end
170
176
  end
171
177
  end
172
178
 
179
+ # The REST API exposes annotated tags as tag objects, requiring one
180
+ # additional request per tag to reach the commit. A single Git
181
+ # protocol exchange returns both tag objects and peeled commit refs,
182
+ # avoiding a refresh timeout for repositories with large tag histories.
183
+ def git_tag_ref_shas(repo_ref)
184
+ return unless @api_base.to_s.sub(%r{/\z}, "") == API_BASE
185
+
186
+ stdout, _stderr, status = Open3.capture3(
187
+ {"GIT_TERMINAL_PROMPT" => "0"},
188
+ "git",
189
+ "ls-remote",
190
+ "--tags",
191
+ "https://github.com/#{repo_ref}.git"
192
+ )
193
+ return unless status.success?
194
+
195
+ stdout.each_line.each_with_object({}) do |line, memo|
196
+ sha, ref = line.split
197
+ next unless SHA_RE.match?(sha.to_s)
198
+ next unless ref.to_s.start_with?("refs/tags/")
199
+
200
+ peeled = ref.end_with?("^{}")
201
+ tag = ref.sub(%r{\Arefs/tags/}, "").sub(/\^\{\}\z/, "")
202
+ next unless VersionRubric.parse(tag)
203
+
204
+ memo[tag] = sha if peeled || !memo.key?(tag)
205
+ end
206
+ rescue IOError, SystemCallError
207
+ nil
208
+ end
209
+
173
210
  def annotated_tag_commit_sha(repo_ref, tag_sha)
174
211
  return nil if tag_sha.to_s.empty?
175
212
 
@@ -54,8 +54,15 @@ module Kettle
54
54
  return if repo_ref.to_s.empty?
55
55
 
56
56
  action = data.fetch("actions")[repo_ref] ||= {}
57
- stored_versions = action["versions"] ||= {}
58
- refs = action["refs"] ||= {}
57
+ previous_versions = action["versions"] || {}
58
+ previous_tags = previous_versions.values.each_with_object([]) do |entry, tags|
59
+ tag = entry["tag"].to_s
60
+ tags << tag unless tag.empty?
61
+ end
62
+ stored_versions = {}
63
+ refs = (action["refs"] || {}).reject { |ref, _entry| previous_tags.include?(ref) }
64
+ action["versions"] = stored_versions
65
+ action["refs"] = refs
59
66
  timestamp = @clock.call.utc.iso8601
60
67
 
61
68
  versions.each do |entry|
@@ -6,7 +6,7 @@ module Kettle
6
6
  # Version namespace for this gem.
7
7
  module Version
8
8
  # Current gem version.
9
- VERSION = "0.3.9"
9
+ VERSION = "0.3.11"
10
10
  end
11
11
  # Current gem version exposed at the traditional constant location.
12
12
  VERSION = Version::VERSION # Traditional Constant Location
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.3.9
4
+ version: 0.3.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -301,10 +301,10 @@ licenses:
301
301
  - MIT
302
302
  metadata:
303
303
  homepage_uri: https://github.com/kettle-dev/kettle-gha-pins
304
- source_code_uri: https://github.com/kettle-dev/kettle-gha-pins/tree/v0.3.9
305
- changelog_uri: https://github.com/kettle-dev/kettle-gha-pins/blob/v0.3.9/CHANGELOG.md
304
+ source_code_uri: https://github.com/kettle-dev/kettle-gha-pins/tree/v0.3.11
305
+ changelog_uri: https://github.com/kettle-dev/kettle-gha-pins/blob/v0.3.11/CHANGELOG.md
306
306
  bug_tracker_uri: https://github.com/kettle-dev/kettle-gha-pins/issues
307
- documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.3.9
307
+ documentation_uri: https://www.rubydoc.info/gems/kettle-gha-pins/0.3.11
308
308
  funding_uri: https://github.com/sponsors/pboling
309
309
  wiki_uri: https://github.com/kettle-dev/kettle-gha-pins/wiki
310
310
  news_uri: https://www.railsbling.com/tags/kettle-gha-pins
metadata.gz.sig CHANGED
Binary file