Refactor formula, cask and Ruby source downloads to use shared code
This commit is contained in:
		
							parent
							
								
									7386d4e407
								
							
						
					
					
						commit
						44f058edb5
					
				@ -14,6 +14,7 @@ module Homebrew
 | 
				
			|||||||
    extend Cachable
 | 
					    extend Cachable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HOMEBREW_CACHE_API = (HOMEBREW_CACHE/"api").freeze
 | 
					    HOMEBREW_CACHE_API = (HOMEBREW_CACHE/"api").freeze
 | 
				
			||||||
 | 
					    HOMEBREW_CACHE_API_SOURCE = (HOMEBREW_CACHE/"api-source").freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sig { params(endpoint: String).returns(Hash) }
 | 
					    sig { params(endpoint: String).returns(Hash) }
 | 
				
			||||||
    def self.fetch(endpoint)
 | 
					    def self.fetch(endpoint)
 | 
				
			||||||
@ -114,50 +115,6 @@ module Homebrew
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sig {
 | 
					 | 
				
			||||||
      params(name: String, path: T.any(Pathname, String), git_head: String,
 | 
					 | 
				
			||||||
             sha256: T.nilable(String)).returns(String)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    def self.fetch_homebrew_cask_source(name, path:, git_head:, sha256: nil)
 | 
					 | 
				
			||||||
      # TODO: unify with formula logic (https://github.com/Homebrew/brew/issues/14746)
 | 
					 | 
				
			||||||
      raw_endpoint = "#{git_head}/#{path}"
 | 
					 | 
				
			||||||
      return cache[raw_endpoint] if cache.present? && cache.key?(raw_endpoint)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      # This API sometimes returns random 404s so needs a fallback at formulae.brew.sh.
 | 
					 | 
				
			||||||
      raw_source_url = "https://raw.githubusercontent.com/Homebrew/homebrew-cask/#{raw_endpoint}"
 | 
					 | 
				
			||||||
      api_source_url = "#{HOMEBREW_API_DEFAULT_DOMAIN}/cask-source/#{name}.rb"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      url = raw_source_url
 | 
					 | 
				
			||||||
      output = Utils::Curl.curl_output("--fail", url)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if !output.success? || output.blank?
 | 
					 | 
				
			||||||
        url = api_source_url
 | 
					 | 
				
			||||||
        output = Utils::Curl.curl_output("--fail", url)
 | 
					 | 
				
			||||||
        if !output.success? || output.blank?
 | 
					 | 
				
			||||||
          raise ArgumentError, <<~EOS
 | 
					 | 
				
			||||||
            No valid file found at either of:
 | 
					 | 
				
			||||||
            #{Tty.underline}#{raw_source_url}#{Tty.reset}
 | 
					 | 
				
			||||||
            #{Tty.underline}#{api_source_url}#{Tty.reset}
 | 
					 | 
				
			||||||
          EOS
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cask_source = output.stdout
 | 
					 | 
				
			||||||
      actual_sha256 = Digest::SHA256.hexdigest(cask_source)
 | 
					 | 
				
			||||||
      if sha256 && actual_sha256 != sha256
 | 
					 | 
				
			||||||
        raise ArgumentError, <<~EOS
 | 
					 | 
				
			||||||
          SHA256 mismatch
 | 
					 | 
				
			||||||
          Expected: #{Formatter.success(sha256.to_s)}
 | 
					 | 
				
			||||||
            Actual: #{Formatter.error(actual_sha256.to_s)}
 | 
					 | 
				
			||||||
               URL: #{url}
 | 
					 | 
				
			||||||
          Check if you can access the URL in your browser.
 | 
					 | 
				
			||||||
          Regardless, try again in a few minutes.
 | 
					 | 
				
			||||||
        EOS
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cache[raw_endpoint] = cask_source
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sig { params(json: Hash).returns(Hash) }
 | 
					    sig { params(json: Hash).returns(Hash) }
 | 
				
			||||||
    def self.merge_variations(json)
 | 
					    def self.merge_variations(json)
 | 
				
			||||||
      bottle_tag = ::Utils::Bottles::Tag.new(system: Homebrew::SimulateSystem.current_os,
 | 
					      bottle_tag = ::Utils::Bottles::Tag.new(system: Homebrew::SimulateSystem.current_os,
 | 
				
			||||||
@ -207,5 +164,16 @@ module Homebrew
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      [true, JSON.parse(json_data["payload"])]
 | 
					      [true, JSON.parse(json_data["payload"])]
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { params(path: Pathname).returns(T.nilable(Tap)) }
 | 
				
			||||||
 | 
					    def self.tap_from_source_download(path)
 | 
				
			||||||
 | 
					      source_relative_path = path.relative_path_from(Homebrew::API::HOMEBREW_CACHE_API_SOURCE)
 | 
				
			||||||
 | 
					      return if source_relative_path.to_s.start_with?("../")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      org, repo = source_relative_path.each_filename.first(2)
 | 
				
			||||||
 | 
					      return if org.blank? || repo.blank?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Tap.fetch(org, repo)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require "extend/cachable"
 | 
					require "extend/cachable"
 | 
				
			||||||
 | 
					require "api/download"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Homebrew
 | 
					module Homebrew
 | 
				
			||||||
  module API
 | 
					  module API
 | 
				
			||||||
@ -19,12 +20,25 @@ module Homebrew
 | 
				
			|||||||
          Homebrew::API.fetch "cask/#{token}.json"
 | 
					          Homebrew::API.fetch "cask/#{token}.json"
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sig {
 | 
					        sig { params(cask: ::Cask::Cask).returns(::Cask::Cask) }
 | 
				
			||||||
          params(token: String, path: T.any(String, Pathname), git_head: String,
 | 
					        def source_download(cask)
 | 
				
			||||||
                 sha256: T.nilable(String)).returns(String)
 | 
					          path = cask.ruby_source_path.to_s || "Casks/#{cask.token}.rb"
 | 
				
			||||||
        }
 | 
					          sha256 = cask.ruby_source_checksum[:sha256]
 | 
				
			||||||
        def fetch_source(token, path:, git_head:, sha256: nil)
 | 
					          checksum = Checksum.new(sha256) if sha256
 | 
				
			||||||
          Homebrew::API.fetch_homebrew_cask_source token, path: path, git_head: git_head, sha256: sha256
 | 
					          git_head = cask.tap_git_head || "HEAD"
 | 
				
			||||||
 | 
					          tap = cask.tap&.full_name || "Homebrew/homebrew-cask"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          download = Homebrew::API::Download.new(
 | 
				
			||||||
 | 
					            "https://raw.githubusercontent.com/#{tap}/#{git_head}/#{path}",
 | 
				
			||||||
 | 
					            checksum,
 | 
				
			||||||
 | 
					            mirrors: [
 | 
				
			||||||
 | 
					              "#{HOMEBREW_API_DEFAULT_DOMAIN}/cask-source/#{File.basename(path)}",
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            cache:   HOMEBREW_CACHE_API_SOURCE/"#{tap}/#{git_head}/Cask",
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          download.fetch
 | 
				
			||||||
 | 
					          ::Cask::CaskLoader::FromPathLoader.new(download.symlink_location)
 | 
				
			||||||
 | 
					                                            .load(config: cask.config)
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sig { returns(T::Boolean) }
 | 
					        sig { returns(T::Boolean) }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										45
									
								
								Library/Homebrew/api/download.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Library/Homebrew/api/download.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					# typed: true
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require "downloadable"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module Homebrew
 | 
				
			||||||
 | 
					  module API
 | 
				
			||||||
 | 
					    # @api private
 | 
				
			||||||
 | 
					    class DownloadStrategy < CurlDownloadStrategy
 | 
				
			||||||
 | 
					      sig { override.returns(Pathname) }
 | 
				
			||||||
 | 
					      def symlink_location
 | 
				
			||||||
 | 
					        cache/name
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # @api private
 | 
				
			||||||
 | 
					    class Download < Downloadable
 | 
				
			||||||
 | 
					      sig {
 | 
				
			||||||
 | 
					        params(
 | 
				
			||||||
 | 
					          url:      String,
 | 
				
			||||||
 | 
					          checksum: T.nilable(Checksum),
 | 
				
			||||||
 | 
					          mirrors:  T::Array[String],
 | 
				
			||||||
 | 
					          cache:    T.nilable(Pathname),
 | 
				
			||||||
 | 
					        ).void
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      def initialize(url, checksum, mirrors: [], cache: nil)
 | 
				
			||||||
 | 
					        super()
 | 
				
			||||||
 | 
					        @url = URL.new(url, using: API::DownloadStrategy)
 | 
				
			||||||
 | 
					        @checksum = checksum
 | 
				
			||||||
 | 
					        @mirrors = mirrors
 | 
				
			||||||
 | 
					        @cache = cache
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig { override.returns(Pathname) }
 | 
				
			||||||
 | 
					      def cache
 | 
				
			||||||
 | 
					        @cache || super
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig { returns(Pathname) }
 | 
				
			||||||
 | 
					      def symlink_location
 | 
				
			||||||
 | 
					        T.cast(downloader, API::DownloadStrategy).symlink_location
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require "extend/cachable"
 | 
					require "extend/cachable"
 | 
				
			||||||
 | 
					require "api/download"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Homebrew
 | 
					module Homebrew
 | 
				
			||||||
  module API
 | 
					  module API
 | 
				
			||||||
@ -19,6 +20,24 @@ module Homebrew
 | 
				
			|||||||
          Homebrew::API.fetch "formula/#{name}.json"
 | 
					          Homebrew::API.fetch "formula/#{name}.json"
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sig { params(formula: ::Formula).returns(::Formula) }
 | 
				
			||||||
 | 
					        def source_download(formula)
 | 
				
			||||||
 | 
					          path = formula.ruby_source_path || "Formula/#{formula.name}.rb"
 | 
				
			||||||
 | 
					          git_head = formula.tap_git_head || "HEAD"
 | 
				
			||||||
 | 
					          tap = formula.tap&.full_name || "Homebrew/homebrew-core"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          download = Homebrew::API::Download.new(
 | 
				
			||||||
 | 
					            "https://raw.githubusercontent.com/#{tap}/#{git_head}/#{path}",
 | 
				
			||||||
 | 
					            formula.ruby_source_checksum,
 | 
				
			||||||
 | 
					            cache: HOMEBREW_CACHE_API_SOURCE/"#{tap}/#{git_head}/Formula",
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          download.fetch
 | 
				
			||||||
 | 
					          Formulary.factory(download.symlink_location,
 | 
				
			||||||
 | 
					                            formula.active_spec_sym,
 | 
				
			||||||
 | 
					                            alias_path: formula.alias_path,
 | 
				
			||||||
 | 
					                            flags:      formula.class.build_flags)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sig { returns(T::Boolean) }
 | 
					        sig { returns(T::Boolean) }
 | 
				
			||||||
        def download_and_cache_data!
 | 
					        def download_and_cache_data!
 | 
				
			||||||
          json_formulae, updated = Homebrew::API.fetch_json_api_file "formula.jws.json",
 | 
					          json_formulae, updated = Homebrew::API.fetch_json_api_file "formula.jws.json",
 | 
				
			||||||
 | 
				
			|||||||
@ -21,10 +21,33 @@ module Cask
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Loads a cask from a string.
 | 
					    # Loads a cask from a string.
 | 
				
			||||||
    class FromContentLoader
 | 
					    class AbstractContentLoader
 | 
				
			||||||
      include ILoader
 | 
					      include ILoader
 | 
				
			||||||
      attr_reader :content, :tap
 | 
					      extend T::Helpers
 | 
				
			||||||
 | 
					      abstract!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig { returns(String) }
 | 
				
			||||||
 | 
					      attr_reader :content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig { returns(T.nilable(Tap)) }
 | 
				
			||||||
 | 
					      attr_reader :tap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig {
 | 
				
			||||||
 | 
					        overridable.params(
 | 
				
			||||||
 | 
					          header_token: String,
 | 
				
			||||||
 | 
					          options:      T.untyped,
 | 
				
			||||||
 | 
					          block:        T.nilable(T.proc.bind(DSL).void),
 | 
				
			||||||
 | 
					        ).returns(Cask)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      def cask(header_token, **options, &block)
 | 
				
			||||||
 | 
					        Cask.new(header_token, source: content, tap: tap, **options, config: @config, &block)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Loads a cask from a string.
 | 
				
			||||||
 | 
					    class FromContentLoader < AbstractContentLoader
 | 
				
			||||||
      def self.can_load?(ref)
 | 
					      def self.can_load?(ref)
 | 
				
			||||||
        return false unless ref.respond_to?(:to_str)
 | 
					        return false unless ref.respond_to?(:to_str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,6 +65,8 @@ module Cask
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def initialize(content, tap: nil)
 | 
					      def initialize(content, tap: nil)
 | 
				
			||||||
 | 
					        super()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @content = content.force_encoding("UTF-8")
 | 
					        @content = content.force_encoding("UTF-8")
 | 
				
			||||||
        @tap = tap
 | 
					        @tap = tap
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@ -51,16 +76,10 @@ module Cask
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        instance_eval(content, __FILE__, __LINE__)
 | 
					        instance_eval(content, __FILE__, __LINE__)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					 | 
				
			||||||
      private
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      def cask(header_token, **options, &block)
 | 
					 | 
				
			||||||
        Cask.new(header_token, source: content, tap: tap, **options, config: @config, &block)
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Loads a cask from a path.
 | 
					    # Loads a cask from a path.
 | 
				
			||||||
    class FromPathLoader < FromContentLoader
 | 
					    class FromPathLoader < AbstractContentLoader
 | 
				
			||||||
      def self.can_load?(ref)
 | 
					      def self.can_load?(ref)
 | 
				
			||||||
        path = Pathname(ref)
 | 
					        path = Pathname(ref)
 | 
				
			||||||
        %w[.rb .json].include?(path.extname) && path.expand_path.exist?
 | 
					        %w[.rb .json].include?(path.extname) && path.expand_path.exist?
 | 
				
			||||||
@ -68,11 +87,15 @@ module Cask
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      attr_reader :token, :path
 | 
					      attr_reader :token, :path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def initialize(path) # rubocop:disable Lint/MissingSuper
 | 
					      def initialize(path, token: nil)
 | 
				
			||||||
 | 
					        super()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        path = Pathname(path).expand_path
 | 
					        path = Pathname(path).expand_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @token = path.basename(path.extname).to_s
 | 
					        @token = path.basename(path.extname).to_s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @path = path
 | 
					        @path = path
 | 
				
			||||||
 | 
					        @tap = Homebrew::API.tap_from_source_download(path)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def load(config:)
 | 
					      def load(config:)
 | 
				
			||||||
@ -153,8 +176,8 @@ module Cask
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def initialize(path)
 | 
					      def initialize(path)
 | 
				
			||||||
        @tap = Tap.from_path(path)
 | 
					 | 
				
			||||||
        super(path)
 | 
					        super(path)
 | 
				
			||||||
 | 
					        @tap = Tap.from_path(path)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -172,7 +195,7 @@ module Cask
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def load(config:)
 | 
					      def load(config:)
 | 
				
			||||||
        raise TapCaskUnavailableError.new(tap, token) unless tap.installed?
 | 
					        raise TapCaskUnavailableError.new(tap, token) unless T.must(tap).installed?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super
 | 
					        super
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@ -215,12 +238,12 @@ module Cask
 | 
				
			|||||||
        return false unless ref.is_a?(String)
 | 
					        return false unless ref.is_a?(String)
 | 
				
			||||||
        return false unless ref.match?(HOMEBREW_MAIN_TAP_CASK_REGEX)
 | 
					        return false unless ref.match?(HOMEBREW_MAIN_TAP_CASK_REGEX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        token = ref.delete_prefix("homebrew/cask/")
 | 
					        token = ref.sub(%r{^homebrew/(?:homebrew-)?cask/}i, "")
 | 
				
			||||||
        Homebrew::API::Cask.all_casks.key?(token)
 | 
					        Homebrew::API::Cask.all_casks.key?(token)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def initialize(token, from_json: nil)
 | 
					      def initialize(token, from_json: nil)
 | 
				
			||||||
        @token = token.delete_prefix("homebrew/cask/")
 | 
					        @token = token.sub(%r{^homebrew/(?:homebrew-)?cask/}i, "")
 | 
				
			||||||
        @path = CaskLoader.default_path(token)
 | 
					        @path = CaskLoader.default_path(token)
 | 
				
			||||||
        @from_json = from_json
 | 
					        @from_json = from_json
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
# typed: true
 | 
					# typed: true
 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require "downloadable"
 | 
				
			||||||
require "fileutils"
 | 
					require "fileutils"
 | 
				
			||||||
require "cask/cache"
 | 
					require "cask/cache"
 | 
				
			||||||
require "cask/quarantine"
 | 
					require "cask/quarantine"
 | 
				
			||||||
@ -9,71 +10,75 @@ module Cask
 | 
				
			|||||||
  # A download corresponding to a {Cask}.
 | 
					  # A download corresponding to a {Cask}.
 | 
				
			||||||
  #
 | 
					  #
 | 
				
			||||||
  # @api private
 | 
					  # @api private
 | 
				
			||||||
  class Download
 | 
					  class Download < ::Downloadable
 | 
				
			||||||
    include Context
 | 
					    include Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    attr_reader :cask
 | 
					    attr_reader :cask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def initialize(cask, quarantine: nil)
 | 
					    def initialize(cask, quarantine: nil)
 | 
				
			||||||
 | 
					      super()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      @cask = cask
 | 
					      @cask = cask
 | 
				
			||||||
      @quarantine = quarantine
 | 
					      @quarantine = quarantine
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.returns(T.nilable(::URL)) }
 | 
				
			||||||
 | 
					    def url
 | 
				
			||||||
 | 
					      @url ||= ::URL.new(cask.url.to_s, cask.url.specs)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.returns(T.nilable(::Checksum)) }
 | 
				
			||||||
 | 
					    def checksum
 | 
				
			||||||
 | 
					      @checksum ||= cask.sha256 if cask.sha256 != :no_check
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.returns(T.nilable(::Version)) }
 | 
				
			||||||
 | 
					    def version
 | 
				
			||||||
 | 
					      @version ||= ::Version.create(cask.version)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig {
 | 
				
			||||||
 | 
					      override
 | 
				
			||||||
 | 
					        .params(quiet:                     T.nilable(T::Boolean),
 | 
				
			||||||
 | 
					                verify_download_integrity: T::Boolean,
 | 
				
			||||||
 | 
					                timeout:                   T.nilable(T.any(Integer, Float)))
 | 
				
			||||||
 | 
					        .returns(Pathname)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    def fetch(quiet: nil, verify_download_integrity: true, timeout: nil)
 | 
					    def fetch(quiet: nil, verify_download_integrity: true, timeout: nil)
 | 
				
			||||||
      downloaded_path = begin
 | 
					      downloader.shutup! if quiet
 | 
				
			||||||
        downloader.shutup! if quiet
 | 
					
 | 
				
			||||||
        downloader.fetch(timeout: timeout)
 | 
					      begin
 | 
				
			||||||
        downloader.cached_location
 | 
					        super(verify_download_integrity: false, timeout: timeout)
 | 
				
			||||||
      rescue => e
 | 
					      rescue DownloadError => e
 | 
				
			||||||
        error = CaskError.new("Download failed on Cask '#{cask}' with message: #{e}")
 | 
					        error = CaskError.new("Download failed on Cask '#{cask}' with message: #{e.cause}")
 | 
				
			||||||
        error.set_backtrace e.backtrace
 | 
					        error.set_backtrace e.backtrace
 | 
				
			||||||
        raise error
 | 
					        raise error
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      downloaded_path = cached_download
 | 
				
			||||||
      quarantine(downloaded_path)
 | 
					      quarantine(downloaded_path)
 | 
				
			||||||
      self.verify_download_integrity(downloaded_path) if verify_download_integrity
 | 
					      self.verify_download_integrity(downloaded_path) if verify_download_integrity
 | 
				
			||||||
      downloaded_path
 | 
					      downloaded_path
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def downloader
 | 
					 | 
				
			||||||
      @downloader ||= begin
 | 
					 | 
				
			||||||
        strategy = DownloadStrategyDetector.detect(cask.url.to_s, cask.url.using)
 | 
					 | 
				
			||||||
        strategy.new(cask.url.to_s, cask.token, cask.version, cache: Cache.path, **cask.url.specs)
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def time_file_size(timeout: nil)
 | 
					    def time_file_size(timeout: nil)
 | 
				
			||||||
      downloader.resolved_time_file_size(timeout: timeout)
 | 
					      raise ArgumentError, "not supported for this download strategy" unless downloader.is_a?(CurlDownloadStrategy)
 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def clear_cache
 | 
					      T.cast(downloader, CurlDownloadStrategy).resolved_time_file_size(timeout: timeout)
 | 
				
			||||||
      downloader.clear_cache
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def cached_download
 | 
					 | 
				
			||||||
      downloader.cached_location
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def basename
 | 
					    def basename
 | 
				
			||||||
      downloader.basename
 | 
					      downloader.basename
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.params(filename: Pathname).void }
 | 
				
			||||||
    def verify_download_integrity(filename)
 | 
					    def verify_download_integrity(filename)
 | 
				
			||||||
      if @cask.sha256 == :no_check
 | 
					      if @cask.sha256 == :no_check
 | 
				
			||||||
        opoo "No checksum defined for cask '#{@cask}', skipping verification."
 | 
					        opoo "No checksum defined for cask '#{@cask}', skipping verification."
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      begin
 | 
					      super
 | 
				
			||||||
        ohai "Verifying checksum for cask '#{@cask}'" if verbose?
 | 
					 | 
				
			||||||
        filename.verify_checksum(@cask.sha256)
 | 
					 | 
				
			||||||
      rescue ChecksumMissingError
 | 
					 | 
				
			||||||
        opoo <<~EOS
 | 
					 | 
				
			||||||
          Cannot verify integrity of '#{filename.basename}'.
 | 
					 | 
				
			||||||
          No checksum was provided for this cask.
 | 
					 | 
				
			||||||
          For your reference, the checksum is:
 | 
					 | 
				
			||||||
            sha256 "#{filename.sha256}"
 | 
					 | 
				
			||||||
        EOS
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private
 | 
					    private
 | 
				
			||||||
@ -88,5 +93,20 @@ module Cask
 | 
				
			|||||||
        Quarantine.release!(download_path: path)
 | 
					        Quarantine.release!(download_path: path)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.returns(String) }
 | 
				
			||||||
 | 
					    def download_name
 | 
				
			||||||
 | 
					      cask.token
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.returns(T.nilable(::URL)) }
 | 
				
			||||||
 | 
					    def determine_url
 | 
				
			||||||
 | 
					      url
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sig { override.returns(Pathname) }
 | 
				
			||||||
 | 
					    def cache
 | 
				
			||||||
 | 
					      Cache.path
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -562,13 +562,7 @@ on_request: true)
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load_cask_from_source_api!
 | 
					    def load_cask_from_source_api!
 | 
				
			||||||
      cask_source = Homebrew::API::Cask.fetch_source(
 | 
					      @cask = Homebrew::API::Cask.source_download(@cask)
 | 
				
			||||||
        @cask.token,
 | 
					 | 
				
			||||||
        path:     @cask.ruby_source_path || "Casks/#{@cask.token}.rb",
 | 
					 | 
				
			||||||
        git_head: @cask.tap_git_head,
 | 
					 | 
				
			||||||
        sha256:   @cask.ruby_source_checksum["sha256"],
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
      @cask = CaskLoader::FromContentLoader.new(cask_source, tap: @cask.tap).load(config: @cask.config)
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -1,42 +1,166 @@
 | 
				
			|||||||
# typed: true
 | 
					# typed: true
 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Class corresponding to the `url` stanza.
 | 
					module Cask
 | 
				
			||||||
#
 | 
					  # Class corresponding to the `url` stanza.
 | 
				
			||||||
# @api private
 | 
					  #
 | 
				
			||||||
class URL < Delegator
 | 
					 | 
				
			||||||
  # @api private
 | 
					  # @api private
 | 
				
			||||||
  class DSL
 | 
					  class URL < Delegator
 | 
				
			||||||
    attr_reader :uri, :specs,
 | 
					    # @api private
 | 
				
			||||||
                :verified, :using,
 | 
					    class DSL
 | 
				
			||||||
                :tag, :branch, :revisions, :revision,
 | 
					      attr_reader :uri, :specs,
 | 
				
			||||||
                :trust_cert, :cookies, :referer, :header, :user_agent,
 | 
					                  :verified, :using,
 | 
				
			||||||
                :data, :only_path
 | 
					                  :tag, :branch, :revisions, :revision,
 | 
				
			||||||
 | 
					                  :trust_cert, :cookies, :referer, :header, :user_agent,
 | 
				
			||||||
 | 
					                  :data, :only_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    extend Forwardable
 | 
					      extend Forwardable
 | 
				
			||||||
    def_delegators :uri, :path, :scheme, :to_s
 | 
					      def_delegators :uri, :path, :scheme, :to_s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # @api public
 | 
				
			||||||
 | 
					      sig {
 | 
				
			||||||
 | 
					        params(
 | 
				
			||||||
 | 
					          uri:        T.any(URI::Generic, String),
 | 
				
			||||||
 | 
					          verified:   T.nilable(String),
 | 
				
			||||||
 | 
					          using:      T.nilable(Symbol),
 | 
				
			||||||
 | 
					          tag:        T.nilable(String),
 | 
				
			||||||
 | 
					          branch:     T.nilable(String),
 | 
				
			||||||
 | 
					          revisions:  T.nilable(T::Array[String]),
 | 
				
			||||||
 | 
					          revision:   T.nilable(String),
 | 
				
			||||||
 | 
					          trust_cert: T.nilable(T::Boolean),
 | 
				
			||||||
 | 
					          cookies:    T.nilable(T::Hash[String, String]),
 | 
				
			||||||
 | 
					          referer:    T.nilable(T.any(URI::Generic, String)),
 | 
				
			||||||
 | 
					          header:     T.nilable(String),
 | 
				
			||||||
 | 
					          user_agent: T.nilable(T.any(Symbol, String)),
 | 
				
			||||||
 | 
					          data:       T.nilable(T::Hash[String, String]),
 | 
				
			||||||
 | 
					          only_path:  T.nilable(String),
 | 
				
			||||||
 | 
					        ).void
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      def initialize(
 | 
				
			||||||
 | 
					        uri,
 | 
				
			||||||
 | 
					        verified: nil,
 | 
				
			||||||
 | 
					        using: nil,
 | 
				
			||||||
 | 
					        tag: nil,
 | 
				
			||||||
 | 
					        branch: nil,
 | 
				
			||||||
 | 
					        revisions: nil,
 | 
				
			||||||
 | 
					        revision: nil,
 | 
				
			||||||
 | 
					        trust_cert: nil,
 | 
				
			||||||
 | 
					        cookies: nil,
 | 
				
			||||||
 | 
					        referer: nil,
 | 
				
			||||||
 | 
					        header: nil,
 | 
				
			||||||
 | 
					        user_agent: nil,
 | 
				
			||||||
 | 
					        data: nil,
 | 
				
			||||||
 | 
					        only_path: nil
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @uri = URI(uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        specs = {}
 | 
				
			||||||
 | 
					        specs[:verified]   = @verified   = verified
 | 
				
			||||||
 | 
					        specs[:using]      = @using      = using
 | 
				
			||||||
 | 
					        specs[:tag]        = @tag        = tag
 | 
				
			||||||
 | 
					        specs[:branch]     = @branch     = branch
 | 
				
			||||||
 | 
					        specs[:revisions]  = @revisions  = revisions
 | 
				
			||||||
 | 
					        specs[:revision]   = @revision   = revision
 | 
				
			||||||
 | 
					        specs[:trust_cert] = @trust_cert = trust_cert
 | 
				
			||||||
 | 
					        specs[:cookies]    = @cookies    = cookies
 | 
				
			||||||
 | 
					        specs[:referer]    = @referer    = referer
 | 
				
			||||||
 | 
					        specs[:header]     = @header     = header
 | 
				
			||||||
 | 
					        specs[:user_agent] = @user_agent = user_agent || :default
 | 
				
			||||||
 | 
					        specs[:data]       = @data       = data
 | 
				
			||||||
 | 
					        specs[:only_path]  = @only_path  = only_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @specs = specs.compact
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # @api private
 | 
				
			||||||
 | 
					    class BlockDSL
 | 
				
			||||||
 | 
					      # To access URL associated with page contents.
 | 
				
			||||||
 | 
					      module PageWithURL
 | 
				
			||||||
 | 
					        # @api public
 | 
				
			||||||
 | 
					        sig { returns(URI::Generic) }
 | 
				
			||||||
 | 
					        attr_accessor :url
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig {
 | 
				
			||||||
 | 
					        params(
 | 
				
			||||||
 | 
					          uri:   T.nilable(T.any(URI::Generic, String)),
 | 
				
			||||||
 | 
					          dsl:   T.nilable(::Cask::DSL),
 | 
				
			||||||
 | 
					          block: T.proc.params(arg0: T.all(String, PageWithURL)).returns(T.untyped),
 | 
				
			||||||
 | 
					        ).void
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      def initialize(uri, dsl: nil, &block)
 | 
				
			||||||
 | 
					        @uri = uri
 | 
				
			||||||
 | 
					        @dsl = dsl
 | 
				
			||||||
 | 
					        @block = block
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig { returns(T.untyped) }
 | 
				
			||||||
 | 
					      def call
 | 
				
			||||||
 | 
					        if @uri
 | 
				
			||||||
 | 
					          result = curl_output("--fail", "--silent", "--location", @uri)
 | 
				
			||||||
 | 
					          result.assert_success!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          page = result.stdout
 | 
				
			||||||
 | 
					          page.extend PageWithURL
 | 
				
			||||||
 | 
					          page.url = URI(@uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          instance_exec(page, &@block)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          instance_exec(&@block)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # @api public
 | 
				
			||||||
 | 
					      sig {
 | 
				
			||||||
 | 
					        params(
 | 
				
			||||||
 | 
					          uri:   T.any(URI::Generic, String),
 | 
				
			||||||
 | 
					          block: T.proc.params(arg0: T.all(String, PageWithURL)).returns(T.untyped),
 | 
				
			||||||
 | 
					        ).void
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      def url(uri, &block)
 | 
				
			||||||
 | 
					        self.class.new(uri, &block).call
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      private :url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # @api public
 | 
				
			||||||
 | 
					      def method_missing(method, *args, &block)
 | 
				
			||||||
 | 
					        if @dsl.respond_to?(method)
 | 
				
			||||||
 | 
					          T.unsafe(@dsl).public_send(method, *args, &block)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          super
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      def respond_to_missing?(method, include_all)
 | 
				
			||||||
 | 
					        @dsl.respond_to?(method, include_all) || super
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # @api public
 | 
					 | 
				
			||||||
    sig {
 | 
					    sig {
 | 
				
			||||||
      params(
 | 
					      params(
 | 
				
			||||||
        uri:        T.any(URI::Generic, String),
 | 
					        uri:             T.nilable(T.any(URI::Generic, String)),
 | 
				
			||||||
        verified:   T.nilable(String),
 | 
					        verified:        T.nilable(String),
 | 
				
			||||||
        using:      T.nilable(Symbol),
 | 
					        using:           T.nilable(Symbol),
 | 
				
			||||||
        tag:        T.nilable(String),
 | 
					        tag:             T.nilable(String),
 | 
				
			||||||
        branch:     T.nilable(String),
 | 
					        branch:          T.nilable(String),
 | 
				
			||||||
        revisions:  T.nilable(T::Array[String]),
 | 
					        revisions:       T.nilable(T::Array[String]),
 | 
				
			||||||
        revision:   T.nilable(String),
 | 
					        revision:        T.nilable(String),
 | 
				
			||||||
        trust_cert: T.nilable(T::Boolean),
 | 
					        trust_cert:      T.nilable(T::Boolean),
 | 
				
			||||||
        cookies:    T.nilable(T::Hash[String, String]),
 | 
					        cookies:         T.nilable(T::Hash[String, String]),
 | 
				
			||||||
        referer:    T.nilable(T.any(URI::Generic, String)),
 | 
					        referer:         T.nilable(T.any(URI::Generic, String)),
 | 
				
			||||||
        header:     T.nilable(String),
 | 
					        header:          T.nilable(String),
 | 
				
			||||||
        user_agent: T.nilable(T.any(Symbol, String)),
 | 
					        user_agent:      T.nilable(T.any(Symbol, String)),
 | 
				
			||||||
        data:       T.nilable(T::Hash[String, String]),
 | 
					        data:            T.nilable(T::Hash[String, String]),
 | 
				
			||||||
        only_path:  T.nilable(String),
 | 
					        only_path:       T.nilable(String),
 | 
				
			||||||
 | 
					        caller_location: Thread::Backtrace::Location,
 | 
				
			||||||
 | 
					        dsl:             T.nilable(::Cask::DSL),
 | 
				
			||||||
 | 
					        block:           T.nilable(T.proc.params(arg0: T.all(String, BlockDSL::PageWithURL)).returns(T.untyped)),
 | 
				
			||||||
      ).void
 | 
					      ).void
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    def initialize(
 | 
					    def initialize(
 | 
				
			||||||
      uri,
 | 
					      uri = nil,
 | 
				
			||||||
      verified: nil,
 | 
					      verified: nil,
 | 
				
			||||||
      using: nil,
 | 
					      using: nil,
 | 
				
			||||||
      tag: nil,
 | 
					      tag: nil,
 | 
				
			||||||
@ -49,198 +173,76 @@ class URL < Delegator
 | 
				
			|||||||
      header: nil,
 | 
					      header: nil,
 | 
				
			||||||
      user_agent: nil,
 | 
					      user_agent: nil,
 | 
				
			||||||
      data: nil,
 | 
					      data: nil,
 | 
				
			||||||
      only_path: nil
 | 
					      only_path: nil,
 | 
				
			||||||
 | 
					      caller_location: T.must(caller_locations).fetch(0),
 | 
				
			||||||
 | 
					      dsl: nil,
 | 
				
			||||||
 | 
					      &block
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					      super(
 | 
				
			||||||
      @uri = URI(uri)
 | 
					        if block
 | 
				
			||||||
 | 
					          LazyObject.new do
 | 
				
			||||||
      specs = {}
 | 
					            *args = BlockDSL.new(uri, dsl: dsl, &block).call
 | 
				
			||||||
      specs[:verified]   = @verified   = verified
 | 
					            options = args.last.is_a?(Hash) ? args.pop : {}
 | 
				
			||||||
      specs[:using]      = @using      = using
 | 
					            uri = T.let(args.first, T.any(URI::Generic, String))
 | 
				
			||||||
      specs[:tag]        = @tag        = tag
 | 
					            DSL.new(uri, **options)
 | 
				
			||||||
      specs[:branch]     = @branch     = branch
 | 
					          end
 | 
				
			||||||
      specs[:revisions]  = @revisions  = revisions
 | 
					        else
 | 
				
			||||||
      specs[:revision]   = @revision   = revision
 | 
					          DSL.new(
 | 
				
			||||||
      specs[:trust_cert] = @trust_cert = trust_cert
 | 
					            T.must(uri),
 | 
				
			||||||
      specs[:cookies]    = @cookies    = cookies
 | 
					            verified:   verified,
 | 
				
			||||||
      specs[:referer]    = @referer    = referer
 | 
					            using:      using,
 | 
				
			||||||
      specs[:header]     = @header     = header
 | 
					            tag:        tag,
 | 
				
			||||||
      specs[:user_agent] = @user_agent = user_agent || :default
 | 
					            branch:     branch,
 | 
				
			||||||
      specs[:data]       = @data       = data
 | 
					            revisions:  revisions,
 | 
				
			||||||
      specs[:only_path]  = @only_path  = only_path
 | 
					            revision:   revision,
 | 
				
			||||||
 | 
					            trust_cert: trust_cert,
 | 
				
			||||||
      @specs = specs.compact
 | 
					            cookies:    cookies,
 | 
				
			||||||
    end
 | 
					            referer:    referer,
 | 
				
			||||||
  end
 | 
					            header:     header,
 | 
				
			||||||
 | 
					            user_agent: user_agent,
 | 
				
			||||||
  # @api private
 | 
					            data:       data,
 | 
				
			||||||
  class BlockDSL
 | 
					            only_path:  only_path,
 | 
				
			||||||
    # To access URL associated with page contents.
 | 
					          )
 | 
				
			||||||
    module PageWithURL
 | 
					 | 
				
			||||||
      # @api public
 | 
					 | 
				
			||||||
      sig { returns(URI::Generic) }
 | 
					 | 
				
			||||||
      attr_accessor :url
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sig {
 | 
					 | 
				
			||||||
      params(
 | 
					 | 
				
			||||||
        uri:   T.nilable(T.any(URI::Generic, String)),
 | 
					 | 
				
			||||||
        dsl:   T.nilable(Cask::DSL),
 | 
					 | 
				
			||||||
        block: T.proc.params(arg0: T.all(String, PageWithURL)).returns(T.untyped),
 | 
					 | 
				
			||||||
      ).void
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    def initialize(uri, dsl: nil, &block)
 | 
					 | 
				
			||||||
      @uri = uri
 | 
					 | 
				
			||||||
      @dsl = dsl
 | 
					 | 
				
			||||||
      @block = block
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sig { returns(T.untyped) }
 | 
					 | 
				
			||||||
    def call
 | 
					 | 
				
			||||||
      if @uri
 | 
					 | 
				
			||||||
        result = curl_output("--fail", "--silent", "--location", @uri)
 | 
					 | 
				
			||||||
        result.assert_success!
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        page = result.stdout
 | 
					 | 
				
			||||||
        page.extend PageWithURL
 | 
					 | 
				
			||||||
        page.url = URI(@uri)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        instance_exec(page, &@block)
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        instance_exec(&@block)
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # @api public
 | 
					 | 
				
			||||||
    sig {
 | 
					 | 
				
			||||||
      params(
 | 
					 | 
				
			||||||
        uri:   T.any(URI::Generic, String),
 | 
					 | 
				
			||||||
        block: T.proc.params(arg0: T.all(String, PageWithURL)).returns(T.untyped),
 | 
					 | 
				
			||||||
      ).void
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    def url(uri, &block)
 | 
					 | 
				
			||||||
      self.class.new(uri, &block).call
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    private :url
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # @api public
 | 
					 | 
				
			||||||
    def method_missing(method, *args, &block)
 | 
					 | 
				
			||||||
      if @dsl.respond_to?(method)
 | 
					 | 
				
			||||||
        T.unsafe(@dsl).public_send(method, *args, &block)
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        super
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def respond_to_missing?(method, include_all)
 | 
					 | 
				
			||||||
      @dsl.respond_to?(method, include_all) || super
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  sig {
 | 
					 | 
				
			||||||
    params(
 | 
					 | 
				
			||||||
      uri:             T.nilable(T.any(URI::Generic, String)),
 | 
					 | 
				
			||||||
      verified:        T.nilable(String),
 | 
					 | 
				
			||||||
      using:           T.nilable(Symbol),
 | 
					 | 
				
			||||||
      tag:             T.nilable(String),
 | 
					 | 
				
			||||||
      branch:          T.nilable(String),
 | 
					 | 
				
			||||||
      revisions:       T.nilable(T::Array[String]),
 | 
					 | 
				
			||||||
      revision:        T.nilable(String),
 | 
					 | 
				
			||||||
      trust_cert:      T.nilable(T::Boolean),
 | 
					 | 
				
			||||||
      cookies:         T.nilable(T::Hash[String, String]),
 | 
					 | 
				
			||||||
      referer:         T.nilable(T.any(URI::Generic, String)),
 | 
					 | 
				
			||||||
      header:          T.nilable(String),
 | 
					 | 
				
			||||||
      user_agent:      T.nilable(T.any(Symbol, String)),
 | 
					 | 
				
			||||||
      data:            T.nilable(T::Hash[String, String]),
 | 
					 | 
				
			||||||
      only_path:       T.nilable(String),
 | 
					 | 
				
			||||||
      caller_location: Thread::Backtrace::Location,
 | 
					 | 
				
			||||||
      dsl:             T.nilable(Cask::DSL),
 | 
					 | 
				
			||||||
      block:           T.nilable(T.proc.params(arg0: T.all(String, BlockDSL::PageWithURL)).returns(T.untyped)),
 | 
					 | 
				
			||||||
    ).void
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  def initialize(
 | 
					 | 
				
			||||||
    uri = nil,
 | 
					 | 
				
			||||||
    verified: nil,
 | 
					 | 
				
			||||||
    using: nil,
 | 
					 | 
				
			||||||
    tag: nil,
 | 
					 | 
				
			||||||
    branch: nil,
 | 
					 | 
				
			||||||
    revisions: nil,
 | 
					 | 
				
			||||||
    revision: nil,
 | 
					 | 
				
			||||||
    trust_cert: nil,
 | 
					 | 
				
			||||||
    cookies: nil,
 | 
					 | 
				
			||||||
    referer: nil,
 | 
					 | 
				
			||||||
    header: nil,
 | 
					 | 
				
			||||||
    user_agent: nil,
 | 
					 | 
				
			||||||
    data: nil,
 | 
					 | 
				
			||||||
    only_path: nil,
 | 
					 | 
				
			||||||
    caller_location: T.must(caller_locations).fetch(0),
 | 
					 | 
				
			||||||
    dsl: nil,
 | 
					 | 
				
			||||||
    &block
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
    super(
 | 
					 | 
				
			||||||
      if block
 | 
					 | 
				
			||||||
        LazyObject.new do
 | 
					 | 
				
			||||||
          *args = BlockDSL.new(uri, dsl: dsl, &block).call
 | 
					 | 
				
			||||||
          options = args.last.is_a?(Hash) ? args.pop : {}
 | 
					 | 
				
			||||||
          uri = T.let(args.first, T.any(URI::Generic, String))
 | 
					 | 
				
			||||||
          DSL.new(uri, **options)
 | 
					 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      else
 | 
					      )
 | 
				
			||||||
        DSL.new(
 | 
					 | 
				
			||||||
          T.must(uri),
 | 
					 | 
				
			||||||
          verified:   verified,
 | 
					 | 
				
			||||||
          using:      using,
 | 
					 | 
				
			||||||
          tag:        tag,
 | 
					 | 
				
			||||||
          branch:     branch,
 | 
					 | 
				
			||||||
          revisions:  revisions,
 | 
					 | 
				
			||||||
          revision:   revision,
 | 
					 | 
				
			||||||
          trust_cert: trust_cert,
 | 
					 | 
				
			||||||
          cookies:    cookies,
 | 
					 | 
				
			||||||
          referer:    referer,
 | 
					 | 
				
			||||||
          header:     header,
 | 
					 | 
				
			||||||
          user_agent: user_agent,
 | 
					 | 
				
			||||||
          data:       data,
 | 
					 | 
				
			||||||
          only_path:  only_path,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @from_block = !block.nil?
 | 
					      @from_block = !block.nil?
 | 
				
			||||||
    @caller_location = caller_location
 | 
					      @caller_location = caller_location
 | 
				
			||||||
  end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def __getobj__
 | 
					    def __getobj__
 | 
				
			||||||
    @dsl
 | 
					      @dsl
 | 
				
			||||||
  end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def __setobj__(dsl)
 | 
					    def __setobj__(dsl)
 | 
				
			||||||
    @dsl = dsl
 | 
					      @dsl = dsl
 | 
				
			||||||
  end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sig { returns(T.nilable(String)) }
 | 
					    sig { returns(T.nilable(String)) }
 | 
				
			||||||
  def raw_interpolated_url
 | 
					    def raw_interpolated_url
 | 
				
			||||||
    return @raw_interpolated_url if defined?(@raw_interpolated_url)
 | 
					      return @raw_interpolated_url if defined?(@raw_interpolated_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @raw_interpolated_url =
 | 
					      @raw_interpolated_url =
 | 
				
			||||||
      Pathname(@caller_location.path)
 | 
					        Pathname(@caller_location.path)
 | 
				
			||||||
      .each_line.drop(@caller_location.lineno - 1)
 | 
					        .each_line.drop(@caller_location.lineno - 1)
 | 
				
			||||||
      .first&.then { |line| line[/url\s+"([^"]+)"/, 1] }
 | 
					        .first&.then { |line| line[/url\s+"([^"]+)"/, 1] }
 | 
				
			||||||
  end
 | 
					    end
 | 
				
			||||||
  private :raw_interpolated_url
 | 
					    private :raw_interpolated_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sig { params(ignore_major_version: T::Boolean).returns(T::Boolean) }
 | 
					    sig { params(ignore_major_version: T::Boolean).returns(T::Boolean) }
 | 
				
			||||||
  def unversioned?(ignore_major_version: false)
 | 
					    def unversioned?(ignore_major_version: false)
 | 
				
			||||||
    interpolated_url = raw_interpolated_url
 | 
					      interpolated_url = raw_interpolated_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false unless interpolated_url
 | 
					      return false unless interpolated_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    interpolated_url = interpolated_url.gsub(/\#{\s*version\s*\.major\s*}/, "") if ignore_major_version
 | 
					      interpolated_url = interpolated_url.gsub(/\#{\s*version\s*\.major\s*}/, "") if ignore_major_version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    interpolated_url.exclude?('#{')
 | 
					      interpolated_url.exclude?('#{')
 | 
				
			||||||
  end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sig { returns(T::Boolean) }
 | 
					    sig { returns(T::Boolean) }
 | 
				
			||||||
  def from_block?
 | 
					    def from_block?
 | 
				
			||||||
    @from_block
 | 
					      @from_block
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
# typed: strict
 | 
					# typed: strict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class URL
 | 
					module Cask
 | 
				
			||||||
  include Kernel
 | 
					  class URL
 | 
				
			||||||
 | 
					    include Kernel
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -51,11 +51,15 @@ module Homebrew
 | 
				
			|||||||
        pathname.mtime < days_ago && pathname.ctime < days_ago
 | 
					        pathname.mtime < days_ago && pathname.ctime < days_ago
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) }
 | 
					      sig { params(entry: { path: Pathname, type: T.nilable(Symbol) }, scrub: T::Boolean).returns(T::Boolean) }
 | 
				
			||||||
      def stale?(pathname, scrub: false)
 | 
					      def stale?(entry, scrub: false)
 | 
				
			||||||
 | 
					        pathname = entry[:path]
 | 
				
			||||||
        return false unless pathname.resolved_path.file?
 | 
					        return false unless pathname.resolved_path.file?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if pathname.dirname.basename.to_s == "Cask"
 | 
					        case entry[:type]
 | 
				
			||||||
 | 
					        when :api_source
 | 
				
			||||||
 | 
					          stale_api_source?(pathname, scrub)
 | 
				
			||||||
 | 
					        when :cask
 | 
				
			||||||
          stale_cask?(pathname, scrub)
 | 
					          stale_cask?(pathname, scrub)
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
          stale_formula?(pathname, scrub)
 | 
					          stale_formula?(pathname, scrub)
 | 
				
			||||||
@ -64,6 +68,31 @@ module Homebrew
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      private
 | 
					      private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) }
 | 
				
			||||||
 | 
					      def stale_api_source?(pathname, scrub)
 | 
				
			||||||
 | 
					        return true if scrub
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        org, repo, git_head, type, basename = pathname.each_filename.to_a.last(5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        name = "#{org}/#{repo}/#{File.basename(T.must(basename), ".rb")}"
 | 
				
			||||||
 | 
					        package = if type == "Cask"
 | 
				
			||||||
 | 
					          begin
 | 
				
			||||||
 | 
					            Cask::CaskLoader.load(name)
 | 
				
			||||||
 | 
					          rescue Cask::CaskError
 | 
				
			||||||
 | 
					            nil
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          begin
 | 
				
			||||||
 | 
					            Formulary.factory(name)
 | 
				
			||||||
 | 
					          rescue FormulaUnavailableError
 | 
				
			||||||
 | 
					            nil
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        return true if package.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        package.tap_git_head != git_head
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) }
 | 
					      sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) }
 | 
				
			||||||
      def stale_formula?(pathname, scrub)
 | 
					      def stale_formula?(pathname, scrub)
 | 
				
			||||||
        return false unless HOMEBREW_CELLAR.directory?
 | 
					        return false unless HOMEBREW_CELLAR.directory?
 | 
				
			||||||
@ -235,6 +264,7 @@ module Homebrew
 | 
				
			|||||||
        Cleanup.autoremove(dry_run: dry_run?) if Homebrew::EnvConfig.autoremove?
 | 
					        Cleanup.autoremove(dry_run: dry_run?) if Homebrew::EnvConfig.autoremove?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cleanup_cache
 | 
					        cleanup_cache
 | 
				
			||||||
 | 
					        cleanup_empty_api_source_directories
 | 
				
			||||||
        cleanup_logs
 | 
					        cleanup_logs
 | 
				
			||||||
        cleanup_lockfiles
 | 
					        cleanup_lockfiles
 | 
				
			||||||
        cleanup_python_site_packages
 | 
					        cleanup_python_site_packages
 | 
				
			||||||
@ -287,14 +317,14 @@ module Homebrew
 | 
				
			|||||||
    def cleanup_formula(formula, quiet: false, ds_store: true, cache_db: true)
 | 
					    def cleanup_formula(formula, quiet: false, ds_store: true, cache_db: true)
 | 
				
			||||||
      formula.eligible_kegs_for_cleanup(quiet: quiet)
 | 
					      formula.eligible_kegs_for_cleanup(quiet: quiet)
 | 
				
			||||||
             .each(&method(:cleanup_keg))
 | 
					             .each(&method(:cleanup_keg))
 | 
				
			||||||
      cleanup_cache(Pathname.glob(cache/"#{formula.name}--*"))
 | 
					      cleanup_cache(Pathname.glob(cache/"#{formula.name}--*").map { |path| { path: path, type: nil } })
 | 
				
			||||||
      rm_ds_store([formula.rack]) if ds_store
 | 
					      rm_ds_store([formula.rack]) if ds_store
 | 
				
			||||||
      cleanup_cache_db(formula.rack) if cache_db
 | 
					      cleanup_cache_db(formula.rack) if cache_db
 | 
				
			||||||
      cleanup_lockfiles(FormulaLock.new(formula.name).path)
 | 
					      cleanup_lockfiles(FormulaLock.new(formula.name).path)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def cleanup_cask(cask, ds_store: true)
 | 
					    def cleanup_cask(cask, ds_store: true)
 | 
				
			||||||
      cleanup_cache(Pathname.glob(cache/"Cask/#{cask.token}--*"))
 | 
					      cleanup_cache(Pathname.glob(cache/"Cask/#{cask.token}--*").map { |path| { path: path, type: :cask } })
 | 
				
			||||||
      rm_ds_store([cask.caskroom_path]) if ds_store
 | 
					      rm_ds_store([cask.caskroom_path]) if ds_store
 | 
				
			||||||
      cleanup_lockfiles(CaskLock.new(cask.token).path)
 | 
					      cleanup_lockfiles(CaskLock.new(cask.token).path)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@ -316,16 +346,35 @@ module Homebrew
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cache_files
 | 
				
			||||||
 | 
					      files = cache.directory? ? cache.children : []
 | 
				
			||||||
 | 
					      cask_files = (cache/"Cask").directory? ? (cache/"Cask").children : []
 | 
				
			||||||
 | 
					      api_source_files = (cache/"api-source").glob("*/*/*/*/*") # org/repo/git_head/type/file.rb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      files.map { |path| { path: path, type: nil } } +
 | 
				
			||||||
 | 
					        cask_files.map { |path| { path: path, type: :cask } } +
 | 
				
			||||||
 | 
					        api_source_files.map { |path| { path: path, type: :api_source } }
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cleanup_empty_api_source_directories(directory = cache/"api-source")
 | 
				
			||||||
 | 
					      return if dry_run?
 | 
				
			||||||
 | 
					      return unless directory.directory?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      directory.each_child do |child|
 | 
				
			||||||
 | 
					        next unless child.directory?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cleanup_empty_api_source_directories(child)
 | 
				
			||||||
 | 
					        child.rmdir if child.empty?
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def cleanup_unreferenced_downloads
 | 
					    def cleanup_unreferenced_downloads
 | 
				
			||||||
      return if dry_run?
 | 
					      return if dry_run?
 | 
				
			||||||
      return unless (cache/"downloads").directory?
 | 
					      return unless (cache/"downloads").directory?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      downloads = (cache/"downloads").children
 | 
					      downloads = (cache/"downloads").children
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      referenced_downloads = [cache, cache/"Cask"].select(&:directory?)
 | 
					      referenced_downloads = cache_files.map { |file| file[:path] }.select(&:symlink?).map(&:resolved_path)
 | 
				
			||||||
                                                  .flat_map(&:children)
 | 
					 | 
				
			||||||
                                                  .select(&:symlink?)
 | 
					 | 
				
			||||||
                                                  .map(&:resolved_path)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      (downloads - referenced_downloads).each do |download|
 | 
					      (downloads - referenced_downloads).each do |download|
 | 
				
			||||||
        if self.class.incomplete?(download)
 | 
					        if self.class.incomplete?(download)
 | 
				
			||||||
@ -346,9 +395,10 @@ module Homebrew
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def cleanup_cache(entries = nil)
 | 
					    def cleanup_cache(entries = nil)
 | 
				
			||||||
      entries ||= [cache, cache/"Cask"].select(&:directory?).flat_map(&:children)
 | 
					      entries ||= cache_files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      entries.each do |path|
 | 
					      entries.each do |entry|
 | 
				
			||||||
 | 
					        path = entry[:path]
 | 
				
			||||||
        next if path == PERIODIC_CLEAN_FILE
 | 
					        next if path == PERIODIC_CLEAN_FILE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        FileUtils.chmod_R 0755, path if self.class.go_cache_directory?(path) && !dry_run?
 | 
					        FileUtils.chmod_R 0755, path if self.class.go_cache_directory?(path) && !dry_run?
 | 
				
			||||||
@ -365,7 +415,7 @@ module Homebrew
 | 
				
			|||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # If we've specified --prune don't do the (expensive) .stale? check.
 | 
					        # If we've specified --prune don't do the (expensive) .stale? check.
 | 
				
			||||||
        cleanup_path(path) { path.unlink } if !prune? && self.class.stale?(path, scrub: scrub?)
 | 
					        cleanup_path(path) { path.unlink } if !prune? && self.class.stale?(entry, scrub: scrub?)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      cleanup_unreferenced_downloads
 | 
					      cleanup_unreferenced_downloads
 | 
				
			||||||
 | 
				
			|||||||
@ -413,7 +413,7 @@ module Homebrew
 | 
				
			|||||||
    resource.url(url, specs)
 | 
					    resource.url(url, specs)
 | 
				
			||||||
    resource.owner = Resource.new(formula.name)
 | 
					    resource.owner = Resource.new(formula.name)
 | 
				
			||||||
    forced_version = new_version && new_version != resource.version.to_s
 | 
					    forced_version = new_version && new_version != resource.version.to_s
 | 
				
			||||||
    resource.version = new_version if forced_version
 | 
					    resource.version(new_version) if forced_version
 | 
				
			||||||
    odie "Couldn't identify version, specify it using `--version=`." if resource.version.blank?
 | 
					    odie "Couldn't identify version, specify it using `--version=`." if resource.version.blank?
 | 
				
			||||||
    [resource.fetch, forced_version]
 | 
					    [resource.fetch, forced_version]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										133
									
								
								Library/Homebrew/downloadable.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								Library/Homebrew/downloadable.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					# typed: true
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require "url"
 | 
				
			||||||
 | 
					require "checksum"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# @api private
 | 
				
			||||||
 | 
					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,
 | 
				
			||||||
 | 
					                            mirrors: mirrors, cache: cache, **T.must(@url).specs)
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					      downloader.fetch(timeout: timeout)
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sig { overridable.returns(String) }
 | 
				
			||||||
 | 
					  def download_name
 | 
				
			||||||
 | 
					    File.basename(determine_url.to_s)
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
@ -603,13 +603,16 @@ class CompilerSelectionError < RuntimeError
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Raised in {Resource#fetch}.
 | 
					# Raised in {Downloadable#fetch}.
 | 
				
			||||||
class DownloadError < RuntimeError
 | 
					class DownloadError < RuntimeError
 | 
				
			||||||
  def initialize(resource, cause)
 | 
					  attr_reader :cause
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def initialize(downloadable, cause)
 | 
				
			||||||
    super <<~EOS
 | 
					    super <<~EOS
 | 
				
			||||||
      Failed to download resource #{resource.download_name.inspect}
 | 
					      Failed to download resource #{downloadable.download_name.inspect}
 | 
				
			||||||
      #{cause.message}
 | 
					      #{cause.message}
 | 
				
			||||||
    EOS
 | 
					    EOS
 | 
				
			||||||
 | 
					    @cause = cause
 | 
				
			||||||
    set_backtrace(cause.backtrace)
 | 
					    set_backtrace(cause.backtrace)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -181,7 +181,7 @@ class Formula
 | 
				
			|||||||
  attr_accessor :force_bottle
 | 
					  attr_accessor :force_bottle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # @private
 | 
					  # @private
 | 
				
			||||||
  def initialize(name, path, spec, alias_path: nil, force_bottle: false)
 | 
					  def initialize(name, path, spec, alias_path: nil, tap: nil, force_bottle: false)
 | 
				
			||||||
    # Only allow instances of subclasses. The base class does not hold any spec information (URLs etc).
 | 
					    # Only allow instances of subclasses. The base class does not hold any spec information (URLs etc).
 | 
				
			||||||
    raise "Do not call `Formula.new' directly without a subclass." unless self.class < Formula
 | 
					    raise "Do not call `Formula.new' directly without a subclass." unless self.class < Formula
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -191,7 +191,8 @@ class Formula
 | 
				
			|||||||
    self.class.freeze
 | 
					    self.class.freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @name = name
 | 
					    @name = name
 | 
				
			||||||
    @path = path
 | 
					    @unresolved_path = path
 | 
				
			||||||
 | 
					    @path = path.resolved_path
 | 
				
			||||||
    @alias_path = alias_path
 | 
					    @alias_path = alias_path
 | 
				
			||||||
    @alias_name = (File.basename(alias_path) if alias_path)
 | 
					    @alias_name = (File.basename(alias_path) if alias_path)
 | 
				
			||||||
    @revision = self.class.revision || 0
 | 
					    @revision = self.class.revision || 0
 | 
				
			||||||
@ -199,7 +200,8 @@ class Formula
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @force_bottle = force_bottle
 | 
					    @force_bottle = force_bottle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @tap = if path == Formulary.core_path(name)
 | 
					    @tap = tap
 | 
				
			||||||
 | 
					    @tap ||= if path == Formulary.core_path(name)
 | 
				
			||||||
      CoreTap.instance
 | 
					      CoreTap.instance
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      Tap.from_path(path)
 | 
					      Tap.from_path(path)
 | 
				
			||||||
@ -320,7 +322,7 @@ class Formula
 | 
				
			|||||||
  # The path that was specified to find this formula.
 | 
					  # The path that was specified to find this formula.
 | 
				
			||||||
  def specified_path
 | 
					  def specified_path
 | 
				
			||||||
    default_specified_path = Pathname(alias_path) if alias_path.present?
 | 
					    default_specified_path = Pathname(alias_path) if alias_path.present?
 | 
				
			||||||
    default_specified_path ||= path
 | 
					    default_specified_path ||= @unresolved_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return default_specified_path if default_specified_path.presence&.exist?
 | 
					    return default_specified_path if default_specified_path.presence&.exist?
 | 
				
			||||||
    return local_bottle_path if local_bottle_path.presence&.exist?
 | 
					    return local_bottle_path if local_bottle_path.presence&.exist?
 | 
				
			||||||
@ -2094,6 +2096,18 @@ class Formula
 | 
				
			|||||||
    []
 | 
					    []
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # @private
 | 
				
			||||||
 | 
					  sig { returns(T.nilable(String)) }
 | 
				
			||||||
 | 
					  def ruby_source_path
 | 
				
			||||||
 | 
					    path.relative_path_from(tap.path).to_s if tap && path.exist?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # @private
 | 
				
			||||||
 | 
					  sig { returns(T.nilable(Checksum)) }
 | 
				
			||||||
 | 
					  def ruby_source_checksum
 | 
				
			||||||
 | 
					    Checksum.new(Digest::SHA256.file(path).hexdigest) if path.exist?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # @private
 | 
					  # @private
 | 
				
			||||||
  def to_hash
 | 
					  def to_hash
 | 
				
			||||||
    dependencies = deps
 | 
					    dependencies = deps
 | 
				
			||||||
@ -2157,6 +2171,7 @@ class Formula
 | 
				
			|||||||
      "disable_reason"           => disable_reason,
 | 
					      "disable_reason"           => disable_reason,
 | 
				
			||||||
      "service"                  => service&.serialize,
 | 
					      "service"                  => service&.serialize,
 | 
				
			||||||
      "tap_git_head"             => tap_git_head,
 | 
					      "tap_git_head"             => tap_git_head,
 | 
				
			||||||
 | 
					      "ruby_source_path"         => ruby_source_path,
 | 
				
			||||||
      "ruby_source_checksum"     => {},
 | 
					      "ruby_source_checksum"     => {},
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2208,14 +2223,9 @@ class Formula
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if self.class.loaded_from_api && active_spec.resource_defined?("ruby-source")
 | 
					    if (source_checksum = ruby_source_checksum)
 | 
				
			||||||
      hsh["ruby_source_checksum"] = {
 | 
					      hsh["ruby_source_checksum"] = {
 | 
				
			||||||
        "sha256" => resource("ruby-source").checksum.hexdigest,
 | 
					        "sha256" => source_checksum.hexdigest,
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    elsif !self.class.loaded_from_api && path.exist?
 | 
					 | 
				
			||||||
      hsh["ruby_source_path"] = (path.relative_path_from(tap.path).to_s if tap)
 | 
					 | 
				
			||||||
      hsh["ruby_source_checksum"] = {
 | 
					 | 
				
			||||||
        "sha256" => Digest::SHA256.file(path).hexdigest,
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1191,16 +1191,7 @@ on_request: installed_on_request?, options: options)
 | 
				
			|||||||
    if pour_bottle?(output_warning: true)
 | 
					    if pour_bottle?(output_warning: true)
 | 
				
			||||||
      formula.fetch_bottle_tab
 | 
					      formula.fetch_bottle_tab
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      if formula.class.loaded_from_api
 | 
					      @formula = Homebrew::API::Formula.source_download(formula) if formula.class.loaded_from_api
 | 
				
			||||||
        # TODO: unify with cask logic (https://github.com/Homebrew/brew/issues/14746)
 | 
					 | 
				
			||||||
        resource = formula.resource("ruby-source")
 | 
					 | 
				
			||||||
        resource.fetch
 | 
					 | 
				
			||||||
        @formula = Formulary.factory(resource.cached_download,
 | 
					 | 
				
			||||||
                                     formula.active_spec_sym,
 | 
					 | 
				
			||||||
                                     alias_path: formula.alias_path,
 | 
					 | 
				
			||||||
                                     flags:      formula.class.build_flags,
 | 
					 | 
				
			||||||
                                     from:       :formula_installer)
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      formula.fetch_patches
 | 
					      formula.fetch_patches
 | 
				
			||||||
      formula.resources.each(&:fetch)
 | 
					      formula.resources.each(&:fetch)
 | 
				
			||||||
 | 
				
			|||||||
@ -252,15 +252,6 @@ module Formulary
 | 
				
			|||||||
        link_overwrite overwrite_path
 | 
					        link_overwrite overwrite_path
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      resource "ruby-source" do
 | 
					 | 
				
			||||||
        tap_git_head = json_formula.fetch("tap_git_head", "HEAD")
 | 
					 | 
				
			||||||
        ruby_source_path = json_formula.fetch("ruby_source_path", "Formula/#{name}.rb")
 | 
					 | 
				
			||||||
        ruby_source_sha256 = json_formula.dig("ruby_source_checksum", "sha256")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        url "https://raw.githubusercontent.com/Homebrew/homebrew-core/#{tap_git_head}/#{ruby_source_path}"
 | 
					 | 
				
			||||||
        sha256 ruby_source_sha256 if ruby_source_sha256
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      def install
 | 
					      def install
 | 
				
			||||||
        raise "Cannot build from source from abstract formula."
 | 
					        raise "Cannot build from source from abstract formula."
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@ -307,6 +298,17 @@ module Formulary
 | 
				
			|||||||
      def versioned_formulae_names
 | 
					      def versioned_formulae_names
 | 
				
			||||||
        self.class.instance_variable_get(:@versioned_formulae_array)
 | 
					        self.class.instance_variable_get(:@versioned_formulae_array)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @ruby_source_path_string = json_formula["ruby_source_path"]
 | 
				
			||||||
 | 
					      def ruby_source_path
 | 
				
			||||||
 | 
					        self.class.instance_variable_get(:@ruby_source_path_string)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @ruby_source_checksum_hash = json_formula["ruby_source_checksum"]
 | 
				
			||||||
 | 
					      def ruby_source_checksum
 | 
				
			||||||
 | 
					        checksum_hash = self.class.instance_variable_get(:@ruby_source_checksum_hash)
 | 
				
			||||||
 | 
					        Checksum.new(checksum_hash["sha256"]) if checksum_hash&.key?("sha256")
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    T.cast(klass, T.class_of(Formula)).loaded_from_api = true
 | 
					    T.cast(klass, T.class_of(Formula)).loaded_from_api = true
 | 
				
			||||||
@ -384,10 +386,13 @@ module Formulary
 | 
				
			|||||||
    attr_reader :path
 | 
					    attr_reader :path
 | 
				
			||||||
    # The name used to install the formula
 | 
					    # The name used to install the formula
 | 
				
			||||||
    attr_reader :alias_path
 | 
					    attr_reader :alias_path
 | 
				
			||||||
 | 
					    # The formula's tap (nil if it should be implicitly determined)
 | 
				
			||||||
 | 
					    attr_reader :tap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def initialize(name, path)
 | 
					    def initialize(name, path, tap: nil)
 | 
				
			||||||
      @name = name
 | 
					      @name = name
 | 
				
			||||||
      @path = path.resolved_path
 | 
					      @path = path
 | 
				
			||||||
 | 
					      @tap = tap
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Gets the formula instance.
 | 
					    # Gets the formula instance.
 | 
				
			||||||
@ -396,7 +401,7 @@ module Formulary
 | 
				
			|||||||
    def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
 | 
					    def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
 | 
				
			||||||
      alias_path ||= self.alias_path
 | 
					      alias_path ||= self.alias_path
 | 
				
			||||||
      klass(flags: flags, ignore_errors: ignore_errors)
 | 
					      klass(flags: flags, ignore_errors: ignore_errors)
 | 
				
			||||||
        .new(name, path, spec, alias_path: alias_path, force_bottle: force_bottle)
 | 
					        .new(name, path, spec, alias_path: alias_path, tap: tap, force_bottle: force_bottle)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def klass(flags:, ignore_errors:)
 | 
					    def klass(flags:, ignore_errors:)
 | 
				
			||||||
@ -473,12 +478,7 @@ module Formulary
 | 
				
			|||||||
    def initialize(path)
 | 
					    def initialize(path)
 | 
				
			||||||
      path = Pathname.new(path).expand_path
 | 
					      path = Pathname.new(path).expand_path
 | 
				
			||||||
      name = path.basename(".rb").to_s
 | 
					      name = path.basename(".rb").to_s
 | 
				
			||||||
 | 
					      super name, path, tap: Homebrew::API.tap_from_source_download(path)
 | 
				
			||||||
      # For files we've downloaded, they will be prefixed with `{URL MD5}--`.
 | 
					 | 
				
			||||||
      # Remove that prefix to get the original filename.
 | 
					 | 
				
			||||||
      name = name.split("--", 2).last if path.dirname == HOMEBREW_CACHE/"downloads"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      super name, path
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -498,18 +498,16 @@ module Formulary
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load_file(flags:, ignore_errors:)
 | 
					    def load_file(flags:, ignore_errors:)
 | 
				
			||||||
      if @from != :formula_installer
 | 
					      match = url.match(%r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(?:/Formula)?/(?<name>[\w+-.@]+).rb})
 | 
				
			||||||
        match = url.match(%r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(?:/Formula)?/(?<name>[\w+-.@]+).rb})
 | 
					      if match
 | 
				
			||||||
        if match
 | 
					        raise UnsupportedInstallationMethod,
 | 
				
			||||||
          raise UnsupportedInstallationMethod,
 | 
					              "Installation of #{match[:name]} from a GitHub commit URL is unsupported! " \
 | 
				
			||||||
                "Installation of #{match[:name]} from a GitHub commit URL is unsupported! " \
 | 
					              "`brew extract #{match[:name]}` to a stable tap on GitHub instead."
 | 
				
			||||||
                "`brew extract #{match[:name]}` to a stable tap on GitHub instead."
 | 
					      elsif url.match?(%r{^(https?|ftp)://})
 | 
				
			||||||
        elsif url.match?(%r{^(https?|ftp)://})
 | 
					        raise UnsupportedInstallationMethod,
 | 
				
			||||||
          raise UnsupportedInstallationMethod,
 | 
					              "Non-checksummed download of #{name} formula file from an arbitrary URL is unsupported! " \
 | 
				
			||||||
                "Non-checksummed download of #{name} formula file from an arbitrary URL is unsupported! " \
 | 
					              "`brew extract` or `brew create` and `brew tap-new` to create a formula file in a tap " \
 | 
				
			||||||
                "`brew extract` or `brew create` and `brew tap-new` to create a formula file in a tap " \
 | 
					              "on GitHub instead."
 | 
				
			||||||
                "on GitHub instead."
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
      HOMEBREW_CACHE_FORMULA.mkpath
 | 
					      HOMEBREW_CACHE_FORMULA.mkpath
 | 
				
			||||||
      FileUtils.rm_f(path)
 | 
					      FileUtils.rm_f(path)
 | 
				
			||||||
@ -525,30 +523,28 @@ module Formulary
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  # Loads tapped formulae.
 | 
					  # Loads tapped formulae.
 | 
				
			||||||
  class TapLoader < FormulaLoader
 | 
					  class TapLoader < FormulaLoader
 | 
				
			||||||
    attr_reader :tap
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def initialize(tapped_name, from: nil)
 | 
					    def initialize(tapped_name, from: nil)
 | 
				
			||||||
      warn = [:keg, :rack].exclude?(from)
 | 
					      warn = [:keg, :rack].exclude?(from)
 | 
				
			||||||
      name, path = formula_name_path(tapped_name, warn: warn)
 | 
					      name, path, tap = formula_name_path(tapped_name, warn: warn)
 | 
				
			||||||
      super name, path
 | 
					      super name, path, tap: tap
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def formula_name_path(tapped_name, warn: true)
 | 
					    def formula_name_path(tapped_name, warn: true)
 | 
				
			||||||
      user, repo, name = tapped_name.split("/", 3).map(&:downcase)
 | 
					      user, repo, name = tapped_name.split("/", 3).map(&:downcase)
 | 
				
			||||||
      @tap = Tap.fetch user, repo
 | 
					      tap = Tap.fetch user, repo
 | 
				
			||||||
      path = find_formula_from_name(name)
 | 
					      path = find_formula_from_name(name, tap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      unless path.file?
 | 
					      unless path.file?
 | 
				
			||||||
        if (possible_alias = @tap.alias_dir/name).file?
 | 
					        if (possible_alias = tap.alias_dir/name).file?
 | 
				
			||||||
          path = possible_alias.resolved_path
 | 
					          path = possible_alias.resolved_path
 | 
				
			||||||
          name = path.basename(".rb").to_s
 | 
					          name = path.basename(".rb").to_s
 | 
				
			||||||
        elsif (new_name = @tap.formula_renames[name]) &&
 | 
					        elsif (new_name = tap.formula_renames[name]) &&
 | 
				
			||||||
              (new_path = find_formula_from_name(new_name)).file?
 | 
					              (new_path = find_formula_from_name(new_name, tap)).file?
 | 
				
			||||||
          old_name = name
 | 
					          old_name = name
 | 
				
			||||||
          path = new_path
 | 
					          path = new_path
 | 
				
			||||||
          name = new_name
 | 
					          name = new_name
 | 
				
			||||||
          new_name = @tap.core_tap? ? name : "#{@tap}/#{name}"
 | 
					          new_name = tap.core_tap? ? name : "#{tap}/#{name}"
 | 
				
			||||||
        elsif (new_tap_name = @tap.tap_migrations[name])
 | 
					        elsif (new_tap_name = tap.tap_migrations[name])
 | 
				
			||||||
          new_tap_user, new_tap_repo, = new_tap_name.split("/")
 | 
					          new_tap_user, new_tap_repo, = new_tap_name.split("/")
 | 
				
			||||||
          new_tap_name = "#{new_tap_user}/#{new_tap_repo}"
 | 
					          new_tap_name = "#{new_tap_user}/#{new_tap_repo}"
 | 
				
			||||||
          new_tap = Tap.fetch new_tap_name
 | 
					          new_tap = Tap.fetch new_tap_name
 | 
				
			||||||
@ -562,7 +558,7 @@ module Formulary
 | 
				
			|||||||
        opoo "Use #{new_name} instead of deprecated #{old_name}" if warn && old_name && new_name
 | 
					        opoo "Use #{new_name} instead of deprecated #{old_name}" if warn && old_name && new_name
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      [name, path]
 | 
					      [name, path, tap]
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
 | 
					    def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
 | 
				
			||||||
@ -584,8 +580,8 @@ module Formulary
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private
 | 
					    private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def find_formula_from_name(name)
 | 
					    def find_formula_from_name(name, tap)
 | 
				
			||||||
      Formulary.find_formula_in_tap(name, @tap)
 | 
					      Formulary.find_formula_in_tap(name, tap)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -768,7 +764,7 @@ module Formulary
 | 
				
			|||||||
    when URL_START_REGEX
 | 
					    when URL_START_REGEX
 | 
				
			||||||
      return FromUrlLoader.new(ref, from: from)
 | 
					      return FromUrlLoader.new(ref, from: from)
 | 
				
			||||||
    when HOMEBREW_TAP_FORMULA_REGEX
 | 
					    when HOMEBREW_TAP_FORMULA_REGEX
 | 
				
			||||||
      if ref.start_with?("homebrew/core/") && !Homebrew::EnvConfig.no_install_from_api?
 | 
					      if ref.match?(%r{^homebrew/(?:homebrew-)?core/}i) && !Homebrew::EnvConfig.no_install_from_api?
 | 
				
			||||||
        name = ref.split("/", 3).last
 | 
					        name = ref.split("/", 3).last
 | 
				
			||||||
        return FormulaAPILoader.new(name) if Homebrew::API::Formula.all_formulae.key?(name)
 | 
					        return FormulaAPILoader.new(name) if Homebrew::API::Formula.all_formulae.key?(name)
 | 
				
			||||||
        return AliasAPILoader.new(name) if Homebrew::API::Formula.all_aliases.key?(name)
 | 
					        return AliasAPILoader.new(name) if Homebrew::API::Formula.all_aliases.key?(name)
 | 
				
			||||||
 | 
				
			|||||||
@ -125,8 +125,8 @@ class ExternalPatch
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def owner=(owner)
 | 
					  def owner=(owner)
 | 
				
			||||||
    resource.owner   = owner
 | 
					    resource.owner = owner
 | 
				
			||||||
    resource.version = resource.checksum || ERB::Util.url_encode(resource.url)
 | 
					    resource.version(resource.checksum&.hexdigest || ERB::Util.url_encode(resource.url))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def apply
 | 
					  def apply
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,7 @@
 | 
				
			|||||||
# typed: true
 | 
					# typed: true
 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require "download_strategy"
 | 
					require "downloadable"
 | 
				
			||||||
require "checksum"
 | 
					 | 
				
			||||||
require "version"
 | 
					 | 
				
			||||||
require "mktemp"
 | 
					require "mktemp"
 | 
				
			||||||
require "livecheck"
 | 
					require "livecheck"
 | 
				
			||||||
require "extend/on_system"
 | 
					require "extend/on_system"
 | 
				
			||||||
@ -13,14 +11,13 @@ require "extend/on_system"
 | 
				
			|||||||
# of this class.
 | 
					# of this class.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# @api private
 | 
					# @api private
 | 
				
			||||||
class Resource
 | 
					class Resource < Downloadable
 | 
				
			||||||
  include Context
 | 
					 | 
				
			||||||
  include FileUtils
 | 
					  include FileUtils
 | 
				
			||||||
  include OnSystem::MacOSAndLinux
 | 
					  include OnSystem::MacOSAndLinux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attr_reader :mirrors, :specs, :using, :source_modified_time, :patches, :owner
 | 
					  attr_reader :source_modified_time, :patches, :owner
 | 
				
			||||||
  attr_writer :version
 | 
					  attr_writer :checksum
 | 
				
			||||||
  attr_accessor :download_strategy, :checksum
 | 
					  attr_accessor :download_strategy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Formula name must be set after the DSL, as we have no access to the
 | 
					  # Formula name must be set after the DSL, as we have no access to the
 | 
				
			||||||
  # formula name before initialization of the formula.
 | 
					  # formula name before initialization of the formula.
 | 
				
			||||||
@ -28,39 +25,25 @@ class Resource
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  sig { params(name: T.nilable(String), block: T.nilable(T.proc.bind(Resource).void)).void }
 | 
					  sig { params(name: T.nilable(String), block: T.nilable(T.proc.bind(Resource).void)).void }
 | 
				
			||||||
  def initialize(name = nil, &block)
 | 
					  def initialize(name = nil, &block)
 | 
				
			||||||
 | 
					    super()
 | 
				
			||||||
    # Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
 | 
					    # Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
 | 
				
			||||||
    @name = name
 | 
					    @name = name
 | 
				
			||||||
    @url = nil
 | 
					 | 
				
			||||||
    @version = nil
 | 
					 | 
				
			||||||
    @mirrors = []
 | 
					 | 
				
			||||||
    @specs = {}
 | 
					 | 
				
			||||||
    @checksum = nil
 | 
					 | 
				
			||||||
    @using = nil
 | 
					 | 
				
			||||||
    @patches = []
 | 
					    @patches = []
 | 
				
			||||||
    @livecheck = Livecheck.new(self)
 | 
					    @livecheck = Livecheck.new(self)
 | 
				
			||||||
    @livecheckable = false
 | 
					    @livecheckable = false
 | 
				
			||||||
 | 
					    @insecure = false
 | 
				
			||||||
    instance_eval(&block) if block
 | 
					    instance_eval(&block) if block
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def initialize_dup(other)
 | 
					  def initialize_dup(other)
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
    @name = @name.dup
 | 
					    @name = @name.dup
 | 
				
			||||||
    @version = @version.dup
 | 
					 | 
				
			||||||
    @mirrors = @mirrors.dup
 | 
					 | 
				
			||||||
    @specs = @specs.dup
 | 
					 | 
				
			||||||
    @checksum = @checksum.dup
 | 
					 | 
				
			||||||
    @using = @using.dup
 | 
					 | 
				
			||||||
    @patches = @patches.dup
 | 
					    @patches = @patches.dup
 | 
				
			||||||
    @livecheck = @livecheck.dup
 | 
					    @livecheck = @livecheck.dup
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def freeze
 | 
					  def freeze
 | 
				
			||||||
    @name.freeze
 | 
					    @name.freeze
 | 
				
			||||||
    @version.freeze
 | 
					 | 
				
			||||||
    @mirrors.freeze
 | 
					 | 
				
			||||||
    @specs.freeze
 | 
					 | 
				
			||||||
    @checksum.freeze
 | 
					 | 
				
			||||||
    @using.freeze
 | 
					 | 
				
			||||||
    @patches.freeze
 | 
					    @patches.freeze
 | 
				
			||||||
    @livecheck.freeze
 | 
					    @livecheck.freeze
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
@ -73,15 +56,15 @@ class Resource
 | 
				
			|||||||
    return if !owner.respond_to?(:full_name) || owner.full_name != "ca-certificates"
 | 
					    return if !owner.respond_to?(:full_name) || owner.full_name != "ca-certificates"
 | 
				
			||||||
    return if Homebrew::EnvConfig.no_insecure_redirect?
 | 
					    return if Homebrew::EnvConfig.no_insecure_redirect?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @specs[:insecure] = !specs[:bottle] && !DevelopmentTools.ca_file_handles_most_https_certificates?
 | 
					    @insecure = !specs[:bottle] && !DevelopmentTools.ca_file_handles_most_https_certificates?
 | 
				
			||||||
  end
 | 
					    return if @url.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def downloader
 | 
					    specs = if @insecure
 | 
				
			||||||
    return @downloader if @downloader.present?
 | 
					      @url.specs.merge({ insecure: true })
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
    url, *mirrors = determine_url_mirrors
 | 
					      @url.specs.except(:insecure)
 | 
				
			||||||
    @downloader = download_strategy.new(url, download_name, version,
 | 
					    end
 | 
				
			||||||
                                        mirrors: mirrors, **specs)
 | 
					    @url = URL.new(@url.to_s, specs)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Removes /s from resource names; this allows Go package names
 | 
					  # Removes /s from resource names; this allows Go package names
 | 
				
			||||||
@ -98,18 +81,6 @@ class Resource
 | 
				
			|||||||
    "#{owner.name}--#{escaped_name}"
 | 
					    "#{owner.name}--#{escaped_name}"
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def downloaded?
 | 
					 | 
				
			||||||
    cached_download.exist?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def cached_download
 | 
					 | 
				
			||||||
    downloader.cached_location
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def clear_cache
 | 
					 | 
				
			||||||
    downloader.clear_cache
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Verifies download and unpacks it.
 | 
					  # Verifies download and unpacks it.
 | 
				
			||||||
  # The block may call `|resource, staging| staging.retain!` to retain the staging
 | 
					  # The block may call `|resource, staging| staging.retain!` to retain the staging
 | 
				
			||||||
  # directory. Subclasses that override stage should implement the tmp
 | 
					  # directory. Subclasses that override stage should implement the tmp
 | 
				
			||||||
@ -171,33 +142,9 @@ class Resource
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def fetch(verify_download_integrity: true)
 | 
					  def fetch(verify_download_integrity: true)
 | 
				
			||||||
    HOMEBREW_CACHE.mkpath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fetch_patches
 | 
					    fetch_patches
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    begin
 | 
					    super(verify_download_integrity: verify_download_integrity)
 | 
				
			||||||
      downloader.fetch
 | 
					 | 
				
			||||||
    rescue ErrorDuringExecution, CurlDownloadStrategyError => e
 | 
					 | 
				
			||||||
      raise DownloadError.new(self, e)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    download = cached_download
 | 
					 | 
				
			||||||
    verify_download_integrity(download) if verify_download_integrity
 | 
					 | 
				
			||||||
    download
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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 this resource.
 | 
					 | 
				
			||||||
      For your reference, the checksum is:
 | 
					 | 
				
			||||||
        sha256 "#{filename.sha256}"
 | 
					 | 
				
			||||||
    EOS
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # @!attribute [w] livecheck
 | 
					  # @!attribute [w] livecheck
 | 
				
			||||||
@ -230,24 +177,29 @@ class Resource
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def url(val = nil, **specs)
 | 
					  def url(val = nil, **specs)
 | 
				
			||||||
    return @url if val.nil?
 | 
					    return @url&.to_s if val.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    specs = specs.dup
 | 
					    specs = specs.dup
 | 
				
			||||||
    # Don't allow this to be set.
 | 
					    # Don't allow this to be set.
 | 
				
			||||||
    specs.delete(:insecure)
 | 
					    specs.delete(:insecure)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @url = val
 | 
					    specs[:insecure] = true if @insecure
 | 
				
			||||||
    @using = specs.delete(:using)
 | 
					
 | 
				
			||||||
    @download_strategy = DownloadStrategyDetector.detect(url, using)
 | 
					    @url = URL.new(val, specs)
 | 
				
			||||||
    @specs.merge!(specs)
 | 
					 | 
				
			||||||
    @downloader = nil
 | 
					    @downloader = nil
 | 
				
			||||||
    @version = detect_version(@version)
 | 
					    @download_strategy = @url.download_strategy
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def version(val = nil)
 | 
					  def version(val = nil)
 | 
				
			||||||
    return @version if val.nil?
 | 
					    return super() if val.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @version = detect_version(val)
 | 
					    @version = case val
 | 
				
			||||||
 | 
					    when String  then Version.create(val)
 | 
				
			||||||
 | 
					    when Version then val
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      # TODO: This can probably go if/when typechecking is enforced in taps.
 | 
				
			||||||
 | 
					      raise TypeError, "version '#{val.inspect}' should be a string"
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def mirror(val)
 | 
					  def mirror(val)
 | 
				
			||||||
@ -259,6 +211,14 @@ class Resource
 | 
				
			|||||||
    patches << p
 | 
					    patches << p
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def using
 | 
				
			||||||
 | 
					    @url&.using
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def specs
 | 
				
			||||||
 | 
					    @url&.specs || {}.freeze
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected
 | 
					  protected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def stage_resource(prefix, debug_symbols: false, &block)
 | 
					  def stage_resource(prefix, debug_symbols: false, &block)
 | 
				
			||||||
@ -267,18 +227,6 @@ class Resource
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def detect_version(val)
 | 
					 | 
				
			||||||
    version = case val
 | 
					 | 
				
			||||||
    when nil     then url.nil? ? Version::NULL : Version.detect(url, **specs)
 | 
					 | 
				
			||||||
    when String  then Version.create(val)
 | 
					 | 
				
			||||||
    when Version then val
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      raise TypeError, "version '#{val.inspect}' should be a string"
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    version unless version.null?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def determine_url_mirrors
 | 
					  def determine_url_mirrors
 | 
				
			||||||
    extra_urls = []
 | 
					    extra_urls = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -301,7 +249,7 @@ class Resource
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [*extra_urls, url, *mirrors].uniq
 | 
					    [*extra_urls, *super].uniq
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # A resource containing a Go package.
 | 
					  # A resource containing a Go package.
 | 
				
			||||||
 | 
				
			|||||||
@ -89,15 +89,11 @@ class SoftwareSpec
 | 
				
			|||||||
    @resource.owner = self
 | 
					    @resource.owner = self
 | 
				
			||||||
    resources.each_value do |r|
 | 
					    resources.each_value do |r|
 | 
				
			||||||
      r.owner = self
 | 
					      r.owner = self
 | 
				
			||||||
      r.version ||= begin
 | 
					      next if r.version
 | 
				
			||||||
        raise "#{full_name}: version missing for \"#{r.name}\" resource!" if version.nil?
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if version.head?
 | 
					      raise "#{full_name}: version missing for \"#{r.name}\" resource!" if version.nil?
 | 
				
			||||||
          Version.create("HEAD")
 | 
					
 | 
				
			||||||
        else
 | 
					      r.version(version.head? ? Version.create("HEAD") : version.dup)
 | 
				
			||||||
          version.dup
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    patches.each { |p| p.owner = self }
 | 
					    patches.each { |p| p.owner = self }
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@ -281,7 +277,7 @@ end
 | 
				
			|||||||
class HeadSoftwareSpec < SoftwareSpec
 | 
					class HeadSoftwareSpec < SoftwareSpec
 | 
				
			||||||
  def initialize(flags: [])
 | 
					  def initialize(flags: [])
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
    @resource.version = Version.create("HEAD")
 | 
					    @resource.version(Version.create("HEAD"))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def verify_download_integrity(_filename)
 | 
					  def verify_download_integrity(_filename)
 | 
				
			||||||
@ -340,7 +336,6 @@ class Bottle
 | 
				
			|||||||
  def initialize(formula, spec, tag = nil)
 | 
					  def initialize(formula, spec, tag = nil)
 | 
				
			||||||
    @name = formula.name
 | 
					    @name = formula.name
 | 
				
			||||||
    @resource = Resource.new
 | 
					    @resource = Resource.new
 | 
				
			||||||
    @resource.specs[:bottle] = true
 | 
					 | 
				
			||||||
    @resource.owner = formula
 | 
					    @resource.owner = formula
 | 
				
			||||||
    @spec = spec
 | 
					    @spec = spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -350,7 +345,7 @@ class Bottle
 | 
				
			|||||||
    @cellar = tag_spec.cellar
 | 
					    @cellar = tag_spec.cellar
 | 
				
			||||||
    @rebuild = spec.rebuild
 | 
					    @rebuild = spec.rebuild
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @resource.version = formula.pkg_version.to_s
 | 
					    @resource.version(formula.pkg_version.to_s)
 | 
				
			||||||
    @resource.checksum = tag_spec.checksum
 | 
					    @resource.checksum = tag_spec.checksum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @fetch_tab_retried = false
 | 
					    @fetch_tab_retried = false
 | 
				
			||||||
@ -468,13 +463,15 @@ class Bottle
 | 
				
			|||||||
        using:   CurlGitHubPackagesDownloadStrategy,
 | 
					        using:   CurlGitHubPackagesDownloadStrategy,
 | 
				
			||||||
        headers: ["Accept: application/vnd.oci.image.index.v1+json"],
 | 
					        headers: ["Accept: application/vnd.oci.image.index.v1+json"],
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      resource.downloader.resolved_basename = "#{name}-#{version_rebuild}.bottle_manifest.json"
 | 
					      T.cast(resource.downloader, CurlGitHubPackagesDownloadStrategy).resolved_basename =
 | 
				
			||||||
 | 
					        "#{name}-#{version_rebuild}.bottle_manifest.json"
 | 
				
			||||||
      resource
 | 
					      resource
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def select_download_strategy(specs)
 | 
					  def select_download_strategy(specs)
 | 
				
			||||||
    specs[:using] ||= DownloadStrategyDetector.detect(@root_url)
 | 
					    specs[:using] ||= DownloadStrategyDetector.detect(@root_url)
 | 
				
			||||||
 | 
					    specs[:bottle] = true
 | 
				
			||||||
    specs
 | 
					    specs
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,11 +6,12 @@ HOMEBREW_TAP_FORMULA_REGEX = %r{^([\w-]+)/([\w-]+)/([\w+-.@]+)$}.freeze
 | 
				
			|||||||
# Match taps' casks, e.g. `someuser/sometap/somecask`
 | 
					# Match taps' casks, e.g. `someuser/sometap/somecask`
 | 
				
			||||||
HOMEBREW_TAP_CASK_REGEX = %r{^([\w-]+)/([\w-]+)/([a-z0-9\-_]+)$}.freeze
 | 
					HOMEBREW_TAP_CASK_REGEX = %r{^([\w-]+)/([\w-]+)/([a-z0-9\-_]+)$}.freeze
 | 
				
			||||||
# Match main cask taps' casks, e.g. `homebrew/cask/somecask` or `somecask`
 | 
					# Match main cask taps' casks, e.g. `homebrew/cask/somecask` or `somecask`
 | 
				
			||||||
HOMEBREW_MAIN_TAP_CASK_REGEX = %r{^(homebrew/cask/)?[a-z0-9\-_]+$}.freeze
 | 
					HOMEBREW_MAIN_TAP_CASK_REGEX = %r{^([Hh]omebrew/(?:homebrew-)?cask/)?[a-z0-9\-_]+$}.freeze
 | 
				
			||||||
# Match taps' directory paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap`
 | 
					# Match taps' directory paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap`
 | 
				
			||||||
HOMEBREW_TAP_DIR_REGEX = %r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/(?<user>[\w-]+)/(?<repo>[\w-]+)}.freeze
 | 
					HOMEBREW_TAP_DIR_REGEX = %r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/(?<user>[\w-]+)/(?<repo>[\w-]+)}.freeze
 | 
				
			||||||
# Match taps' formula paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula`
 | 
					# Match taps' formula paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula`
 | 
				
			||||||
HOMEBREW_TAP_PATH_REGEX = Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{(?:/.*)?$}.source).freeze
 | 
					HOMEBREW_TAP_PATH_REGEX = Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{(?:/.*)?$}.source).freeze
 | 
				
			||||||
# Match official taps' casks, e.g. `homebrew/cask/somecask or homebrew/cask-versions/somecask`
 | 
					# Match official taps' casks, e.g. `homebrew/cask/somecask or homebrew/cask-versions/somecask`
 | 
				
			||||||
HOMEBREW_CASK_TAP_CASK_REGEX = %r{^(?:([Cc]askroom)/(cask|versions)|(homebrew)/(cask|cask-[\w-]+))/([\w+-.]+)$}.freeze
 | 
					HOMEBREW_CASK_TAP_CASK_REGEX =
 | 
				
			||||||
 | 
					  %r{^(?:([Cc]askroom)/(cask|versions)|([Hh]omebrew)/(?:homebrew-)?(cask|cask-[\w-]+))/([\w+-.]+)$}.freeze
 | 
				
			||||||
HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX = /^(home|linux)brew-/.freeze
 | 
					HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX = /^(home|linux)brew-/.freeze
 | 
				
			||||||
 | 
				
			|||||||
@ -45,14 +45,4 @@ describe Homebrew::API::Cask do
 | 
				
			|||||||
      expect(casks_output).to eq casks_hash
 | 
					      expect(casks_output).to eq casks_hash
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe "::fetch_source" do
 | 
					 | 
				
			||||||
    it "fetches the source of a cask (defaulting to master when no `git_head` is passed)" do
 | 
					 | 
				
			||||||
      curl_output = instance_double(SystemCommand::Result, stdout: "foo", success?: true)
 | 
					 | 
				
			||||||
      expect(Utils::Curl).to receive(:curl_output)
 | 
					 | 
				
			||||||
        .with("--fail", "https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/foo.rb")
 | 
					 | 
				
			||||||
        .and_return(curl_output)
 | 
					 | 
				
			||||||
      described_class.fetch_source("foo", path: "Casks/foo.rb", git_head: "HEAD")
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -67,19 +67,4 @@ describe Homebrew::API do
 | 
				
			|||||||
      end.to raise_error(SystemExit)
 | 
					      end.to raise_error(SystemExit)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe "::fetch_file_source" do
 | 
					 | 
				
			||||||
    it "fetches a file" do
 | 
					 | 
				
			||||||
      mock_curl_output stdout: json
 | 
					 | 
				
			||||||
      fetched_json = described_class.fetch_homebrew_cask_source("foo", path: "Casks/foo.rb", git_head: "HEAD")
 | 
					 | 
				
			||||||
      expect(fetched_json).to eq json
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it "raises an error if the file does not exist" do
 | 
					 | 
				
			||||||
      mock_curl_output success: false
 | 
					 | 
				
			||||||
      expect do
 | 
					 | 
				
			||||||
        described_class.fetch_homebrew_cask_source("bar", path: "Casks/bar.rb", git_head: "HEAD")
 | 
					 | 
				
			||||||
      end.to raise_error(ArgumentError, /No valid file found/)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -231,7 +231,7 @@ describe Cask::Cask, :cask do
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    context "when loaded from json file" do
 | 
					    context "when loaded from json file" do
 | 
				
			||||||
      it "returns expected hash" do
 | 
					      it "returns expected hash" do
 | 
				
			||||||
        expect(Homebrew::API::Cask).not_to receive(:fetch_source)
 | 
					        expect(Homebrew::API::Cask).not_to receive(:source_download)
 | 
				
			||||||
        hash = Cask::CaskLoader::FromAPILoader.new(
 | 
					        hash = Cask::CaskLoader::FromAPILoader.new(
 | 
				
			||||||
          "everything", from_json: JSON.parse(expected_json)
 | 
					          "everything", from_json: JSON.parse(expected_json)
 | 
				
			||||||
        ).load(config: nil).to_h
 | 
					        ).load(config: nil).to_h
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ module Cask
 | 
				
			|||||||
      let(:downloaded_path) { Pathname.new("cask.zip") }
 | 
					      let(:downloaded_path) { Pathname.new("cask.zip") }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
 | 
					        allow(downloaded_path).to receive(:file?).and_return(true)
 | 
				
			||||||
        allow(downloaded_path).to receive(:sha256).and_return(computed_sha256)
 | 
					        allow(downloaded_path).to receive(:sha256).and_return(computed_sha256)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -240,7 +240,8 @@ describe Cask::Installer, :cask do
 | 
				
			|||||||
      let(:content) { File.read(path) }
 | 
					      let(:content) { File.read(path) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it "installs cask" do
 | 
					      it "installs cask" do
 | 
				
			||||||
        expect(Homebrew::API::Cask).to receive(:fetch_source).once.and_return(content)
 | 
					        source_caffeine = Cask::CaskLoader.load(path)
 | 
				
			||||||
 | 
					        expect(Homebrew::API::Cask).to receive(:source_download).once.and_return(source_caffeine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        caffeine = Cask::CaskLoader.load(path)
 | 
					        caffeine = Cask::CaskLoader.load(path)
 | 
				
			||||||
        expect(caffeine).to receive(:loaded_from_api?).once.and_return(true)
 | 
					        expect(caffeine).to receive(:loaded_from_api?).once.and_return(true)
 | 
				
			||||||
@ -307,7 +308,8 @@ describe Cask::Installer, :cask do
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it "uninstalls cask" do
 | 
					      it "uninstalls cask" do
 | 
				
			||||||
        expect(Homebrew::API::Cask).to receive(:fetch_source).twice.and_return(content)
 | 
					        source_caffeine = Cask::CaskLoader.load(path)
 | 
				
			||||||
 | 
					        expect(Homebrew::API::Cask).to receive(:source_download).twice.and_return(source_caffeine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        caffeine = Cask::CaskLoader.load(path)
 | 
					        caffeine = Cask::CaskLoader.load(path)
 | 
				
			||||||
        expect(caffeine).to receive(:loaded_from_api?).twice.and_return(true)
 | 
					        expect(caffeine).to receive(:loaded_from_api?).twice.and_return(true)
 | 
				
			||||||
 | 
				
			|||||||
@ -206,14 +206,14 @@ describe Formula do
 | 
				
			|||||||
  example "installed alias with tap" do
 | 
					  example "installed alias with tap" do
 | 
				
			||||||
    tap = Tap.new("user", "repo")
 | 
					    tap = Tap.new("user", "repo")
 | 
				
			||||||
    name = "foo"
 | 
					    name = "foo"
 | 
				
			||||||
    path = "#{tap.path}/Formula/#{name}.rb"
 | 
					    path = tap.path/"Formula/#{name}.rb"
 | 
				
			||||||
    f = formula name, path: path do
 | 
					    f = formula name, path: path do
 | 
				
			||||||
      url "foo-1.0"
 | 
					      url "foo-1.0"
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    build_values_with_no_installed_alias = [
 | 
					    build_values_with_no_installed_alias = [
 | 
				
			||||||
      BuildOptions.new(Options.new, f.options),
 | 
					      BuildOptions.new(Options.new, f.options),
 | 
				
			||||||
      Tab.new(source: { "path" => f.path }),
 | 
					      Tab.new(source: { "path" => f.path.to_s }),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    build_values_with_no_installed_alias.each do |build|
 | 
					    build_values_with_no_installed_alias.each do |build|
 | 
				
			||||||
      f.build = build
 | 
					      f.build = build
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ describe Formulary do
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        bottle do
 | 
					        bottle do
 | 
				
			||||||
          root_url "file://#{bottle_dir}"
 | 
					          root_url "file://#{bottle_dir}"
 | 
				
			||||||
          sha256 cellar: :any_skip_relocation, #{Utils::Bottles.tag}: "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
 | 
					          sha256 cellar: :any_skip_relocation, #{Utils::Bottles.tag}: "d7b9f4e8bf83608b71fe958a99f19f2e5e68bb2582965d32e41759c24f1aef97"
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def install
 | 
					        def install
 | 
				
			||||||
@ -243,7 +243,7 @@ describe Formulary do
 | 
				
			|||||||
                  Utils::Bottles.tag.to_s => {
 | 
					                  Utils::Bottles.tag.to_s => {
 | 
				
			||||||
                    "cellar" => ":any",
 | 
					                    "cellar" => ":any",
 | 
				
			||||||
                    "url"    => "file://#{bottle_dir}/#{formula_name}",
 | 
					                    "url"    => "file://#{bottle_dir}/#{formula_name}",
 | 
				
			||||||
                    "sha256" => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149",
 | 
					                    "sha256" => "d7b9f4e8bf83608b71fe958a99f19f2e5e68bb2582965d32e41759c24f1aef97",
 | 
				
			||||||
                  },
 | 
					                  },
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
 | 
				
			|||||||
@ -143,7 +143,7 @@ describe Resource do
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  describe "#download_strategy" do
 | 
					  describe "#download_strategy" do
 | 
				
			||||||
    it "returns the download strategy" do
 | 
					    it "returns the download strategy" do
 | 
				
			||||||
      strategy = Object.new
 | 
					      strategy = Class.new(AbstractDownloadStrategy)
 | 
				
			||||||
      expect(DownloadStrategyDetector)
 | 
					      expect(DownloadStrategyDetector)
 | 
				
			||||||
        .to receive(:detect).with("foo", nil).and_return(strategy)
 | 
					        .to receive(:detect).with("foo", nil).and_return(strategy)
 | 
				
			||||||
      resource.url("foo")
 | 
					      resource.url("foo")
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@ -3,7 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Failball < Formula
 | 
					class Failball < Formula
 | 
				
			||||||
  def initialize(name = "failball", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
					  def initialize(name = "failball", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
				
			||||||
                 alias_path: nil, force_bottle: false)
 | 
					                 alias_path: nil, tap: nil, force_bottle: false)
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Testball < Formula
 | 
					class Testball < Formula
 | 
				
			||||||
  def initialize(name = "testball", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
					  def initialize(name = "testball", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
				
			||||||
                 alias_path: nil, force_bottle: false)
 | 
					                 alias_path: nil, tap: nil, force_bottle: false)
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class TestballBottle < Formula
 | 
					class TestballBottle < Formula
 | 
				
			||||||
  def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
					  def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
				
			||||||
                 alias_path: nil, force_bottle: false)
 | 
					                 alias_path: nil, tap: nil, force_bottle: false)
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,7 +13,7 @@ class TestballBottle < Formula
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bottle do
 | 
					    bottle do
 | 
				
			||||||
      root_url "file://#{TEST_FIXTURE_DIR}/bottles"
 | 
					      root_url "file://#{TEST_FIXTURE_DIR}/bottles"
 | 
				
			||||||
      sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
 | 
					      sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "d7b9f4e8bf83608b71fe958a99f19f2e5e68bb2582965d32e41759c24f1aef97"
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cxxstdlib_check :skip
 | 
					    cxxstdlib_check :skip
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class TestballBottleCellar < Formula
 | 
					class TestballBottleCellar < Formula
 | 
				
			||||||
  def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
					  def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable,
 | 
				
			||||||
                 alias_path: nil, force_bottle: false)
 | 
					                 alias_path: nil, tap: nil, force_bottle: false)
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,7 +13,7 @@ class TestballBottleCellar < Formula
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bottle do
 | 
					    bottle do
 | 
				
			||||||
      root_url "file://#{TEST_FIXTURE_DIR}/bottles"
 | 
					      root_url "file://#{TEST_FIXTURE_DIR}/bottles"
 | 
				
			||||||
      sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
 | 
					      sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "d7b9f4e8bf83608b71fe958a99f19f2e5e68bb2582965d32e41759c24f1aef97"
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cxxstdlib_check :skip
 | 
					    cxxstdlib_check :skip
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										33
									
								
								Library/Homebrew/url.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Library/Homebrew/url.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					# typed: true
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require "download_strategy"
 | 
				
			||||||
 | 
					require "version"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# @api private
 | 
				
			||||||
 | 
					class URL
 | 
				
			||||||
 | 
					  attr_reader :specs, :using
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sig { params(url: String, specs: T::Hash[Symbol, T.untyped]).void }
 | 
				
			||||||
 | 
					  def initialize(url, specs = {})
 | 
				
			||||||
 | 
					    @url = url.freeze
 | 
				
			||||||
 | 
					    @specs = specs.dup
 | 
				
			||||||
 | 
					    @using = @specs.delete(:using)
 | 
				
			||||||
 | 
					    @specs.freeze
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sig { returns(String) }
 | 
				
			||||||
 | 
					  def to_s
 | 
				
			||||||
 | 
					    @url
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sig { returns(T.class_of(AbstractDownloadStrategy)) }
 | 
				
			||||||
 | 
					  def download_strategy
 | 
				
			||||||
 | 
					    @download_strategy ||= DownloadStrategyDetector.detect(@url, @using)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sig { returns(Version) }
 | 
				
			||||||
 | 
					  def version
 | 
				
			||||||
 | 
					    @version ||= Version.detect(@url, **@specs)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user