| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  | # typed: true | 
					
						
							|  |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require "url" | 
					
						
							|  |  |  | require "checksum" | 
					
						
							| 
									
										
										
										
											2024-07-16 21:19:37 +02:00
										 |  |  | require "download_strategy" | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Downloadable | 
					
						
							|  |  |  |   include Context | 
					
						
							|  |  |  |   extend T::Helpers | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   abstract! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(T.nilable(URL)) } | 
					
						
							|  |  |  |   attr_reader :url | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(T.nilable(Checksum)) } | 
					
						
							|  |  |  |   attr_reader :checksum | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(T::Array[String]) } | 
					
						
							|  |  |  |   attr_reader :mirrors | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { void } | 
					
						
							|  |  |  |   def initialize | 
					
						
							|  |  |  |     @mirrors = T.let([], T::Array[String]) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def initialize_dup(other) | 
					
						
							|  |  |  |     super | 
					
						
							|  |  |  |     @checksum = @checksum.dup | 
					
						
							|  |  |  |     @mirrors = @mirrors.dup | 
					
						
							|  |  |  |     @version = @version.dup | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { override.returns(T.self_type) } | 
					
						
							|  |  |  |   def freeze | 
					
						
							|  |  |  |     @checksum.freeze | 
					
						
							|  |  |  |     @mirrors.freeze | 
					
						
							|  |  |  |     @version.freeze | 
					
						
							|  |  |  |     super | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(T::Boolean) } | 
					
						
							|  |  |  |   def downloaded? | 
					
						
							|  |  |  |     cached_download.exist? | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(Pathname) } | 
					
						
							|  |  |  |   def cached_download | 
					
						
							|  |  |  |     downloader.cached_location | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { void } | 
					
						
							|  |  |  |   def clear_cache | 
					
						
							|  |  |  |     downloader.clear_cache | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(T.nilable(Version)) } | 
					
						
							|  |  |  |   def version | 
					
						
							|  |  |  |     return @version if @version && !@version.null? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     version = determine_url&.version | 
					
						
							|  |  |  |     version unless version&.null? | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(T.class_of(AbstractDownloadStrategy)) } | 
					
						
							|  |  |  |   def download_strategy | 
					
						
							|  |  |  |     @download_strategy ||= determine_url&.download_strategy | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { returns(AbstractDownloadStrategy) } | 
					
						
							|  |  |  |   def downloader | 
					
						
							|  |  |  |     @downloader ||= begin | 
					
						
							|  |  |  |       primary_url, *mirrors = determine_url_mirrors | 
					
						
							|  |  |  |       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) | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { params(verify_download_integrity: T::Boolean, timeout: T.nilable(T.any(Integer, Float))).returns(Pathname) } | 
					
						
							|  |  |  |   def fetch(verify_download_integrity: true, timeout: nil) | 
					
						
							|  |  |  |     cache.mkpath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     begin | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |       downloader.fetch(timeout:) | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |     rescue ErrorDuringExecution, CurlDownloadStrategyError => e | 
					
						
							|  |  |  |       raise DownloadError.new(self, e) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     download = cached_download | 
					
						
							|  |  |  |     verify_download_integrity(download) if verify_download_integrity | 
					
						
							|  |  |  |     download | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sig { 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 | 
					
						
							|  |  |  |     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 | 
					
						
							|  |  |  |     File.basename(determine_url.to_s) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-02 02:09:53 +01:00
										 |  |  |   private | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |   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 |