brew/Library/Homebrew/downloadable.rb

165 lines
4.0 KiB
Ruby
Raw Normal View History

# typed: strict
# frozen_string_literal: true
require "url"
require "checksum"
require "download_strategy"
2024-07-14 21:03:08 -04:00
module Downloadable
include Context
extend T::Helpers
abstract!
2024-10-06 09:25:57 -07:00
requires_ancestor { Kernel }
sig { overridable.returns(T.any(NilClass, String, URL)) }
attr_reader :url
2024-07-14 22:51:54 -04:00
sig { overridable.returns(T.nilable(Checksum)) }
attr_reader :checksum
2024-07-14 22:51:54 -04:00
sig { overridable.returns(T::Array[String]) }
attr_reader :mirrors
sig { void }
def initialize
@url = T.let(nil, T.nilable(URL))
@checksum = T.let(nil, T.nilable(Checksum))
@mirrors = T.let([], T::Array[String])
@version = T.let(nil, T.nilable(Version))
@download_strategy = T.let(nil, T.nilable(T::Class[AbstractDownloadStrategy]))
@downloader = T.let(nil, T.nilable(AbstractDownloadStrategy))
@download_name = T.let(nil, T.nilable(String))
end
sig { params(other: Object).void }
def initialize_dup(other)
super
@checksum = @checksum.dup
@mirrors = @mirrors.dup
@version = @version.dup
end
2024-07-14 22:51:54 -04:00
sig { overridable.returns(T.self_type) }
def freeze
@checksum.freeze
@mirrors.freeze
@version.freeze
super
end
2024-07-14 21:03:08 -04:00
sig { abstract.returns(String) }
def name; end
2024-07-14 11:42:22 -04:00
sig { returns(String) }
def download_type
class_name = T.let(self.class, T::Class[Downloadable]).name&.split("::")&.last
T.must(class_name).gsub(/([[:lower:]])([[:upper:]])/, '\1 \2').downcase
2024-07-14 11:42:22 -04:00
end
2024-07-14 22:51:54 -04:00
sig(:final) { returns(T::Boolean) }
def downloaded?
cached_download.exist?
end
2024-07-14 22:51:54 -04:00
sig { overridable.returns(Pathname) }
def cached_download
downloader.cached_location
end
2024-07-14 22:51:54 -04:00
sig { overridable.void }
def clear_cache
downloader.clear_cache
end
2024-07-14 22:51:54 -04:00
sig { overridable.returns(T.nilable(Version)) }
def version
return @version if @version && !@version.null?
version = determine_url&.version
version unless version&.null?
end
sig { overridable.returns(T::Class[AbstractDownloadStrategy]) }
def download_strategy
@download_strategy ||= T.must(determine_url).download_strategy
end
2024-07-14 21:03:08 -04:00
sig { overridable.returns(AbstractDownloadStrategy) }
def downloader
@downloader ||= begin
primary_url, *mirrors = determine_url_mirrors
2024-07-14 21:03:08 -04:00
raise ArgumentError, "attempted to use a `Downloadable` without a URL!" if primary_url.blank?
download_strategy.new(primary_url, download_name, version,
2024-03-07 16:20:20 +00:00
mirrors:, cache:, **T.must(@url).specs)
end
end
2024-07-14 11:42:22 -04:00
sig {
2024-07-14 21:03:08 -04:00
overridable.params(
2024-07-14 11:42:22 -04:00
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)
cache.mkpath
begin
2024-07-14 11:42:22 -04:00
downloader.quiet! if quiet
2024-03-07 16:20:20 +00:00
downloader.fetch(timeout:)
rescue ErrorDuringExecution, CurlDownloadStrategyError => e
raise DownloadError.new(self, e)
end
download = cached_download
verify_download_integrity(download) if verify_download_integrity
download
end
2024-07-14 21:03:08 -04:00
sig { overridable.params(filename: Pathname).void }
def verify_download_integrity(filename)
if filename.file?
ohai "Verifying checksum for '#{filename.basename}'" if verbose?
filename.verify_checksum(checksum)
end
rescue ChecksumMissingError
return if silence_checksum_missing_error?
opoo <<~EOS
Cannot verify integrity of '#{filename.basename}'.
No checksum was provided.
For your reference, the checksum is:
sha256 "#{filename.sha256}"
EOS
end
sig { overridable.returns(String) }
def download_name
@download_name ||= File.basename(determine_url.to_s).freeze
end
2023-05-02 02:09:53 +01:00
private
sig { overridable.returns(T::Boolean) }
def silence_checksum_missing_error?
false
end
sig { overridable.returns(T.nilable(URL)) }
def determine_url
@url
end
sig { overridable.returns(T::Array[String]) }
def determine_url_mirrors
[determine_url.to_s, *mirrors].uniq
end
sig { overridable.returns(Pathname) }
def cache
HOMEBREW_CACHE
end
end