diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 558a1212b5..643c6a54d5 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -364,7 +364,7 @@ class Formula # @private sig { returns(T.nilable(Bottle)) } def bottle - Bottle.new(self, bottle_specification) if bottled? + @bottle ||= Bottle.new(self, bottle_specification) if bottled? end # The description of the software. @@ -1906,7 +1906,7 @@ class Formula checksum = collector_os[:checksum].hexdigest filename = Bottle::Filename.create(self, os, bottle_spec.rebuild) - path, = bottle_spec.path_resolved_basename(name, checksum, filename) + path, = Utils::Bottles.path_resolved_basename(bottle_spec.root_url, name, checksum, filename) url = "#{bottle_spec.root_url}/#{path}" hash["files"][os] = { diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index bcd4a97ef2..23ccf9afe2 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -174,6 +174,7 @@ class Resource @specs.merge!(specs) @using = @specs.delete(:using) @download_strategy = DownloadStrategyDetector.detect(url, using) + @downloader = nil end def version(val = nil) diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index 8f625597b0..3be1f16e2d 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -309,29 +309,24 @@ class Bottle checksum, tag, cellar = spec.checksum_for(Utils::Bottles.tag) - filename = Filename.create(formula, tag, spec.rebuild) - - path, resolved_basename = spec.path_resolved_basename(@name, checksum, filename) - - @resource.url("#{spec.root_url}/#{path}", select_download_strategy(spec.root_url_specs)) - @resource.version = formula.pkg_version - @resource.checksum = checksum - @resource.downloader.resolved_basename = resolved_basename if resolved_basename.present? @prefix = spec.prefix + @tag = tag @cellar = cellar @rebuild = spec.rebuild + + @resource.version = formula.pkg_version + @resource.checksum = checksum + + root_url(spec.root_url, spec.root_url_specs) end def fetch(verify_download_integrity: true) - # add the default bottle domain as a fallback mirror - if @resource.download_strategy == CurlDownloadStrategy && - @resource.url.start_with?(Homebrew::EnvConfig.bottle_domain) - fallback_url = @resource.url - .sub(/^#{Regexp.escape(Homebrew::EnvConfig.bottle_domain)}/, - HOMEBREW_BOTTLE_DEFAULT_DOMAIN) - @resource.mirror(fallback_url) if [@resource.url, *@resource.mirrors].exclude?(fallback_url) - end @resource.fetch(verify_download_integrity: verify_download_integrity) + rescue DownloadError + raise unless fallback_on_error + + fetch_tab + retry end def clear_cache @@ -355,6 +350,10 @@ class Bottle def fetch_tab # a checksum is used later identifying the correct tab but we do not have the checksum for the manifest/tab github_packages_manifest_resource&.fetch(verify_download_integrity: false) + rescue DownloadError + raise unless fallback_on_error + + retry end def tab_attributes @@ -403,7 +402,7 @@ class Bottle image_name = GitHubPackages.image_formula_name(@name) image_tag = GitHubPackages.image_version_rebuild(version_rebuild) - resource.url("#{@spec.root_url}/#{image_name}/manifests/#{image_tag}", { + resource.url("#{root_url}/#{image_name}/manifests/#{image_tag}", { using: CurlGitHubPackagesDownloadStrategy, headers: ["Accept: application/vnd.oci.image.index.v1+json"], }) @@ -413,9 +412,33 @@ class Bottle end def select_download_strategy(specs) - specs[:using] ||= DownloadStrategyDetector.detect(@spec.root_url) + specs[:using] ||= DownloadStrategyDetector.detect(@root_url) specs end + + def fallback_on_error + # Use the default bottle domain as a fallback mirror + if @resource.url.start_with?(Homebrew::EnvConfig.bottle_domain) && + Homebrew::EnvConfig.bottle_domain != HOMEBREW_BOTTLE_DEFAULT_DOMAIN + opoo "Bottle missing, falling back to the default domain..." + root_url(HOMEBREW_BOTTLE_DEFAULT_DOMAIN) + @github_packages_manifest_resource = nil + true + else + false + end + end + + def root_url(val = nil, specs = {}) + return @root_url if val.nil? + + @root_url = val + + filename = Filename.create(resource.owner, @tag, @spec.rebuild) + path, resolved_basename = Utils::Bottles.path_resolved_basename(val, name, resource.checksum, filename) + @resource.url("#{val}/#{path}", select_download_strategy(specs)) + @resource.downloader.resolved_basename = resolved_basename if resolved_basename.present? + end end class BottleSpecification @@ -453,15 +476,6 @@ class BottleSpecification end end - def path_resolved_basename(name, checksum, filename) - if root_url.match?(GitHubPackages::URL_REGEX) - image_name = GitHubPackages.image_formula_name(name) - ["#{image_name}/blobs/sha256:#{checksum}", filename&.github_packages] - else - filename&.url_encode - end - end - def cellar(val = nil) if val.present? odeprecated( diff --git a/Library/Homebrew/utils/bottles.rb b/Library/Homebrew/utils/bottles.rb index 1262b7161a..4a0d38b0b1 100644 --- a/Library/Homebrew/utils/bottles.rb +++ b/Library/Homebrew/utils/bottles.rb @@ -85,6 +85,15 @@ module Utils contents end + + def path_resolved_basename(root_url, name, checksum, filename) + if root_url.match?(GitHubPackages::URL_REGEX) + image_name = GitHubPackages.image_formula_name(name) + ["#{image_name}/blobs/sha256:#{checksum}", filename&.github_packages] + else + filename&.url_encode + end + end end # Denotes the arch and OS of a bottle.