downloadable: skip repeated checksum verification of the same file

Multiple download objects can reference the same cached file within one
process (the concurrent prefetch queue, the installer's download and the
internal-API synthetic cask download), so a cask upgrade SHA-256 hashes
each download up to three times and prints duplicated "Verifying
checksum" lines under --verbose. Remember successful verifications for
the process, keyed on path, checksum, size and modification time so a
re-downloaded file is verified afresh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Anthony Holland
2026-07-15 09:11:26 +12:00
co-authored by Claude Fable 5
parent 38acf7eeda
commit 7d546e7314
2 changed files with 66 additions and 3 deletions
+49 -2
View File
@@ -14,6 +14,48 @@ module Downloadable
abstract!
requires_ancestor { Kernel }
class << self
sig { params(filename: Pathname, checksum: T.nilable(Checksum)).returns(T::Boolean) }
def already_verified?(filename, checksum)
key = verification_key(filename, checksum)
return false if key.nil?
verification_lock.synchronize { verified_downloads.include?(key) }
end
sig { params(filename: Pathname, checksum: T.nilable(Checksum)).void }
def record_verification(filename, checksum)
key = verification_key(filename, checksum)
return if key.nil?
verification_lock.synchronize { verified_downloads.add(key) }
end
private
# The size and modification time ensure a file downloaded again to the
# same path (e.g. after `--force` cleared the cache) is verified again.
sig { params(filename: Pathname, checksum: T.nilable(Checksum)).returns(T.nilable(String)) }
def verification_key(filename, checksum)
return if checksum.nil?
stat = filename.stat
"#{filename.expand_path}|#{checksum.hexdigest}|#{stat.size}|#{stat.mtime.to_f}"
rescue SystemCallError
nil
end
sig { returns(T::Set[String]) }
def verified_downloads
@verified_downloads ||= T.let(Set.new, T.nilable(T::Set[String]))
end
sig { returns(Mutex) }
def verification_lock
@verification_lock ||= T.let(Mutex.new, T.nilable(Mutex))
end
end
sig { overridable.returns(T.nilable(T.any(String, URL))) }
attr_reader :url
@@ -181,8 +223,13 @@ module Downloadable
verifying!
if filename.file?
ohai "Verifying checksum for '#{filename.basename}'" if verbose?
filename.verify_checksum(checksum)
if Downloadable.already_verified?(filename, checksum)
odebug "Skipping checksum verification for '#{filename.basename}' (already verified in this run)"
else
ohai "Verifying checksum for '#{filename.basename}'" if verbose?
filename.verify_checksum(checksum)
Downloadable.record_verification(filename, checksum)
end
verified!
end
rescue ChecksumMissingError
+17 -1
View File
@@ -301,6 +301,21 @@ RSpec.describe Resource do
end
end
specify "#verify_download_integrity skips files already verified in this process" do
fn = mktmpdir/"test.tar.gz"
fn.write "content"
digest = Digest::SHA256.hexdigest("content")
resource.sha256(digest)
other_resource = described_class.new("other")
other_resource.sha256(digest)
expect(fn).to receive(:verify_checksum).once.and_call_original
resource.verify_download_integrity(fn)
other_resource.verify_download_integrity(fn)
end
specify "#verify_download_integrity_missing" do
fn = Pathname.new("test")
@@ -312,7 +327,8 @@ RSpec.describe Resource do
end
specify "#verify_download_integrity_mismatch" do
fn = instance_double(Pathname, file?: true, basename: "foo")
fn = Pathname.new("foo")
allow(fn).to receive(:file?).and_return(true)
checksum = resource.sha256(TEST_SHA256)
expect(fn).to receive(:verify_checksum)