220 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
		
		
			
		
	
	
			220 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| 
								 | 
							
								# typed: true # rubocop:todo Sorbet/StrictSigil
							 | 
						||
| 
								 | 
							
								# frozen_string_literal: true
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Bottle
							 | 
						||
| 
								 | 
							
								  include Downloadable
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  class Filename
							 | 
						||
| 
								 | 
							
								    attr_reader :name, :version, :tag, :rebuild
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sig { params(formula: Formula, tag: Utils::Bottles::Tag, rebuild: Integer).returns(T.attached_class) }
							 | 
						||
| 
								 | 
							
								    def self.create(formula, tag, rebuild)
							 | 
						||
| 
								 | 
							
								      new(formula.name, formula.pkg_version, tag, rebuild)
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sig { params(name: String, version: PkgVersion, tag: Utils::Bottles::Tag, rebuild: Integer).void }
							 | 
						||
| 
								 | 
							
								    def initialize(name, version, tag, rebuild)
							 | 
						||
| 
								 | 
							
								      @name = File.basename name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      raise ArgumentError, "Invalid bottle name" unless Utils.safe_filename?(@name)
							 | 
						||
| 
								 | 
							
								      raise ArgumentError, "Invalid bottle version" unless Utils.safe_filename?(version.to_s)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      @version = version
							 | 
						||
| 
								 | 
							
								      @tag = tag.to_unstandardized_sym.to_s
							 | 
						||
| 
								 | 
							
								      @rebuild = rebuild
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sig { returns(String) }
							 | 
						||
| 
								 | 
							
								    def to_str
							 | 
						||
| 
								 | 
							
								      "#{name}--#{version}#{extname}"
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sig { returns(String) }
							 | 
						||
| 
								 | 
							
								    def to_s = to_str
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sig { returns(String) }
							 | 
						||
| 
								 | 
							
								    def json
							 | 
						||
| 
								 | 
							
								      "#{name}--#{version}.#{tag}.bottle.json"
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def url_encode
							 | 
						||
| 
								 | 
							
								      ERB::Util.url_encode("#{name}-#{version}#{extname}")
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def github_packages
							 | 
						||
| 
								 | 
							
								      "#{name}--#{version}#{extname}"
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sig { returns(String) }
							 | 
						||
| 
								 | 
							
								    def extname
							 | 
						||
| 
								 | 
							
								      s = rebuild.positive? ? ".#{rebuild}" : ""
							 | 
						||
| 
								 | 
							
								      ".#{tag}.bottle#{s}.tar.gz"
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  extend Forwardable
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  attr_reader :name, :resource, :tag, :cellar, :rebuild
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def_delegators :resource, :url, :verify_download_integrity
							 | 
						||
| 
								 | 
							
								  def_delegators :resource, :cached_download, :downloader
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def initialize(formula, spec, tag = nil)
							 | 
						||
| 
								 | 
							
								    super()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @name = formula.name
							 | 
						||
| 
								 | 
							
								    @resource = Resource.new
							 | 
						||
| 
								 | 
							
								    @resource.owner = formula
							 | 
						||
| 
								 | 
							
								    @spec = spec
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    tag_spec = spec.tag_specification_for(Utils::Bottles.tag(tag))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @tag = tag_spec.tag
							 | 
						||
| 
								 | 
							
								    @cellar = tag_spec.cellar
							 | 
						||
| 
								 | 
							
								    @rebuild = spec.rebuild
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @resource.version(formula.pkg_version.to_s)
							 | 
						||
