Class: Kettle::Gha::Pins::PersistentActionCache

Inherits:
Object
  • Object
show all
Defined in:
lib/kettle/gha/pins/persistent_action_cache.rb

Overview

Persistent cache of GitHub Action release versions and target SHAs.

Constant Summary collapse

VERSION =
3

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path:, ttl_seconds: DEFAULT_CACHE_TTL_SECONDS, clock: -> { Time.now }) ⇒ PersistentActionCache

Returns a new instance of PersistentActionCache.



22
23
24
25
26
27
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 22

def initialize(path:, ttl_seconds: DEFAULT_CACHE_TTL_SECONDS, clock: -> { Time.now })
  @path = path
  @ttl_seconds = ttl_seconds
  @clock = clock
  @data = nil
end

Class Method Details

.default_pathObject



14
15
16
17
18
19
20
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 14

def self.default_path
  state_home = ENV["XDG_STATE_HOME"]
  state_home = File.join(Dir.home, ".local", "state") if state_home.to_s.empty?
  File.join(state_home, "kettle-dev", "gha-sha-pins-cache.json")
rescue ArgumentError
  nil
end

Instance Method Details

#ref_sha(repo_ref, ref, fresh: true) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 88

def ref_sha(repo_ref, ref, fresh: true)
  action = action_data(repo_ref)
  return nil unless action

  refs = action.fetch("refs", {})
  entry = refs[ref.to_s]
  return nil unless entry
  return nil if fresh && !fresh_entry?(entry)

  sha = entry["sha"].to_s
  sha.empty? ? nil : sha
end

#to_hObject



114
115
116
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 114

def to_h
  data
end

#versions_for_repo(repo_ref, fresh: true) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 29

def versions_for_repo(repo_ref, fresh: true)
  action = action_data(repo_ref)
  return nil unless action

  versions = action.fetch("versions", {}).values
  return nil if versions.empty?

  entries = if fresh
    versions.select { |entry| fresh_entry?(entry) }
  else
    versions
  end
  return nil if entries.empty?
  return nil if fresh && entries.length != versions.length

  entries.each_with_object([]) do |entry, memo|
    deserialized = deserialize_version_entry(entry)
    memo << deserialized if deserialized
  end
    .sort_by { |entry| VersionRubric.sort_key(entry) }
    .reverse
end

#write_ref_sha(repo_ref, ref, sha) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 101

def write_ref_sha(repo_ref, ref, sha)
  return if @path.to_s.empty?
  return if repo_ref.to_s.empty? || ref.to_s.empty? || sha.to_s.empty?

  action = data.fetch("actions")[repo_ref] ||= {}
  refs = action["refs"] ||= {}
  refs[ref.to_s] = {
    "sha" => sha.to_s[0, 40],
    "cached_at" => @clock.call.utc.iso8601
  }
  save!
end

#write_versions(repo_ref, versions) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 52

def write_versions(repo_ref, versions)
  return if @path.to_s.empty?
  return if repo_ref.to_s.empty?

  action = data.fetch("actions")[repo_ref] ||= {}
  previous_versions = action["versions"] || {}
  previous_tags = previous_versions.values.each_with_object([]) do |entry, tags|
    tag = entry["tag"].to_s
    tags << tag unless tag.empty?
  end
  stored_versions = {}
  refs = (action["refs"] || {}).reject { |ref, _entry| previous_tags.include?(ref) }
  action["versions"] = stored_versions
  action["refs"] = refs
  timestamp = @clock.call.utc.iso8601

  versions.each do |entry|
    version = entry[:version].to_s
    next if version.empty?
    tag = entry[:tag].to_s
    sha = entry[:sha].to_s

    stored_versions[version] = {
      "tag" => tag,
      "version" => version,
      "sha" => sha,
      "released_at" => entry[:released_at].to_s,
      "cached_at" => timestamp
    }
    refs[tag] = {"sha" => sha[0, 40], "cached_at" => timestamp} unless tag.empty? || sha.empty?
  end

  action["targets"] = target_cache(stored_versions.values)
  save!
end