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



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 81

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



107
108
109
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 107

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



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/kettle/gha/pins/persistent_action_cache.rb', line 94

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
# 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] ||= {}
  stored_versions = action["versions"] ||= {}
  refs = action["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