class Registry
Reads RubyGems package bytes and verifies registry propagation of the release attestations.
Nested Classes and Modules
class PendingA registered package is not yet available from the download service.
Definitions
def initialize(http: Net::HTTP)
Configure the HTTP transport used for registry requests.
Signature
-
parameter
httpInterface(:start) The transport providing Net::HTTP-compatible sessions.
Implementation
def initialize(http: Net::HTTP)
@http = http
end
def digest(name, version)
Compute a published package's digest, distinguishing absence from propagation delays.
Signature
-
parameter
nameString The gem name.
-
parameter
versionString The stable gem version.
-
returns
String | Nil The SHA256 digest, or nil if the version is not registered.
-
raises
Pending If the version is registered but its download is absent.
-
raises
RuntimeError If a request fails or a redirect violates the HTTPS policy.
Implementation
def digest(name, version)
# Missing downloads can return 403; use the version API to establish absence:
return nil unless get("https://rubygems.org/api/v2/rubygems/#{name}/versions/#{version}.json?platform=ruby")
body = get("https://rubygems.org/downloads/#{name}-#{version}.gem")
raise Pending, "Published gem download is missing; retry after registry propagation." unless body
return Digest::SHA256.hexdigest(body)
end
def verify(receipt, bundle, attempts: 7, delay: 10)
Wait for the published bytes and attestation to match the retained release.
Signature
-
parameter
receiptHash Symbol-keyed release evidence containing
name,version, andsha256.-
parameter
bundleString The local Sigstore bundle path.
-
parameter
attemptsInteger The maximum number of verification attempts.
-
parameter
delayNumeric Seconds to wait between attempts.
-
returns
Nil When both the bytes and attestation match.
-
raises
RuntimeError If verification fails or registry propagation times out.
Implementation
def verify(receipt, bundle, attempts: 7, delay: 10)
local_bundle = JSON.parse(File.read(bundle))
attempts.times do |attempt|
begin
if remote_digest = digest(receipt.fetch(:name), receipt.fetch(:version))
raise "Published version has different bytes." unless remote_digest == receipt.fetch(:sha256)
if attestations = get("https://rubygems.org/api/v1/attestations/#{receipt.fetch(:name)}-#{receipt.fetch(:version)}.json")
raise "Registry does not contain this artifact's Sigstore bundle." unless contains_bundle?(JSON.parse(attestations), local_bundle)
return
end
end
rescue Pending
# The version API can become visible before the gem download.
end
if attempt < attempts - 1
Console.info(self, "Waiting for RubyGems to serve the release.")
sleep(delay)
end
end
raise "Registry propagation did not complete; rerun this workflow to resume from the retained release."
end