| 
								 | 
							
								    @resource.checksum = tag_spec.checksum
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @fetch_tab_retried = false
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    root_url(spec.root_url, spec.root_url_specs)
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sig {
							 | 
						||
| 
								 | 
							
								    override.params(
							 | 
						||
| 
								 | 
							
								      verify_download_integrity: T::Boolean,
							 | 
						||
| 
								 | 
							
								      timeout:                   T.nilable(T.any(Integer, Float)),
							 | 
						||
| 
								 | 
							
								      quiet:                     T.nilable(T::Boolean),
							 | 
						||
| 
								 | 
							
								    ).returns(Pathname)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  def fetch(verify_download_integrity: true, timeout: nil, quiet: false)
							 | 
						||
| 
								 | 
							
								    resource.fetch(verify_download_integrity:, timeout:, quiet:)
							 | 
						||
| 
								 | 
							
								  rescue DownloadError
							 | 
						||
| 
								 | 
							
								    raise unless fallback_on_error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    fetch_tab
							 | 
						||
| 
								 | 
							
								    retry
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sig { override.void }
							 | 
						||
| 
								 | 
							
								  def clear_cache
							 | 
						||
| 
								 | 
							
								    @resource.clear_cache
							 | 
						||
| 
								 | 
							
								    github_packages_manifest_resource&.clear_cache
							 | 
						||
| 
								 | 
							
								    @fetch_tab_retried = false
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def compatible_locations?
							 | 
						||
| 
								 | 
							
								    @spec.compatible_locations?(tag: @tag)
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Does the bottle need to be relocated?
							 | 
						||
| 
								 | 
							
								  def skip_relocation?
							 | 
						||
| 
								 | 
							
								    @spec.skip_relocation?(tag: @tag)
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def stage = downloader.stage
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def fetch_tab(timeout: nil, quiet: false)
							 | 
						||
| 
								 | 
							
								    return unless (resource = github_packages_manifest_resource)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    begin
							 | 
						||
| 
								 | 
							
								      resource.fetch(timeout:, quiet:)
							 | 
						||
| 
								 | 
							
								    rescue DownloadError
							 | 
						||
| 
								 | 
							
								      raise unless fallback_on_error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      retry
							 | 
						||
| 
								 | 
							
								    rescue Resource::BottleManifest::Error
							 | 
						||
| 
								 | 
							
								      raise if @fetch_tab_retried
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      @fetch_tab_retried = true
							 | 
						||
| 
								 | 
							
								      resource.clear_cache
							 | 
						||
| 
								 | 
							
								      retry
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def tab_attributes
							 | 
						||
| 
								 | 
							
								    if (resource = github_packages_manifest_resource) && resource.downloaded?
							 | 
						||
| 
								 | 
							
								      return resource.tab
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    {}
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sig { returns(T.nilable(Integer)) }
							 | 
						||
| 
								 | 
							
								  def bottle_size
							 | 
						||
| 
								 | 
							
								    resource = github_packages_manifest_resource
							 | 
						||
| 
								 | 
							
								    return unless resource&.downloaded?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    resource.bottle_size
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sig { returns(T.nilable(Integer)) }
							 | 
						||
| 
								 | 
							
								  def installed_size
							 | 
						||
| 
								 | 
							
								    resource = github_packages_manifest_resource
							 | 
						||
| 
								 | 
							
								    return unless resource&.downloaded?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    resource.installed_size
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sig { returns(Filename) }
							 | 
						||
| 
								 | 
							
								  def filename
							 | 
						||
| 
								 | 
							
								    Filename.create(resource.owner, @tag, @spec.rebuild)
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sig { returns(T.nilable(Resource::BottleManifest)) }
							 | 
						||
| 
								 | 
							
								  def github_packages_manifest_resource
							 | 
						||
| 
								 | 
							
								    return if @resource.download_strategy != CurlGitHubPackagesDownloadStrategy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @github_packages_manifest_resource ||= begin
							 | 
						||
| 
								 | 
							
								      resource = Resource::BottleManifest.new(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      version_rebuild = GitHubPackages.version_rebuild(@resource.version, rebuild)
							 | 
						||
| 
								 | 
							
								      resource.version(version_rebuild)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      image_name = GitHubPackages.image_formula_name(@name)
							 | 
						||
| 
								 | 
							
								      image_tag = GitHubPackages.image_version_rebuild(version_rebuild)
							 | 
						||
| 
								 | 
							
								      resource.url(
							 | 
						||
| 
								 | 
							
								        "#{root_url}/#{image_name}/manifests/#{image_tag}",
							 | 
						||
| 
								 | 
							
								        using:   CurlGitHubPackagesDownloadStrategy,
							 | 
						||
| 
								 | 
							
								        headers: ["Accept: application/vnd.oci.image.index.v1+json"],
							 | 
						||
| 
								 | 
							
								      )
							 | 
						||
| 
								 | 
							
								      T.cast(resource.downloader, CurlGitHubPackagesDownloadStrategy).resolved_basename =
							 | 
						||
| 
								 | 
							
								        "#{name}-#{version_rebuild}.bottle_manifest.json"
							 | 
						||
| 
								 | 
							
								      resource
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  private
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def select_download_strategy(specs)
							 | 
						||
| 
								 | 
							
								    specs[:using] ||= DownloadStrategyDetector.detect(@root_url)
							 | 
						||
| 
								 | 
							
								    specs[:bottle] = true
							 | 
						||
| 
								 | 
							
								    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
							 |