2025-04-23 03:30:15 +01:00
|
|
|
# typed: strict
|
2024-07-14 11:42:22 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2025-07-22 17:48:32 +01:00
|
|
|
require "bottle"
|
|
|
|
require "api/json_download"
|
2025-08-20 19:20:19 +01:00
|
|
|
require "utils/output"
|
2025-07-22 17:48:32 +01:00
|
|
|
|
2024-07-14 11:42:22 -04:00
|
|
|
module Homebrew
|
2024-07-14 21:03:08 -04:00
|
|
|
class RetryableDownload
|
|
|
|
include Downloadable
|
2025-08-20 19:20:19 +01:00
|
|
|
include Utils::Output::Mixin
|
2024-07-14 21:03:08 -04:00
|
|
|
|
2025-03-26 11:35:26 -07:00
|
|
|
sig { override.returns(T.any(NilClass, String, URL)) }
|
2024-07-14 22:51:54 -04:00
|
|
|
def url = downloadable.url
|
|
|
|
|
|
|
|
sig { override.returns(T.nilable(Checksum)) }
|
|
|
|
def checksum = downloadable.checksum
|
|
|
|
|
|
|
|
sig { override.returns(T::Array[String]) }
|
|
|
|
def mirrors = downloadable.mirrors
|
|
|
|
|
2025-07-22 17:48:32 +01:00
|
|
|
sig { params(downloadable: Downloadable, tries: Integer, pour: T::Boolean).void }
|
|
|
|
def initialize(downloadable, tries:, pour: false)
|
2024-07-14 11:42:22 -04:00
|
|
|
super()
|
|
|
|
|
|
|
|
@downloadable = downloadable
|
2025-04-23 03:30:15 +01:00
|
|
|
@try = T.let(0, Integer)
|
2024-07-14 11:42:22 -04:00
|
|
|
@tries = tries
|
2025-07-22 17:48:32 +01:00
|
|
|
@pour = pour
|
2024-07-14 11:42:22 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
sig { override.returns(String) }
|
2025-08-04 15:51:02 +01:00
|
|
|
def download_queue_name = downloadable.download_queue_name
|
2024-07-14 11:42:22 -04:00
|
|
|
|
|
|
|
sig { override.returns(String) }
|
2025-08-04 15:51:02 +01:00
|
|
|
def download_queue_type = downloadable.download_queue_type
|
2024-07-14 11:42:22 -04:00
|
|
|
|
|
|
|
sig { override.returns(Pathname) }
|
|
|
|
def cached_download = downloadable.cached_download
|
|
|
|
|
2024-07-14 22:51:54 -04:00
|
|
|
sig { override.void }
|
|
|
|
def clear_cache = downloadable.clear_cache
|
|
|
|
|
|
|
|
sig { override.returns(T.nilable(Version)) }
|
|
|
|
def version = downloadable.version
|
|
|
|
|
2025-04-23 03:30:15 +01:00
|
|
|
sig { override.returns(T::Class[AbstractDownloadStrategy]) }
|
2024-07-14 22:51:54 -04:00
|
|
|
def download_strategy = downloadable.download_strategy
|
|
|
|
|
|
|
|
sig { override.returns(AbstractDownloadStrategy) }
|
|
|
|
def downloader = downloadable.downloader
|
|
|
|
|
2024-07-14 11:42:22 -04:00
|
|
|
sig {
|
|
|
|
override.params(
|
|
|
|
verify_download_integrity: T::Boolean,
|
|
|
|
timeout: T.nilable(T.any(Integer, Float)),
|
|
|
|
quiet: T::Boolean,
|
|
|
|
).returns(Pathname)
|
|
|
|
}
|
|
|
|
def fetch(verify_download_integrity: true, timeout: nil, quiet: false)
|
|
|
|
@try += 1
|
|
|
|
|
|
|
|
already_downloaded = downloadable.downloaded?
|
|
|
|
|
2025-08-01 16:56:24 +01:00
|
|
|
download = if downloadable.is_a?(Resource) && (resource = T.cast(downloadable, Resource))
|
|
|
|
resource.fetch(verify_download_integrity: false, timeout:, quiet:, skip_patches: true)
|
|
|
|
else
|
|
|
|
downloadable.fetch(verify_download_integrity: false, timeout:, quiet:)
|
|
|
|
end
|
2024-07-14 11:42:22 -04:00
|
|
|
|
|
|
|
return download unless download.file?
|
|
|
|
|
|
|
|
unless quiet
|
|
|
|
puts "Downloaded to: #{download}" unless already_downloaded
|
2025-08-06 12:07:10 -04:00
|
|
|
puts "SHA-256: #{download.sha256}"
|
2024-07-14 11:42:22 -04:00
|
|
|
end
|
|
|
|
|
2025-07-22 17:48:32 +01:00
|
|
|
json_download = downloadable.is_a?(API::JSONDownload)
|
|
|
|
downloadable.verify_download_integrity(download) if verify_download_integrity && !json_download
|
|
|
|
|
|
|
|
if pour && downloadable.is_a?(Bottle)
|
2025-07-23 17:00:52 +01:00
|
|
|
HOMEBREW_CELLAR.mkpath
|
2025-07-22 17:48:32 +01:00
|
|
|
UnpackStrategy.detect(download, prioritize_extension: true)
|
|
|
|
.extract_nestedly(to: HOMEBREW_CELLAR)
|
|
|
|
elsif json_download
|
|
|
|
FileUtils.touch(download, mtime: Time.now)
|
|
|
|
end
|
2024-07-14 11:42:22 -04:00
|
|
|
|
|
|
|
download
|
2025-07-21 15:07:19 +01:00
|
|
|
rescue DownloadError, ChecksumMismatchError, Resource::BottleManifest::Error
|
2024-07-14 11:42:22 -04:00
|
|
|
tries_remaining = @tries - @try
|
|
|
|
raise if tries_remaining.zero?
|
|
|
|
|
|
|
|
wait = 2 ** @try
|
|
|
|
unless quiet
|
|
|
|
what = Utils.pluralize("tr", tries_remaining, plural: "ies", singular: "y")
|
|
|
|
ohai "Retrying download in #{wait}s... (#{tries_remaining} #{what} left)"
|
|
|
|
end
|
|
|
|
sleep wait
|
|
|
|
|
|
|
|
downloadable.clear_cache
|
|
|
|
retry
|
|
|
|
end
|
2024-07-14 22:51:54 -04:00
|
|
|
|
|
|
|
sig { override.params(filename: Pathname).void }
|
|
|
|
def verify_download_integrity(filename) = downloadable.verify_download_integrity(filename)
|
|
|
|
|
2025-07-17 17:49:53 +01:00
|
|
|
private
|
|
|
|
|
|
|
|
sig { returns(Downloadable) }
|
|
|
|
attr_reader :downloadable
|
2025-07-22 17:48:32 +01:00
|
|
|
|
|
|
|
sig { returns(T::Boolean) }
|
|
|
|
attr_reader :pour
|
2024-07-14 11:42:22 -04:00
|
|
|
end
|
|
|
|
end
|