Merge pull request #17554 from Homebrew/cask-install-receipt
This commit is contained in:
		
						commit
						f39b5c1426
					
				@ -20,5 +20,6 @@ require "cask/migrator"
 | 
			
		||||
require "cask/pkg"
 | 
			
		||||
require "cask/quarantine"
 | 
			
		||||
require "cask/staged"
 | 
			
		||||
require "cask/tab"
 | 
			
		||||
require "cask/url"
 | 
			
		||||
require "cask/utils"
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ require "cask/cask_loader"
 | 
			
		||||
require "cask/config"
 | 
			
		||||
require "cask/dsl"
 | 
			
		||||
require "cask/metadata"
 | 
			
		||||
require "cask/tab"
 | 
			
		||||
require "utils/bottles"
 | 
			
		||||
require "extend/api_hashable"
 | 
			
		||||
 | 
			
		||||
@ -158,6 +159,17 @@ module Cask
 | 
			
		||||
      languages.any? || artifacts.any?(Artifact::AbstractFlightBlock)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def uninstall_flight_blocks?
 | 
			
		||||
      artifacts.any? do |artifact|
 | 
			
		||||
        case artifact
 | 
			
		||||
        when Artifact::PreflightBlock
 | 
			
		||||
          artifact.directives.key?(:uninstall_preflight)
 | 
			
		||||
        when Artifact::PostflightBlock
 | 
			
		||||
          artifact.directives.key?(:uninstall_postflight)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(T.nilable(Time)) }
 | 
			
		||||
    def install_time
 | 
			
		||||
      # <caskroom_path>/.metadata/<version>/<timestamp>/Casks/<token>.{rb,json} -> <timestamp>
 | 
			
		||||
@ -209,6 +221,10 @@ module Cask
 | 
			
		||||
      bundle_version&.version
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def tab
 | 
			
		||||
      Tab.for_cask(self)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def config_path
 | 
			
		||||
      metadata_main_container_path/"config.json"
 | 
			
		||||
    end
 | 
			
		||||
@ -465,6 +481,27 @@ module Cask
 | 
			
		||||
      hash
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def artifacts_list(compact: false, uninstall_only: false)
 | 
			
		||||
      artifacts.filter_map do |artifact|
 | 
			
		||||
        case artifact
 | 
			
		||||
        when Artifact::AbstractFlightBlock
 | 
			
		||||
          uninstall_flight_block = artifact.directives.key?(:uninstall_preflight) ||
 | 
			
		||||
                                   artifact.directives.key?(:uninstall_postflight)
 | 
			
		||||
          next if uninstall_only && !uninstall_flight_block
 | 
			
		||||
 | 
			
		||||
          # Only indicate whether this block is used as we don't load it from the API
 | 
			
		||||
          # We can skip this entirely once we move to internal JSON v3.
 | 
			
		||||
          { artifact.summarize.to_sym => nil } unless compact
 | 
			
		||||
        else
 | 
			
		||||
          zap_artifact = artifact.is_a?(Artifact::Zap)
 | 
			
		||||
          uninstall_artifact = artifact.respond_to?(:uninstall_phase) || artifact.respond_to?(:post_uninstall_phase)
 | 
			
		||||
          next if uninstall_only && !zap_artifact && !uninstall_artifact
 | 
			
		||||
 | 
			
		||||
          { artifact.class.dsl_key => artifact.to_args }
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    sig { returns(T.nilable(Homebrew::BundleVersion)) }
 | 
			
		||||
@ -482,19 +519,6 @@ module Cask
 | 
			
		||||
      hash
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def artifacts_list(compact: false)
 | 
			
		||||
      artifacts.filter_map do |artifact|
 | 
			
		||||
        case artifact
 | 
			
		||||
        when Artifact::AbstractFlightBlock
 | 
			
		||||
          # Only indicate whether this block is used as we don't load it from the API
 | 
			
		||||
          # We can skip this entirely once we move to internal JSON v3.
 | 
			
		||||
          { artifact.summarize => nil } unless compact
 | 
			
		||||
        else
 | 
			
		||||
          { artifact.class.dsl_key => artifact.to_args }
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def url_specs
 | 
			
		||||
      url&.specs.dup.tap do |url_specs|
 | 
			
		||||
        case url_specs&.dig(:user_agent)
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ module Cask
 | 
			
		||||
      output << "#{Formatter.url(cask.homepage)}\n" if cask.homepage
 | 
			
		||||
      deprecate_disable = DeprecateDisable.message(cask)
 | 
			
		||||
      output << "#{deprecate_disable.capitalize}\n" if deprecate_disable
 | 
			
		||||
      output << installation_info(cask)
 | 
			
		||||
      output << "#{installation_info(cask)}\n"
 | 
			
		||||
      repo = repo_info(cask)
 | 
			
		||||
      output << "#{repo}\n" if repo
 | 
			
		||||
      output << name_info(cask)
 | 
			
		||||
@ -37,7 +37,7 @@ module Cask
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.installation_info(cask)
 | 
			
		||||
      return "Not installed\n" unless cask.installed?
 | 
			
		||||
      return "Not installed" unless cask.installed?
 | 
			
		||||
 | 
			
		||||
      versioned_staged_path = cask.caskroom_path.join(cask.installed_version)
 | 
			
		||||
      path_details = if versioned_staged_path.exist?
 | 
			
		||||
@ -46,7 +46,12 @@ module Cask
 | 
			
		||||
        Formatter.error("does not exist")
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      "Installed\n#{versioned_staged_path} (#{path_details})\n"
 | 
			
		||||
      tab = Tab.for_cask(cask)
 | 
			
		||||
 | 
			
		||||
      info = ["Installed"]
 | 
			
		||||
      info << "#{versioned_staged_path} (#{path_details})"
 | 
			
		||||
      info << "  #{tab}" if tab.tabfile&.exist?
 | 
			
		||||
      info.join("\n")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.name_info(cask)
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ require "cask/config"
 | 
			
		||||
require "cask/download"
 | 
			
		||||
require "cask/migrator"
 | 
			
		||||
require "cask/quarantine"
 | 
			
		||||
require "cask/tab"
 | 
			
		||||
 | 
			
		||||
require "cgi"
 | 
			
		||||
 | 
			
		||||
@ -21,8 +22,8 @@ module Cask
 | 
			
		||||
    def initialize(cask, command: SystemCommand, force: false, adopt: false,
 | 
			
		||||
                   skip_cask_deps: false, binaries: true, verbose: false,
 | 
			
		||||
                   zap: false, require_sha: false, upgrade: false, reinstall: false,
 | 
			
		||||
                   installed_as_dependency: false, quarantine: true,
 | 
			
		||||
                   verify_download_integrity: true, quiet: false)
 | 
			
		||||
                   installed_as_dependency: false, installed_on_request: true,
 | 
			
		||||
                   quarantine: true, verify_download_integrity: true, quiet: false)
 | 
			
		||||
      @cask = cask
 | 
			
		||||
      @command = command
 | 
			
		||||
      @force = force
 | 
			
		||||
@ -35,13 +36,14 @@ module Cask
 | 
			
		||||
      @reinstall = reinstall
 | 
			
		||||
      @upgrade = upgrade
 | 
			
		||||
      @installed_as_dependency = installed_as_dependency
 | 
			
		||||
      @installed_on_request = installed_on_request
 | 
			
		||||
      @quarantine = quarantine
 | 
			
		||||
      @verify_download_integrity = verify_download_integrity
 | 
			
		||||
      @quiet = quiet
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    attr_predicate :binaries?, :force?, :adopt?, :skip_cask_deps?, :require_sha?,
 | 
			
		||||
                   :reinstall?, :upgrade?, :verbose?, :zap?, :installed_as_dependency?,
 | 
			
		||||
                   :reinstall?, :upgrade?, :verbose?, :zap?, :installed_as_dependency?, :installed_on_request?,
 | 
			
		||||
                   :quarantine?, :quiet?
 | 
			
		||||
 | 
			
		||||
    def self.caveats(cask)
 | 
			
		||||
@ -112,6 +114,11 @@ module Cask
 | 
			
		||||
 | 
			
		||||
      install_artifacts(predecessor:)
 | 
			
		||||
 | 
			
		||||
      tab = Tab.create(@cask)
 | 
			
		||||
      tab.installed_as_dependency = installed_as_dependency?
 | 
			
		||||
      tab.installed_on_request = installed_on_request?
 | 
			
		||||
      tab.write
 | 
			
		||||
 | 
			
		||||
      if (tap = @cask.tap) && tap.should_report_analytics?
 | 
			
		||||
        ::Utils::Analytics.report_package_event(:cask_install, package_name: @cask.token, tap_name: tap.name,
 | 
			
		||||
on_request: true)
 | 
			
		||||
@ -356,6 +363,7 @@ on_request: true)
 | 
			
		||||
            binaries:                binaries?,
 | 
			
		||||
            verbose:                 verbose?,
 | 
			
		||||
            installed_as_dependency: true,
 | 
			
		||||
            installed_on_request:    false,
 | 
			
		||||
            force:                   false,
 | 
			
		||||
          ).install
 | 
			
		||||
        else
 | 
			
		||||
@ -408,6 +416,7 @@ on_request: true)
 | 
			
		||||
      oh1 "Uninstalling Cask #{Formatter.identifier(@cask)}"
 | 
			
		||||
      uninstall_artifacts(clear: true, successor:)
 | 
			
		||||
      if !reinstall? && !upgrade?
 | 
			
		||||
        remove_tabfile
 | 
			
		||||
        remove_download_sha
 | 
			
		||||
        remove_config_file
 | 
			
		||||
      end
 | 
			
		||||
@ -415,6 +424,12 @@ on_request: true)
 | 
			
		||||
      purge_caskroom_path if force?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def remove_tabfile
 | 
			
		||||
      tabfile = @cask.tab.tabfile
 | 
			
		||||
      FileUtils.rm_f tabfile if tabfile
 | 
			
		||||
      @cask.config_path.parent.rmdir_if_possible
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def remove_config_file
 | 
			
		||||
      FileUtils.rm_f @cask.config_path
 | 
			
		||||
      @cask.config_path.parent.rmdir_if_possible
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								Library/Homebrew/cask/tab.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								Library/Homebrew/cask/tab.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "tab"
 | 
			
		||||
 | 
			
		||||
module Cask
 | 
			
		||||
  class Tab < ::AbstractTab
 | 
			
		||||
    attr_accessor :uninstall_flight_blocks, :uninstall_artifacts
 | 
			
		||||
 | 
			
		||||
    # Instantiates a {Tab} for a new installation of a cask.
 | 
			
		||||
    def self.create(cask)
 | 
			
		||||
      tab = super
 | 
			
		||||
 | 
			
		||||
      tab.tabfile = cask.metadata_main_container_path/FILENAME
 | 
			
		||||
      tab.uninstall_flight_blocks = cask.uninstall_flight_blocks?
 | 
			
		||||
      tab.runtime_dependencies = Tab.runtime_deps_hash(cask)
 | 
			
		||||
      tab.source["version"] = cask.version.to_s
 | 
			
		||||
      tab.source["path"] = cask.sourcefile_path.to_s
 | 
			
		||||
      tab.uninstall_artifacts = cask.artifacts_list(uninstall_only: true)
 | 
			
		||||
 | 
			
		||||
      tab
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Returns a {Tab} for an already installed cask,
 | 
			
		||||
    # or a fake one if the cask is not installed.
 | 
			
		||||
    def self.for_cask(cask)
 | 
			
		||||
      path = cask.metadata_main_container_path/FILENAME
 | 
			
		||||
 | 
			
		||||
      return from_file(path) if path.exist?
 | 
			
		||||
 | 
			
		||||
      tab = empty
 | 
			
		||||
      tab.source = {
 | 
			
		||||
        "path"         => cask.sourcefile_path.to_s,
 | 
			
		||||
        "tap"          => cask.tap&.name,
 | 
			
		||||
        "tap_git_head" => cask.tap_git_head,
 | 
			
		||||
        "version"      => cask.version.to_s,
 | 
			
		||||
      }
 | 
			
		||||
      tab.uninstall_artifacts = cask.artifacts_list(uninstall_only: true)
 | 
			
		||||
 | 
			
		||||
      tab
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.empty
 | 
			
		||||
      tab = super
 | 
			
		||||
      tab.uninstall_flight_blocks = false
 | 
			
		||||
      tab.uninstall_artifacts = []
 | 
			
		||||
      tab.source["version"] = nil
 | 
			
		||||
 | 
			
		||||
      tab
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.runtime_deps_hash(cask)
 | 
			
		||||
      cask_and_formula_dep_graph = ::Utils::TopologicalHash.graph_package_dependencies(cask)
 | 
			
		||||
      cask_deps, formula_deps = cask_and_formula_dep_graph.values.flatten.uniq.partition do |dep|
 | 
			
		||||
        dep.is_a?(Cask)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      runtime_deps = {}
 | 
			
		||||
 | 
			
		||||
      if cask_deps.any?
 | 
			
		||||
        runtime_deps[:cask] = cask_deps.map do |dep|
 | 
			
		||||
          {
 | 
			
		||||
            "full_name"         => dep.full_name,
 | 
			
		||||
            "version"           => dep.version.to_s,
 | 
			
		||||
            "declared_directly" => cask.depends_on.cask.include?(dep.full_name),
 | 
			
		||||
          }
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if formula_deps.any?
 | 
			
		||||
        runtime_deps[:formula] = formula_deps.map do |dep|
 | 
			
		||||
          formula_to_dep_hash(dep, cask.depends_on.formula)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      runtime_deps
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def version
 | 
			
		||||
      source["version"]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def to_json(*_args)
 | 
			
		||||
      attributes = {
 | 
			
		||||
        "homebrew_version"        => homebrew_version,
 | 
			
		||||
        "loaded_from_api"         => loaded_from_api,
 | 
			
		||||
        "uninstall_flight_blocks" => uninstall_flight_blocks,
 | 
			
		||||
        "installed_as_dependency" => installed_as_dependency,
 | 
			
		||||
        "installed_on_request"    => installed_on_request,
 | 
			
		||||
        "time"                    => time,
 | 
			
		||||
        "runtime_dependencies"    => runtime_dependencies,
 | 
			
		||||
        "source"                  => source,
 | 
			
		||||
        "arch"                    => arch,
 | 
			
		||||
        "uninstall_artifacts"     => uninstall_artifacts,
 | 
			
		||||
        "built_on"                => built_on,
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      JSON.pretty_generate(attributes)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def to_s
 | 
			
		||||
      s = ["Installed"]
 | 
			
		||||
      s << "using the formulae.brew.sh API" if loaded_from_api
 | 
			
		||||
      s << Time.at(time).strftime("on %Y-%m-%d at %H:%M:%S") if time
 | 
			
		||||
      s.join(" ")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -10,7 +10,7 @@ module Homebrew
 | 
			
		||||
    class TabCmd < AbstractCommand
 | 
			
		||||
      cmd_args do
 | 
			
		||||
        description <<~EOS
 | 
			
		||||
          Edit tab information for installed formulae.
 | 
			
		||||
          Edit tab information for installed formulae or casks.
 | 
			
		||||
 | 
			
		||||
          This can be useful when you want to control whether an installed
 | 
			
		||||
          formula should be removed by `brew autoremove`.
 | 
			
		||||
@ -19,13 +19,18 @@ module Homebrew
 | 
			
		||||
        EOS
 | 
			
		||||
 | 
			
		||||
        switch "--installed-on-request",
 | 
			
		||||
               description: "Mark <formula> as installed on request."
 | 
			
		||||
               description: "Mark <installed_formula> or <installed_cask> as installed on request."
 | 
			
		||||
        switch "--no-installed-on-request",
 | 
			
		||||
               description: "Mark <formula> as not installed on request."
 | 
			
		||||
               description: "Mark <installed_formula> or <installed_cask> as not installed on request."
 | 
			
		||||
        switch "--formula", "--formulae",
 | 
			
		||||
               description: "Only mark formulae."
 | 
			
		||||
        switch "--cask", "--casks",
 | 
			
		||||
               description: "Only mark casks."
 | 
			
		||||
 | 
			
		||||
        conflicts "--formula", "--cask"
 | 
			
		||||
        conflicts "--installed-on-request", "--no-installed-on-request"
 | 
			
		||||
 | 
			
		||||
        named_args :formula, min: 1
 | 
			
		||||
        named_args [:installed_formula, :installed_cask], min: 1
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { override.void }
 | 
			
		||||
@ -37,38 +42,45 @@ module Homebrew
 | 
			
		||||
        end
 | 
			
		||||
        raise UsageError, "No marking option specified." if installed_on_request.nil?
 | 
			
		||||
 | 
			
		||||
        formulae = args.named.to_formulae
 | 
			
		||||
        if (formulae_not_installed = formulae.reject(&:any_version_installed?)).any?
 | 
			
		||||
          formula_names = formulae_not_installed.map(&:name)
 | 
			
		||||
          is_or_are = (formula_names.length == 1) ? "is" : "are"
 | 
			
		||||
          odie "#{formula_names.to_sentence} #{is_or_are} not installed."
 | 
			
		||||
        formulae, casks = args.named.to_formulae_to_casks
 | 
			
		||||
        formulae_not_installed = formulae.reject(&:any_version_installed?)
 | 
			
		||||
        casks_not_installed = casks.reject(&:installed?)
 | 
			
		||||
        if formulae_not_installed.any? || casks_not_installed.any?
 | 
			
		||||
          names = formulae_not_installed.map(&:name) + casks_not_installed.map(&:token)
 | 
			
		||||
          is_or_are = (names.length == 1) ? "is" : "are"
 | 
			
		||||
          odie "#{names.to_sentence} #{is_or_are} not installed."
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        formulae.each do |formula|
 | 
			
		||||
          update_tab formula, installed_on_request:
 | 
			
		||||
        [*formulae, *casks].each do |formula_or_cask|
 | 
			
		||||
          update_tab formula_or_cask, installed_on_request:
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      sig { params(formula: Formula, installed_on_request: T::Boolean).void }
 | 
			
		||||
      def update_tab(formula, installed_on_request:)
 | 
			
		||||
        tab = Tab.for_formula(formula)
 | 
			
		||||
        unless tab.tabfile.exist?
 | 
			
		||||
      sig { params(formula_or_cask: T.any(Formula, Cask::Cask), installed_on_request: T::Boolean).void }
 | 
			
		||||
      def update_tab(formula_or_cask, installed_on_request:)
 | 
			
		||||
        name, tab = if formula_or_cask.is_a?(Formula)
 | 
			
		||||
          [formula_or_cask.name, Tab.for_formula(formula_or_cask)]
 | 
			
		||||
        else
 | 
			
		||||
          [formula_or_cask.token, formula_or_cask.tab]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        if tab.tabfile.blank? || !tab.tabfile.exist?
 | 
			
		||||
          raise ArgumentError,
 | 
			
		||||
                "Tab file for #{formula.name} does not exist."
 | 
			
		||||
                "Tab file for #{name} does not exist."
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        installed_on_request_str = "#{"not " unless installed_on_request}installed on request"
 | 
			
		||||
        if (tab.installed_on_request && installed_on_request) ||
 | 
			
		||||
           (!tab.installed_on_request && !installed_on_request)
 | 
			
		||||
          ohai "#{formula.name} is already marked as #{installed_on_request_str}."
 | 
			
		||||
          ohai "#{name} is already marked as #{installed_on_request_str}."
 | 
			
		||||
          return
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        tab.installed_on_request = installed_on_request
 | 
			
		||||
        tab.write
 | 
			
		||||
        ohai "#{formula.name} is now marked as #{installed_on_request_str}."
 | 
			
		||||
        ohai "#{name} is now marked as #{installed_on_request_str}."
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -524,7 +524,7 @@ module Homebrew
 | 
			
		||||
            tab.time = nil
 | 
			
		||||
            tab.changed_files = changed_files.dup
 | 
			
		||||
            if args.only_json_tab?
 | 
			
		||||
              tab.changed_files.delete(Pathname.new(Tab::FILENAME))
 | 
			
		||||
              tab.changed_files.delete(Pathname.new(AbstractTab::FILENAME))
 | 
			
		||||
              tab.tabfile.unlink
 | 
			
		||||
            else
 | 
			
		||||
              tab.write
 | 
			
		||||
 | 
			
		||||
@ -637,7 +637,7 @@ class Formula
 | 
			
		||||
  # If at least one version of {Formula} is installed.
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def any_version_installed?
 | 
			
		||||
    installed_prefixes.any? { |keg| (keg/Tab::FILENAME).file? }
 | 
			
		||||
    installed_prefixes.any? { |keg| (keg/AbstractTab::FILENAME).file? }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # The link status symlink directory for this {Formula}.
 | 
			
		||||
 | 
			
		||||
@ -344,6 +344,9 @@ module Cask
 | 
			
		||||
    sig { returns(T::Boolean) }
 | 
			
		||||
    def installed_as_dependency?; end
 | 
			
		||||
 | 
			
		||||
    sig { returns(T::Boolean) }
 | 
			
		||||
    def installed_on_request?; end
 | 
			
		||||
 | 
			
		||||
    sig { returns(T::Boolean) }
 | 
			
		||||
    def quarantine?; end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,78 +8,48 @@ require "development_tools"
 | 
			
		||||
require "extend/cachable"
 | 
			
		||||
 | 
			
		||||
# Rather than calling `new` directly, use one of the class methods like {Tab.create}.
 | 
			
		||||
class Tab
 | 
			
		||||
class AbstractTab
 | 
			
		||||
  extend Cachable
 | 
			
		||||
 | 
			
		||||
  FILENAME = "INSTALL_RECEIPT.json"
 | 
			
		||||
 | 
			
		||||
  # Check whether the formula was installed as a dependency.
 | 
			
		||||
  # Check whether the formula or cask was installed as a dependency.
 | 
			
		||||
  #
 | 
			
		||||
  # @api internal
 | 
			
		||||
  attr_accessor :installed_as_dependency
 | 
			
		||||
 | 
			
		||||
  # Check whether the formula was installed on request.
 | 
			
		||||
  # Check whether the formula or cask was installed on request.
 | 
			
		||||
  #
 | 
			
		||||
  # @api internal
 | 
			
		||||
  attr_accessor :installed_on_request
 | 
			
		||||
 | 
			
		||||
  # Check whether the formula was poured from a bottle.
 | 
			
		||||
  attr_accessor :homebrew_version, :tabfile, :loaded_from_api, :time, :arch, :source, :built_on
 | 
			
		||||
 | 
			
		||||
  # Returns the formula or cask runtime dependencies.
 | 
			
		||||
  #
 | 
			
		||||
  # @api internal
 | 
			
		||||
  attr_accessor :poured_from_bottle
 | 
			
		||||
  attr_accessor :runtime_dependencies
 | 
			
		||||
 | 
			
		||||
  attr_accessor :homebrew_version, :tabfile, :built_as_bottle,
 | 
			
		||||
                :changed_files, :loaded_from_api, :time, :stdlib, :aliases, :arch, :source,
 | 
			
		||||
                :built_on
 | 
			
		||||
  attr_writer :used_options, :unused_options, :compiler, :source_modified_time
 | 
			
		||||
 | 
			
		||||
  # Returns the formula's runtime dependencies.
 | 
			
		||||
  #
 | 
			
		||||
  # @api internal
 | 
			
		||||
  attr_writer :runtime_dependencies
 | 
			
		||||
 | 
			
		||||
  # Instantiates a {Tab} for a new installation of a formula.
 | 
			
		||||
  def self.create(formula, compiler, stdlib)
 | 
			
		||||
    build = formula.build
 | 
			
		||||
    runtime_deps = formula.runtime_dependencies(undeclared: false)
 | 
			
		||||
  # Instantiates a {Tab} for a new installation of a formula or cask.
 | 
			
		||||
  def self.create(formula_or_cask)
 | 
			
		||||
    attributes = {
 | 
			
		||||
      "homebrew_version"        => HOMEBREW_VERSION,
 | 
			
		||||
      "used_options"            => build.used_options.as_flags,
 | 
			
		||||
      "unused_options"          => build.unused_options.as_flags,
 | 
			
		||||
      "tabfile"                 => formula.prefix/FILENAME,
 | 
			
		||||
      "built_as_bottle"         => build.bottle?,
 | 
			
		||||
      "installed_as_dependency" => false,
 | 
			
		||||
      "installed_on_request"    => false,
 | 
			
		||||
      "poured_from_bottle"      => false,
 | 
			
		||||
      "loaded_from_api"         => false,
 | 
			
		||||
      "loaded_from_api"         => formula_or_cask.loaded_from_api?,
 | 
			
		||||
      "time"                    => Time.now.to_i,
 | 
			
		||||
      "source_modified_time"    => formula.source_modified_time.to_i,
 | 
			
		||||
      "compiler"                => compiler,
 | 
			
		||||
      "stdlib"                  => stdlib,
 | 
			
		||||
      "aliases"                 => formula.aliases,
 | 
			
		||||
      "runtime_dependencies"    => Tab.runtime_deps_hash(formula, runtime_deps),
 | 
			
		||||
      "arch"                    => Hardware::CPU.arch,
 | 
			
		||||
      "source"                  => {
 | 
			
		||||
        "path"         => formula.specified_path.to_s,
 | 
			
		||||
        "tap"          => formula.tap&.name,
 | 
			
		||||
        "tap_git_head" => nil, # Filled in later if possible
 | 
			
		||||
        "spec"         => formula.active_spec_sym.to_s,
 | 
			
		||||
        "versions"     => {
 | 
			
		||||
          "stable"         => formula.stable&.version&.to_s,
 | 
			
		||||
          "head"           => formula.head&.version&.to_s,
 | 
			
		||||
          "version_scheme" => formula.version_scheme,
 | 
			
		||||
        },
 | 
			
		||||
        "tap"          => formula_or_cask.tap&.name,
 | 
			
		||||
        "tap_git_head" => formula_or_cask.tap_git_head,
 | 
			
		||||
      },
 | 
			
		||||
      "built_on"                => DevelopmentTools.build_system_info,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # We can only get `tap_git_head` if the tap is installed locally
 | 
			
		||||
    attributes["source"]["tap_git_head"] = formula.tap.git_head if formula.tap&.installed?
 | 
			
		||||
 | 
			
		||||
    new(attributes)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Returns the {Tab} for an install receipt at `path`.
 | 
			
		||||
  # Returns the {Tab} for a formula or cask install receipt at `path`.
 | 
			
		||||
  #
 | 
			
		||||
  # NOTE: Results are cached.
 | 
			
		||||
  def self.from_file(path)
 | 
			
		||||
@ -99,42 +69,132 @@ class Tab
 | 
			
		||||
      raise e, "Cannot parse #{path}: #{e}", e.backtrace
 | 
			
		||||
    end
 | 
			
		||||
    attributes["tabfile"] = path
 | 
			
		||||
    attributes["source_modified_time"] ||= 0
 | 
			
		||||
    attributes["source"] ||= {}
 | 
			
		||||
 | 
			
		||||
    tapped_from = attributes["tapped_from"]
 | 
			
		||||
    if !tapped_from.nil? && tapped_from != "path or URL"
 | 
			
		||||
      attributes["source"]["tap"] = attributes.delete("tapped_from")
 | 
			
		||||
    end
 | 
			
		||||
    new(attributes)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
    if attributes["source"]["tap"] == "mxcl/master" ||
 | 
			
		||||
       attributes["source"]["tap"] == "Homebrew/homebrew"
 | 
			
		||||
      attributes["source"]["tap"] = "homebrew/core"
 | 
			
		||||
    end
 | 
			
		||||
  def self.empty
 | 
			
		||||
    attributes = {
 | 
			
		||||
      "homebrew_version"        => HOMEBREW_VERSION,
 | 
			
		||||
      "installed_as_dependency" => false,
 | 
			
		||||
      "installed_on_request"    => false,
 | 
			
		||||
      "loaded_from_api"         => false,
 | 
			
		||||
      "time"                    => nil,
 | 
			
		||||
      "runtime_dependencies"    => nil,
 | 
			
		||||
      "arch"                    => nil,
 | 
			
		||||
      "source"                  => {
 | 
			
		||||
        "path"         => nil,
 | 
			
		||||
        "tap"          => nil,
 | 
			
		||||
        "tap_git_head" => nil,
 | 
			
		||||
      },
 | 
			
		||||
      "built_on"                => DevelopmentTools.generic_build_system_info,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if attributes["source"]["spec"].nil?
 | 
			
		||||
    new(attributes)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.formula_to_dep_hash(formula, declared_deps)
 | 
			
		||||
    {
 | 
			
		||||
      "full_name"         => formula.full_name,
 | 
			
		||||
      "version"           => formula.version.to_s,
 | 
			
		||||
      "revision"          => formula.revision,
 | 
			
		||||
      "pkg_version"       => formula.pkg_version.to_s,
 | 
			
		||||
      "declared_directly" => declared_deps.include?(formula.full_name),
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
  private_class_method :formula_to_dep_hash
 | 
			
		||||
 | 
			
		||||
  def initialize(attributes = {})
 | 
			
		||||
    attributes.each { |key, value| instance_variable_set(:"@#{key}", value) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def parsed_homebrew_version
 | 
			
		||||
    return Version::NULL if homebrew_version.nil?
 | 
			
		||||
 | 
			
		||||
    Version.new(homebrew_version)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T.nilable(Tap)) }
 | 
			
		||||
  def tap
 | 
			
		||||
    tap_name = source["tap"]
 | 
			
		||||
    Tap.fetch(tap_name) if tap_name
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def tap=(tap)
 | 
			
		||||
    tap_name = tap.respond_to?(:name) ? tap.name : tap
 | 
			
		||||
    source["tap"] = tap_name
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def write
 | 
			
		||||
    self.class.cache[tabfile] = self
 | 
			
		||||
    tabfile.atomic_write(to_json)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Tab < AbstractTab
 | 
			
		||||
  # Check whether the formula was poured from a bottle.
 | 
			
		||||
  #
 | 
			
		||||
  # @api internal
 | 
			
		||||
  attr_accessor :poured_from_bottle
 | 
			
		||||
 | 
			
		||||
  attr_accessor :built_as_bottle, :changed_files, :stdlib, :aliases
 | 
			
		||||
  attr_writer :used_options, :unused_options, :compiler, :source_modified_time
 | 
			
		||||
  attr_reader :tapped_from
 | 
			
		||||
 | 
			
		||||
  # Instantiates a {Tab} for a new installation of a formula.
 | 
			
		||||
  def self.create(formula, compiler, stdlib)
 | 
			
		||||
    tab = super(formula)
 | 
			
		||||
    build = formula.build
 | 
			
		||||
    runtime_deps = formula.runtime_dependencies(undeclared: false)
 | 
			
		||||
 | 
			
		||||
    tab.used_options = build.used_options.as_flags
 | 
			
		||||
    tab.unused_options = build.unused_options.as_flags
 | 
			
		||||
    tab.tabfile = formula.prefix/FILENAME
 | 
			
		||||
    tab.built_as_bottle = build.bottle?
 | 
			
		||||
    tab.poured_from_bottle = false
 | 
			
		||||
    tab.source_modified_time = formula.source_modified_time.to_i
 | 
			
		||||
    tab.compiler = compiler
 | 
			
		||||
    tab.stdlib = stdlib
 | 
			
		||||
    tab.aliases = formula.aliases
 | 
			
		||||
    tab.runtime_dependencies = Tab.runtime_deps_hash(formula, runtime_deps)
 | 
			
		||||
    tab.source["spec"] = formula.active_spec_sym.to_s
 | 
			
		||||
    tab.source["path"] = formula.specified_path.to_s
 | 
			
		||||
    tab.source["versions"] = {
 | 
			
		||||
      "stable"         => formula.stable&.version&.to_s,
 | 
			
		||||
      "head"           => formula.head&.version&.to_s,
 | 
			
		||||
      "version_scheme" => formula.version_scheme,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tab
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Like {from_file}, but bypass the cache.
 | 
			
		||||
  def self.from_file_content(content, path)
 | 
			
		||||
    tab = super
 | 
			
		||||
 | 
			
		||||
    tab.source_modified_time ||= 0
 | 
			
		||||
    tab.source ||= {}
 | 
			
		||||
 | 
			
		||||
    tab.tap = tab.tapped_from if !tab.tapped_from.nil? && tab.tapped_from != "path or URL"
 | 
			
		||||
    tab.tap = "homebrew/core" if tab.tap == "mxcl/master" || tab.tap == "Homebrew/homebrew"
 | 
			
		||||
 | 
			
		||||
    if tab.source["spec"].nil?
 | 
			
		||||
      version = PkgVersion.parse(File.basename(File.dirname(path)))
 | 
			
		||||
      attributes["source"]["spec"] = if version.head?
 | 
			
		||||
      tab.source["spec"] = if version.head?
 | 
			
		||||
        "head"
 | 
			
		||||
      else
 | 
			
		||||
        "stable"
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if attributes["source"]["versions"].nil?
 | 
			
		||||
      attributes["source"]["versions"] = {
 | 
			
		||||
        "stable"         => nil,
 | 
			
		||||
        "head"           => nil,
 | 
			
		||||
        "version_scheme" => 0,
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
    tab.source["versions"] ||= empty_source_versions
 | 
			
		||||
 | 
			
		||||
    # Tabs created with Homebrew 1.5.13 through 4.0.17 inclusive created empty string versions in some cases.
 | 
			
		||||
    ["stable", "head"].each do |spec|
 | 
			
		||||
      attributes["source"]["versions"][spec] = attributes["source"]["versions"][spec].presence
 | 
			
		||||
      tab.source["versions"][spec] = tab.source["versions"][spec].presence
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    new(attributes)
 | 
			
		||||
    tab
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Get the {Tab} for the given {Keg},
 | 
			
		||||
@ -198,10 +258,11 @@ class Tab
 | 
			
		||||
      tab = empty
 | 
			
		||||
      tab.unused_options = formula.options.as_flags
 | 
			
		||||
      tab.source = {
 | 
			
		||||
        "path"     => formula.specified_path.to_s,
 | 
			
		||||
        "tap"      => formula.tap&.name,
 | 
			
		||||
        "spec"     => formula.active_spec_sym.to_s,
 | 
			
		||||
        "versions" => {
 | 
			
		||||
        "path"         => formula.specified_path.to_s,
 | 
			
		||||
        "tap"          => formula.tap&.name,
 | 
			
		||||
        "tap_git_head" => formula.tap_git_head,
 | 
			
		||||
        "spec"         => formula.active_spec_sym.to_s,
 | 
			
		||||
        "versions"     => {
 | 
			
		||||
          "stable"         => formula.stable&.version&.to_s,
 | 
			
		||||
          "head"           => formula.head&.version&.to_s,
 | 
			
		||||
          "version_scheme" => formula.version_scheme,
 | 
			
		||||
@ -213,56 +274,37 @@ class Tab
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.empty
 | 
			
		||||
    attributes = {
 | 
			
		||||
      "homebrew_version"        => HOMEBREW_VERSION,
 | 
			
		||||
      "used_options"            => [],
 | 
			
		||||
      "unused_options"          => [],
 | 
			
		||||
      "built_as_bottle"         => false,
 | 
			
		||||
      "installed_as_dependency" => false,
 | 
			
		||||
      "installed_on_request"    => false,
 | 
			
		||||
      "poured_from_bottle"      => false,
 | 
			
		||||
      "loaded_from_api"         => false,
 | 
			
		||||
      "time"                    => nil,
 | 
			
		||||
      "source_modified_time"    => 0,
 | 
			
		||||
      "stdlib"                  => nil,
 | 
			
		||||
      "compiler"                => DevelopmentTools.default_compiler,
 | 
			
		||||
      "aliases"                 => [],
 | 
			
		||||
      "runtime_dependencies"    => nil,
 | 
			
		||||
      "arch"                    => nil,
 | 
			
		||||
      "source"                  => {
 | 
			
		||||
        "path"         => nil,
 | 
			
		||||
        "tap"          => nil,
 | 
			
		||||
        "tap_git_head" => nil,
 | 
			
		||||
        "spec"         => "stable",
 | 
			
		||||
        "versions"     => {
 | 
			
		||||
          "stable"         => nil,
 | 
			
		||||
          "head"           => nil,
 | 
			
		||||
          "version_scheme" => 0,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      "built_on"                => DevelopmentTools.generic_build_system_info,
 | 
			
		||||
    }
 | 
			
		||||
    tab = super
 | 
			
		||||
 | 
			
		||||
    new(attributes)
 | 
			
		||||
    tab.used_options = []
 | 
			
		||||
    tab.unused_options = []
 | 
			
		||||
    tab.built_as_bottle = false
 | 
			
		||||
    tab.poured_from_bottle = false
 | 
			
		||||
    tab.source_modified_time = 0
 | 
			
		||||
    tab.stdlib = nil
 | 
			
		||||
    tab.compiler = DevelopmentTools.default_compiler
 | 
			
		||||
    tab.aliases = []
 | 
			
		||||
    tab.source["spec"] = "stable"
 | 
			
		||||
    tab.source["versions"] = empty_source_versions
 | 
			
		||||
 | 
			
		||||
    tab
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.empty_source_versions
 | 
			
		||||
    {
 | 
			
		||||
      "stable"         => nil,
 | 
			
		||||
      "head"           => nil,
 | 
			
		||||
      "version_scheme" => 0,
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
  private_class_method :empty_source_versions
 | 
			
		||||
 | 
			
		||||
  def self.runtime_deps_hash(formula, deps)
 | 
			
		||||
    deps.map do |dep|
 | 
			
		||||
      f = dep.to_formula
 | 
			
		||||
      {
 | 
			
		||||
        "full_name"         => f.full_name,
 | 
			
		||||
        "version"           => f.version.to_s,
 | 
			
		||||
        "revision"          => f.revision,
 | 
			
		||||
        "pkg_version"       => f.pkg_version.to_s,
 | 
			
		||||
        "declared_directly" => formula.deps.include?(dep),
 | 
			
		||||
      }
 | 
			
		||||
      formula_to_dep_hash(dep.to_formula, formula.deps.map(&:name))
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def initialize(attributes = {})
 | 
			
		||||
    attributes.each { |key, value| instance_variable_set(:"@#{key}", value) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def any_args_or_options?
 | 
			
		||||
    !used_options.empty? || !unused_options.empty?
 | 
			
		||||
  end
 | 
			
		||||
@ -307,12 +349,6 @@ class Tab
 | 
			
		||||
    @compiler || DevelopmentTools.default_compiler
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def parsed_homebrew_version
 | 
			
		||||
    return Version::NULL if homebrew_version.nil?
 | 
			
		||||
 | 
			
		||||
    Version.new(homebrew_version)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def runtime_dependencies
 | 
			
		||||
    # Homebrew versions prior to 1.1.6 generated incorrect runtime dependency
 | 
			
		||||
    # lists.
 | 
			
		||||
@ -333,17 +369,6 @@ class Tab
 | 
			
		||||
    built_as_bottle
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T.nilable(Tap)) }
 | 
			
		||||
  def tap
 | 
			
		||||
    tap_name = source["tap"]
 | 
			
		||||
    Tap.fetch(tap_name) if tap_name
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def tap=(tap)
 | 
			
		||||
    tap_name = tap.respond_to?(:name) ? tap.name : tap
 | 
			
		||||
    source["tap"] = tap_name
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def spec
 | 
			
		||||
    source["spec"].to_sym
 | 
			
		||||
  end
 | 
			
		||||
@ -416,8 +441,7 @@ class Tab
 | 
			
		||||
    # will no longer be valid.
 | 
			
		||||
    Formula.clear_cache unless tabfile.exist?
 | 
			
		||||
 | 
			
		||||
    self.class.cache[tabfile] = self
 | 
			
		||||
    tabfile.atomic_write(to_json)
 | 
			
		||||
    super
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(String) }
 | 
			
		||||
 | 
			
		||||
@ -212,6 +212,115 @@ RSpec.describe Cask::Cask, :cask do
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#artifacts_list" do
 | 
			
		||||
    subject(:cask) { Cask::CaskLoader.load("many-artifacts") }
 | 
			
		||||
 | 
			
		||||
    it "returns all artifacts when no options are given" do
 | 
			
		||||
      expected_artifacts = [
 | 
			
		||||
        { uninstall_preflight: nil },
 | 
			
		||||
        { preflight: nil },
 | 
			
		||||
        { uninstall: [{
 | 
			
		||||
          rmdir: "#{TEST_TMPDIR}/empty_directory_path",
 | 
			
		||||
          trash: ["#{TEST_TMPDIR}/foo", "#{TEST_TMPDIR}/bar"],
 | 
			
		||||
        }] },
 | 
			
		||||
        { pkg: ["ManyArtifacts/ManyArtifacts.pkg"] },
 | 
			
		||||
        { app: ["ManyArtifacts/ManyArtifacts.app"] },
 | 
			
		||||
        { uninstall_postflight: nil },
 | 
			
		||||
        { postflight: nil },
 | 
			
		||||
        { zap: [{
 | 
			
		||||
          rmdir: ["~/Library/Caches/ManyArtifacts", "~/Library/Application Support/ManyArtifacts"],
 | 
			
		||||
          trash: "~/Library/Logs/ManyArtifacts.log",
 | 
			
		||||
        }] },
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
      expect(cask.artifacts_list).to eq(expected_artifacts)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "skips flight blocks when compact is true" do
 | 
			
		||||
      expected_artifacts = [
 | 
			
		||||
        { uninstall: [{
 | 
			
		||||
          rmdir: "#{TEST_TMPDIR}/empty_directory_path",
 | 
			
		||||
          trash: ["#{TEST_TMPDIR}/foo", "#{TEST_TMPDIR}/bar"],
 | 
			
		||||
        }] },
 | 
			
		||||
        { pkg: ["ManyArtifacts/ManyArtifacts.pkg"] },
 | 
			
		||||
        { app: ["ManyArtifacts/ManyArtifacts.app"] },
 | 
			
		||||
        { zap: [{
 | 
			
		||||
          rmdir: ["~/Library/Caches/ManyArtifacts", "~/Library/Application Support/ManyArtifacts"],
 | 
			
		||||
          trash: "~/Library/Logs/ManyArtifacts.log",
 | 
			
		||||
        }] },
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
      expect(cask.artifacts_list(compact: true)).to eq(expected_artifacts)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns only uninstall artifacts when uninstall_only is true" do
 | 
			
		||||
      expected_artifacts = [
 | 
			
		||||
        { uninstall_preflight: nil },
 | 
			
		||||
        { uninstall: [{
 | 
			
		||||
          rmdir: "#{TEST_TMPDIR}/empty_directory_path",
 | 
			
		||||
          trash: ["#{TEST_TMPDIR}/foo", "#{TEST_TMPDIR}/bar"],
 | 
			
		||||
        }] },
 | 
			
		||||
        { app: ["ManyArtifacts/ManyArtifacts.app"] },
 | 
			
		||||
        { uninstall_postflight: nil },
 | 
			
		||||
        { zap: [{
 | 
			
		||||
          rmdir: ["~/Library/Caches/ManyArtifacts", "~/Library/Application Support/ManyArtifacts"],
 | 
			
		||||
          trash: "~/Library/Logs/ManyArtifacts.log",
 | 
			
		||||
        }] },
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
      expect(cask.artifacts_list(uninstall_only: true)).to eq(expected_artifacts)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "skips flight blocks and returns only uninstall artifacts when compact and uninstall_only are true" do
 | 
			
		||||
      expected_artifacts = [
 | 
			
		||||
        { uninstall: [{
 | 
			
		||||
          rmdir: "#{TEST_TMPDIR}/empty_directory_path",
 | 
			
		||||
          trash: ["#{TEST_TMPDIR}/foo", "#{TEST_TMPDIR}/bar"],
 | 
			
		||||
        }] },
 | 
			
		||||
        { app: ["ManyArtifacts/ManyArtifacts.app"] },
 | 
			
		||||
        { zap: [{
 | 
			
		||||
          rmdir: ["~/Library/Caches/ManyArtifacts", "~/Library/Application Support/ManyArtifacts"],
 | 
			
		||||
          trash: "~/Library/Logs/ManyArtifacts.log",
 | 
			
		||||
        }] },
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
      expect(cask.artifacts_list(compact: true, uninstall_only: true)).to eq(expected_artifacts)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#uninstall_flight_blocks?" do
 | 
			
		||||
    matcher :have_uninstall_flight_blocks do
 | 
			
		||||
      match do |actual|
 | 
			
		||||
        actual.uninstall_flight_blocks? == true
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns true when there are uninstall_preflight blocks" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-uninstall-preflight")
 | 
			
		||||
      expect(cask).to have_uninstall_flight_blocks
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns true when there are uninstall_postflight blocks" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-uninstall-postflight")
 | 
			
		||||
      expect(cask).to have_uninstall_flight_blocks
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns false when there are only preflight blocks" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-preflight")
 | 
			
		||||
      expect(cask).not_to have_uninstall_flight_blocks
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns false when there are only postflight blocks" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-postflight")
 | 
			
		||||
      expect(cask).not_to have_uninstall_flight_blocks
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns false when there are no flight blocks" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("local-caffeine")
 | 
			
		||||
      expect(cask).not_to have_uninstall_flight_blocks
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#to_h" do
 | 
			
		||||
    let(:expected_json) { (TEST_FIXTURE_DIR/"cask/everything.json").read.strip }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -121,4 +121,30 @@ RSpec.describe Cask::Info, :cask do
 | 
			
		||||
      Caffeine.app (App)
 | 
			
		||||
    EOS
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "prints install information for an installed Cask" do
 | 
			
		||||
    cask = Cask::CaskLoader.load("local-transmission")
 | 
			
		||||
    time = 1_720_189_863
 | 
			
		||||
    tab = Cask::Tab.new(loaded_from_api: true, tabfile: TEST_FIXTURE_DIR/"cask_receipt.json", time:)
 | 
			
		||||
    expect(cask).to receive(:installed?).and_return(true)
 | 
			
		||||
    expect(cask).to receive(:installed_version).and_return("2.61")
 | 
			
		||||
    expect(Cask::Tab).to receive(:for_cask).with(cask).and_return(tab)
 | 
			
		||||
 | 
			
		||||
    expect do
 | 
			
		||||
      described_class.info(cask)
 | 
			
		||||
    end.to output(<<~EOS).to_stdout
 | 
			
		||||
      ==> local-transmission: 2.61
 | 
			
		||||
      https://transmissionbt.com/
 | 
			
		||||
      Installed
 | 
			
		||||
      #{HOMEBREW_PREFIX}/Caskroom/local-transmission/2.61 (does not exist)
 | 
			
		||||
        Installed using the formulae.brew.sh API on #{Time.at(time).strftime("%Y-%m-%d at %H:%M:%S")}
 | 
			
		||||
      From: https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/l/local-transmission.rb
 | 
			
		||||
      ==> Name
 | 
			
		||||
      Transmission
 | 
			
		||||
      ==> Description
 | 
			
		||||
      BitTorrent client
 | 
			
		||||
      ==> Artifacts
 | 
			
		||||
      Transmission.app (App)
 | 
			
		||||
    EOS
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										356
									
								
								Library/Homebrew/test/cask/tab_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								Library/Homebrew/test/cask/tab_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,356 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "cask"
 | 
			
		||||
 | 
			
		||||
RSpec.describe Cask::Tab, :cask do
 | 
			
		||||
  matcher :be_installed_as_dependency do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.installed_as_dependency == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  matcher :be_installed_on_request do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.installed_on_request == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  matcher :be_loaded_from_api do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.loaded_from_api == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  matcher :have_uninstall_flight_blocks do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.uninstall_flight_blocks == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  subject(:tab) do
 | 
			
		||||
    described_class.new(
 | 
			
		||||
      "homebrew_version"        => HOMEBREW_VERSION,
 | 
			
		||||
      "loaded_from_api"         => false,
 | 
			
		||||
      "uninstall_flight_blocks" => true,
 | 
			
		||||
      "installed_as_dependency" => false,
 | 
			
		||||
      "installed_on_request"    => true,
 | 
			
		||||
      "time"                    => time,
 | 
			
		||||
      "runtime_dependencies"    => {
 | 
			
		||||
        "cask" => [{ "full_name" => "bar", "version" => "2.0", "declared_directly" => false }],
 | 
			
		||||
      },
 | 
			
		||||
      "source"                  => {
 | 
			
		||||
        "path"         => CoreCaskTap.instance.path.to_s,
 | 
			
		||||
        "tap"          => CoreCaskTap.instance.to_s,
 | 
			
		||||
        "tap_git_head" => "8b79aa759500f0ffdf65a23e12950cbe3bf8fe17",
 | 
			
		||||
        "version"      => "1.2.3",
 | 
			
		||||
      },
 | 
			
		||||
      "arch"                    => Hardware::CPU.arch,
 | 
			
		||||
      "uninstall_artifacts"     => [{ "app" => ["Foo.app"] }],
 | 
			
		||||
      "built_on"                => DevelopmentTools.build_system_info,
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:time) { Time.now.to_i }
 | 
			
		||||
 | 
			
		||||
  let(:f) { formula { url "foo-1.0" } }
 | 
			
		||||
  let(:f_tab_path) { f.prefix/"INSTALL_RECEIPT.json" }
 | 
			
		||||
  let(:f_tab_content) { (TEST_FIXTURE_DIR/"receipt.json").read }
 | 
			
		||||
 | 
			
		||||
  specify "defaults" do
 | 
			
		||||
    stub_const("HOMEBREW_VERSION", "4.3.7")
 | 
			
		||||
 | 
			
		||||
    tab = described_class.empty
 | 
			
		||||
 | 
			
		||||
    expect(tab.homebrew_version).to eq(HOMEBREW_VERSION)
 | 
			
		||||
    expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
    expect(tab).not_to be_installed_on_request
 | 
			
		||||
    expect(tab).not_to be_loaded_from_api
 | 
			
		||||
    expect(tab).not_to have_uninstall_flight_blocks
 | 
			
		||||
    expect(tab.tap).to be_nil
 | 
			
		||||
    expect(tab.time).to be_nil
 | 
			
		||||
    expect(tab.runtime_dependencies).to be_nil
 | 
			
		||||
    expect(tab.source["path"]).to be_nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  specify "#runtime_dependencies" do
 | 
			
		||||
    tab = described_class.new
 | 
			
		||||
    expect(tab.runtime_dependencies).to be_nil
 | 
			
		||||
 | 
			
		||||
    tab.runtime_dependencies = {}
 | 
			
		||||
    expect(tab.runtime_dependencies).not_to be_nil
 | 
			
		||||
 | 
			
		||||
    tab.runtime_dependencies = {
 | 
			
		||||
      "cask" => [{ "full_name" => "bar", "version" => "2.0", "declared_directly" => false }],
 | 
			
		||||
    }
 | 
			
		||||
    expect(tab.runtime_dependencies).not_to be_nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::runtime_deps_hash" do
 | 
			
		||||
    specify "with no dependencies" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("local-transmission")
 | 
			
		||||
 | 
			
		||||
      expect(described_class.runtime_deps_hash(cask)).to eq({})
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    specify "with cask dependencies" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-depends-on-cask")
 | 
			
		||||
 | 
			
		||||
      expected_hash = {
 | 
			
		||||
        cask: [
 | 
			
		||||
          { "full_name"=>"local-transmission", "version"=>"2.61", "declared_directly"=>true },
 | 
			
		||||
        ],
 | 
			
		||||
      }
 | 
			
		||||
      expect(described_class.runtime_deps_hash(cask)).to eq(expected_hash)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "ignores macos symbol dependencies" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-depends-on-macos-symbol")
 | 
			
		||||
 | 
			
		||||
      expect(described_class.runtime_deps_hash(cask)).to eq({})
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "ignores macos array dependencies" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-depends-on-macos-array")
 | 
			
		||||
 | 
			
		||||
      expect(described_class.runtime_deps_hash(cask)).to eq({})
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "ignores arch dependencies" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-depends-on-arch")
 | 
			
		||||
 | 
			
		||||
      expect(described_class.runtime_deps_hash(cask)).to eq({})
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    specify "with all types of dependencies" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("with-depends-on-everything")
 | 
			
		||||
 | 
			
		||||
      unar = instance_double(Formula, full_name: "unar", version: "1.2", revision: 0, pkg_version: "1.2",
 | 
			
		||||
                             deps: [], requirements: [])
 | 
			
		||||
      expect(Formulary).to receive(:factory).with("unar").and_return(unar)
 | 
			
		||||
 | 
			
		||||
      expected_hash = {
 | 
			
		||||
        cask:    [
 | 
			
		||||
          { "full_name"=>"local-caffeine", "version"=>"1.2.3", "declared_directly"=>true },
 | 
			
		||||
          { "full_name"=>"with-depends-on-cask", "version"=>"1.2.3", "declared_directly"=>true },
 | 
			
		||||
          { "full_name"=>"local-transmission", "version"=>"2.61", "declared_directly"=>false },
 | 
			
		||||
        ],
 | 
			
		||||
        formula: [
 | 
			
		||||
          { "full_name"=>"unar", "version"=>"1.2", "revision"=>0, "pkg_version"=>"1.2", "declared_directly"=>true },
 | 
			
		||||
        ],
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      runtime_deps_hash = described_class.runtime_deps_hash(cask)
 | 
			
		||||
      tab = described_class.new
 | 
			
		||||
      tab.runtime_dependencies = runtime_deps_hash
 | 
			
		||||
      expect(tab.runtime_dependencies).to eql(expected_hash)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  specify "other attributes" do
 | 
			
		||||
    expect(tab.tap.name).to eq("homebrew/cask")
 | 
			
		||||
    expect(tab.time).to eq(time)
 | 
			
		||||
    expect(tab).not_to be_loaded_from_api
 | 
			
		||||
    expect(tab).to have_uninstall_flight_blocks
 | 
			
		||||
    expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
    expect(tab).to be_installed_on_request
 | 
			
		||||
    expect(tab).not_to be_loaded_from_api
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::from_file" do
 | 
			
		||||
    it "parses a cask Tab from a file" do
 | 
			
		||||
      path = Pathname.new("#{TEST_FIXTURE_DIR}/cask_receipt.json")
 | 
			
		||||
      tab = described_class.from_file(path)
 | 
			
		||||
      source_path = "/opt/homebrew/Library/Taps/homebrew/homebrew-cask/Casks/f/foo.rb"
 | 
			
		||||
      runtime_dependencies = {
 | 
			
		||||
        "cask"    => [
 | 
			
		||||
          {
 | 
			
		||||
            "full_name"         => "bar",
 | 
			
		||||
            "version"           => "2.0",
 | 
			
		||||
            "declared_directly" => true,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "formula" => [
 | 
			
		||||
          {
 | 
			
		||||
            "full_name"         => "baz",
 | 
			
		||||
            "version"           => "3.0",
 | 
			
		||||
            "revision"          => 0,
 | 
			
		||||
            "pkg_version"       => "3.0",
 | 
			
		||||
            "declared_directly" => true,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "macos"   => {
 | 
			
		||||
          ">=" => [
 | 
			
		||||
            "12",
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      expect(tab).not_to be_loaded_from_api
 | 
			
		||||
      expect(tab).to have_uninstall_flight_blocks
 | 
			
		||||
      expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
      expect(tab).to be_installed_on_request
 | 
			
		||||
      expect(tab.time).to eq(Time.at(1_719_289_256).to_i)
 | 
			
		||||
      expect(tab.runtime_dependencies).to eq(runtime_dependencies)
 | 
			
		||||
      expect(tab.source["path"]).to eq(source_path)
 | 
			
		||||
      expect(tab.version).to eq("1.2.3")
 | 
			
		||||
      expect(tab.tap.name).to eq("homebrew/cask")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::from_file_content" do
 | 
			
		||||
    it "parses a cask Tab from a file" do
 | 
			
		||||
      path = Pathname.new("#{TEST_FIXTURE_DIR}/cask_receipt.json")
 | 
			
		||||
      tab = described_class.from_file_content(path.read, path)
 | 
			
		||||
      source_path = "/opt/homebrew/Library/Taps/homebrew/homebrew-cask/Casks/f/foo.rb"
 | 
			
		||||
      runtime_dependencies = {
 | 
			
		||||
        "cask"    => [
 | 
			
		||||
          {
 | 
			
		||||
            "full_name"         => "bar",
 | 
			
		||||
            "version"           => "2.0",
 | 
			
		||||
            "declared_directly" => true,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "formula" => [
 | 
			
		||||
          {
 | 
			
		||||
            "full_name"         => "baz",
 | 
			
		||||
            "version"           => "3.0",
 | 
			
		||||
            "revision"          => 0,
 | 
			
		||||
            "pkg_version"       => "3.0",
 | 
			
		||||
            "declared_directly" => true,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        "macos"   => {
 | 
			
		||||
          ">=" => [
 | 
			
		||||
            "12",
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      expect(tab).not_to be_loaded_from_api
 | 
			
		||||
      expect(tab).to have_uninstall_flight_blocks
 | 
			
		||||
      expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
      expect(tab).to be_installed_on_request
 | 
			
		||||
      expect(tab.tabfile).to eq(path)
 | 
			
		||||
      expect(tab.time).to eq(Time.at(1_719_289_256).to_i)
 | 
			
		||||
      expect(tab.runtime_dependencies).to eq(runtime_dependencies)
 | 
			
		||||
      expect(tab.source["path"]).to eq(source_path)
 | 
			
		||||
      expect(tab.version).to eq("1.2.3")
 | 
			
		||||
      expect(tab.tap.name).to eq("homebrew/cask")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "raises a parse exception message including the Tab filename" do
 | 
			
		||||
      expect { described_class.from_file_content("''", "cask_receipt.json") }.to raise_error(
 | 
			
		||||
        JSON::ParserError,
 | 
			
		||||
        /receipt.json:/,
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::create" do
 | 
			
		||||
    it "creates a cask Tab" do
 | 
			
		||||
      cask = Cask::CaskLoader.load("local-caffeine")
 | 
			
		||||
      expected_artifacts = [
 | 
			
		||||
        { app: ["Caffeine.app"] },
 | 
			
		||||
        { zap: [{ trash: "#{TEST_FIXTURE_DIR}/cask/caffeine/org.example.caffeine.plist" }] },
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
      tab = described_class.create(cask)
 | 
			
		||||
      expect(tab).not_to be_loaded_from_api
 | 
			
		||||
      expect(tab).not_to have_uninstall_flight_blocks
 | 
			
		||||
      expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
      expect(tab).not_to be_installed_on_request
 | 
			
		||||
      expect(tab.source).to eq({
 | 
			
		||||
        "path"         => "#{CoreCaskTap.instance.path}/Casks/local-caffeine.rb",
 | 
			
		||||
        "tap"          => CoreCaskTap.instance.name,
 | 
			
		||||
        "tap_git_head" => nil,
 | 
			
		||||
        "version"      => "1.2.3",
 | 
			
		||||
      })
 | 
			
		||||
      expect(tab.runtime_dependencies).to eq({})
 | 
			
		||||
      expect(tab.uninstall_artifacts).to eq(expected_artifacts)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::for_cask" do
 | 
			
		||||
    let(:cask) { Cask::CaskLoader.load("local-transmission") }
 | 
			
		||||
    let(:cask_tab_path) { cask.metadata_main_container_path/AbstractTab::FILENAME }
 | 
			
		||||
    let(:cask_tab_content) { (TEST_FIXTURE_DIR/"cask_receipt.json").read }
 | 
			
		||||
 | 
			
		||||
    it "creates a Tab for a given cask" do
 | 
			
		||||
      tab = described_class.for_cask(cask)
 | 
			
		||||
      expect(tab.source["path"]).to eq(cask.sourcefile_path.to_s)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "creates a Tab for a given cask with existing Tab" do
 | 
			
		||||
      cask_tab_path.dirname.mkpath
 | 
			
		||||
      cask_tab_path.write cask_tab_content
 | 
			
		||||
 | 
			
		||||
      tab = described_class.for_cask(cask)
 | 
			
		||||
      expect(tab.tabfile).to eq(cask_tab_path)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "can create a Tab for a non-existent cask" do
 | 
			
		||||
      cask_tab_path.dirname.mkpath
 | 
			
		||||
 | 
			
		||||
      tab = described_class.for_cask(cask)
 | 
			
		||||
      expect(tab.tabfile).to be_nil
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  specify "#to_json" do
 | 
			
		||||
    json_tab = described_class.new(JSON.parse(tab.to_json))
 | 
			
		||||
    expect(json_tab.homebrew_version).to eq(tab.homebrew_version)
 | 
			
		||||
    expect(json_tab.loaded_from_api).to eq(tab.loaded_from_api)
 | 
			
		||||
    expect(json_tab.uninstall_flight_blocks).to eq(tab.uninstall_flight_blocks)
 | 
			
		||||
    expect(json_tab.installed_as_dependency).to eq(tab.installed_as_dependency)
 | 
			
		||||
    expect(json_tab.installed_on_request).to eq(tab.installed_on_request)
 | 
			
		||||
    expect(json_tab.time).to eq(tab.time)
 | 
			
		||||
    expect(json_tab.runtime_dependencies).to eq(tab.runtime_dependencies)
 | 
			
		||||
    expect(json_tab.source["path"]).to eq(tab.source["path"])
 | 
			
		||||
    expect(json_tab.tap).to eq(tab.tap)
 | 
			
		||||
    expect(json_tab.source["tap_git_head"]).to eq(tab.source["tap_git_head"])
 | 
			
		||||
    expect(json_tab.version).to eq(tab.version)
 | 
			
		||||
    expect(json_tab.arch).to eq(tab.arch.to_s)
 | 
			
		||||
    expect(json_tab.uninstall_artifacts).to eq(tab.uninstall_artifacts)
 | 
			
		||||
    expect(json_tab.built_on["os"]).to eq(tab.built_on["os"])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#to_s" do
 | 
			
		||||
    let(:time_string) { Time.at(1_720_189_863).strftime("%Y-%m-%d at %H:%M:%S") }
 | 
			
		||||
 | 
			
		||||
    it "returns install information for a Tab with a time that was loaded from the API" do
 | 
			
		||||
      tab = described_class.new(
 | 
			
		||||
        loaded_from_api: true,
 | 
			
		||||
        time:            1_720_189_863,
 | 
			
		||||
      )
 | 
			
		||||
      output = "Installed using the formulae.brew.sh API on #{time_string}"
 | 
			
		||||
      expect(tab.to_s).to eq(output)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns install information for a Tab with a time that was not loaded from the API" do
 | 
			
		||||
      tab = described_class.new(
 | 
			
		||||
        loaded_from_api: false,
 | 
			
		||||
        time:            1_720_189_863,
 | 
			
		||||
      )
 | 
			
		||||
      output = "Installed on #{time_string}"
 | 
			
		||||
      expect(tab.to_s).to eq(output)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns install information for a Tab without a time that was loaded from the API" do
 | 
			
		||||
      tab = described_class.new(
 | 
			
		||||
        loaded_from_api: true,
 | 
			
		||||
        time:            nil,
 | 
			
		||||
      )
 | 
			
		||||
      output = "Installed using the formulae.brew.sh API"
 | 
			
		||||
      expect(tab.to_s).to eq(output)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns install information for a Tab without a time that was not loaded from the API" do
 | 
			
		||||
      tab = described_class.new(
 | 
			
		||||
        loaded_from_api: false,
 | 
			
		||||
        time:            nil,
 | 
			
		||||
      )
 | 
			
		||||
      output = "Installed"
 | 
			
		||||
      expect(tab.to_s).to eq(output)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -32,7 +32,7 @@ RSpec.describe Homebrew::Cmd::Deps do
 | 
			
		||||
    # Mock `Formula#any_version_installed?` by creating the tab in a plausible keg directory
 | 
			
		||||
    keg_dir = HOMEBREW_CELLAR/"installed"/"1.0"
 | 
			
		||||
    keg_dir.mkpath
 | 
			
		||||
    touch keg_dir/Tab::FILENAME
 | 
			
		||||
    touch keg_dir/AbstractTab::FILENAME
 | 
			
		||||
 | 
			
		||||
    expect { brew "deps", "baz", "--include-test", "--missing", "--skip-recommended" }
 | 
			
		||||
      .to be_a_success
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ RSpec.describe Homebrew::Cmd::Untap do
 | 
			
		||||
          keg_path = HOMEBREW_CELLAR/name/"1.2.3"
 | 
			
		||||
          keg_path.mkpath
 | 
			
		||||
 | 
			
		||||
          tab_path = keg_path/Tab::FILENAME
 | 
			
		||||
          tab_path = keg_path/AbstractTab::FILENAME
 | 
			
		||||
          tab_path.write <<~JSON
 | 
			
		||||
            {
 | 
			
		||||
              "source": {
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ RSpec.describe Homebrew::Cmd::Uses do
 | 
			
		||||
    %w[foo installed].each do |formula_name|
 | 
			
		||||
      keg_dir = HOMEBREW_CELLAR/formula_name/"1.0"
 | 
			
		||||
      keg_dir.mkpath
 | 
			
		||||
      touch keg_dir/Tab::FILENAME
 | 
			
		||||
      touch keg_dir/AbstractTab::FILENAME
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    expect { brew "uses", "foo", "--eval-all", "--include-optional", "--missing", "--recursive" }
 | 
			
		||||
 | 
			
		||||
@ -264,7 +264,7 @@ RSpec.describe Formula do
 | 
			
		||||
 | 
			
		||||
    prefix = HOMEBREW_CELLAR/f.name/"0.1"
 | 
			
		||||
    prefix.mkpath
 | 
			
		||||
    FileUtils.touch prefix/Tab::FILENAME
 | 
			
		||||
    FileUtils.touch prefix/AbstractTab::FILENAME
 | 
			
		||||
 | 
			
		||||
    expect(f).to have_any_version_installed
 | 
			
		||||
  end
 | 
			
		||||
@ -279,7 +279,7 @@ RSpec.describe Formula do
 | 
			
		||||
 | 
			
		||||
    oldname_prefix.mkpath
 | 
			
		||||
    oldname_tab = Tab.empty
 | 
			
		||||
    oldname_tab.tabfile = oldname_prefix/Tab::FILENAME
 | 
			
		||||
    oldname_tab.tabfile = oldname_prefix/AbstractTab::FILENAME
 | 
			
		||||
    oldname_tab.write
 | 
			
		||||
 | 
			
		||||
    expect(f).not_to need_migration
 | 
			
		||||
@ -346,7 +346,7 @@ RSpec.describe Formula do
 | 
			
		||||
      head_prefix.mkpath
 | 
			
		||||
 | 
			
		||||
      tab = Tab.empty
 | 
			
		||||
      tab.tabfile = head_prefix/Tab::FILENAME
 | 
			
		||||
      tab.tabfile = head_prefix/AbstractTab::FILENAME
 | 
			
		||||
      tab.source["versions"] = { "stable" => "1.0" }
 | 
			
		||||
      tab.write
 | 
			
		||||
 | 
			
		||||
@ -378,7 +378,7 @@ RSpec.describe Formula do
 | 
			
		||||
        prefix.mkpath
 | 
			
		||||
 | 
			
		||||
        tab = Tab.empty
 | 
			
		||||
        tab.tabfile = prefix/Tab::FILENAME
 | 
			
		||||
        tab.tabfile = prefix/AbstractTab::FILENAME
 | 
			
		||||
        tab.source_modified_time = stamp
 | 
			
		||||
        tab.write
 | 
			
		||||
      end
 | 
			
		||||
@ -1106,7 +1106,7 @@ RSpec.describe Formula do
 | 
			
		||||
        prefix = f.prefix(version)
 | 
			
		||||
        prefix.mkpath
 | 
			
		||||
        tab = Tab.empty
 | 
			
		||||
        tab.tabfile = prefix/Tab::FILENAME
 | 
			
		||||
        tab.tabfile = prefix/AbstractTab::FILENAME
 | 
			
		||||
        tab.source_modified_time = 1
 | 
			
		||||
        tab.write
 | 
			
		||||
      end
 | 
			
		||||
@ -1340,7 +1340,7 @@ RSpec.describe Formula do
 | 
			
		||||
    def setup_tab_for_prefix(prefix, options = {})
 | 
			
		||||
      prefix.mkpath
 | 
			
		||||
      tab = Tab.empty
 | 
			
		||||
      tab.tabfile = prefix/Tab::FILENAME
 | 
			
		||||
      tab.tabfile = prefix/AbstractTab::FILENAME
 | 
			
		||||
      tab.source["path"] = options[:path].to_s if options[:path]
 | 
			
		||||
      tab.source["tap"] = options[:tap] if options[:tap]
 | 
			
		||||
      tab.source["versions"] = options[:versions] if options[:versions]
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@ RSpec.describe InstalledDependents do
 | 
			
		||||
    def tab_dependencies(keg, deps, homebrew_version: "1.1.6")
 | 
			
		||||
      alter_tab(keg) do |tab|
 | 
			
		||||
        tab.homebrew_version = homebrew_version
 | 
			
		||||
        tab.tabfile = keg/Tab::FILENAME
 | 
			
		||||
        tab.tabfile = keg/AbstractTab::FILENAME
 | 
			
		||||
        tab.runtime_dependencies = deps
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
cask "many-artifacts" do
 | 
			
		||||
  version "1.2.3"
 | 
			
		||||
  sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
 | 
			
		||||
 | 
			
		||||
  url "file://#{TEST_FIXTURE_DIR}/cask/ManyArtifacts.zip"
 | 
			
		||||
  homepage "https://brew.sh/many-artifacts"
 | 
			
		||||
 | 
			
		||||
  app "ManyArtifacts/ManyArtifacts.app"
 | 
			
		||||
  pkg "ManyArtifacts/ManyArtifacts.pkg"
 | 
			
		||||
 | 
			
		||||
  preflight do
 | 
			
		||||
    # do nothing
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  postflight do
 | 
			
		||||
    # do nothing
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  uninstall_preflight do
 | 
			
		||||
    # do nothing
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  uninstall_postflight do
 | 
			
		||||
    # do nothing
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  uninstall trash: ["#{TEST_TMPDIR}/foo", "#{TEST_TMPDIR}/bar"],
 | 
			
		||||
            rmdir: "#{TEST_TMPDIR}/empty_directory_path"
 | 
			
		||||
 | 
			
		||||
  zap trash: "~/Library/Logs/ManyArtifacts.log",
 | 
			
		||||
      rmdir: ["~/Library/Caches/ManyArtifacts", "~/Library/Application Support/ManyArtifacts"]
 | 
			
		||||
end
 | 
			
		||||
@ -0,0 +1,15 @@
 | 
			
		||||
cask "with-depends-on-everything" do
 | 
			
		||||
  version "1.2.3"
 | 
			
		||||
  sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94"
 | 
			
		||||
 | 
			
		||||
  url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
 | 
			
		||||
  homepage "https://brew.sh/with-depends-on-everything"
 | 
			
		||||
 | 
			
		||||
  depends_on arch: [:intel, :arm64]
 | 
			
		||||
  depends_on cask: "local-caffeine"
 | 
			
		||||
  depends_on cask: "with-depends-on-cask"
 | 
			
		||||
  depends_on formula: "unar"
 | 
			
		||||
  depends_on macos: ">= :el_capitan"
 | 
			
		||||
 | 
			
		||||
  app "Caffeine.app"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										53
									
								
								Library/Homebrew/test/support/fixtures/cask_receipt.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								Library/Homebrew/test/support/fixtures/cask_receipt.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
{
 | 
			
		||||
  "homebrew_version": "4.3.7",
 | 
			
		||||
  "loaded_from_api": false,
 | 
			
		||||
  "uninstall_flight_blocks": true,
 | 
			
		||||
  "installed_as_dependency": false,
 | 
			
		||||
  "installed_on_request": true,
 | 
			
		||||
  "time": 1719289256,
 | 
			
		||||
  "runtime_dependencies": {
 | 
			
		||||
    "cask": [
 | 
			
		||||
      {
 | 
			
		||||
        "full_name": "bar",
 | 
			
		||||
        "version": "2.0",
 | 
			
		||||
        "declared_directly": true
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "formula": [
 | 
			
		||||
      {
 | 
			
		||||
        "full_name": "baz",
 | 
			
		||||
        "version": "3.0",
 | 
			
		||||
        "revision": 0,
 | 
			
		||||
        "pkg_version": "3.0",
 | 
			
		||||
        "declared_directly": true
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "macos": {
 | 
			
		||||
      ">=": [
 | 
			
		||||
        "12"
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "source": {
 | 
			
		||||
    "path": "/opt/homebrew/Library/Taps/homebrew/homebrew-cask/Casks/f/foo.rb",
 | 
			
		||||
    "tap": "homebrew/cask",
 | 
			
		||||
    "tap_git_head": "8b79aa759500f0ffdf65a23e12950cbe3bf8fe17",
 | 
			
		||||
    "version": "1.2.3"
 | 
			
		||||
  },
 | 
			
		||||
  "arch": "arm64",
 | 
			
		||||
  "uninstall_artifacts": [
 | 
			
		||||
    {
 | 
			
		||||
      "app": [
 | 
			
		||||
        "Foo.app"
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "built_on": {
 | 
			
		||||
    "os": "Macintosh",
 | 
			
		||||
    "os_version": "macOS 14",
 | 
			
		||||
    "cpu_family": "arm_firestorm_icestorm",
 | 
			
		||||
    "xcode": "15.4",
 | 
			
		||||
    "clt": "15.3.0.0.1.1708646388",
 | 
			
		||||
    "preferred_perl": "5.34"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -10,6 +10,9 @@
 | 
			
		||||
  ],
 | 
			
		||||
  "built_as_bottle": false,
 | 
			
		||||
  "poured_from_bottle": true,
 | 
			
		||||
  "loaded_from_api": false,
 | 
			
		||||
  "installed_as_dependency": false,
 | 
			
		||||
  "installed_on_request": true,
 | 
			
		||||
  "changed_files": [
 | 
			
		||||
    "INSTALL_RECEIPT.json",
 | 
			
		||||
    "bin/foo"
 | 
			
		||||
@ -27,12 +30,12 @@
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "source": {
 | 
			
		||||
      "path": "/usr/local/Library/Taps/homebrew/homebrew-core/Formula/foo.rb",
 | 
			
		||||
      "tap": "homebrew/core",
 | 
			
		||||
      "spec": "stable",
 | 
			
		||||
      "versions": {
 | 
			
		||||
        "stable": "2.14",
 | 
			
		||||
        "head": "HEAD-0000000"
 | 
			
		||||
      }
 | 
			
		||||
    "path": "/usr/local/Library/Taps/homebrew/homebrew-core/Formula/foo.rb",
 | 
			
		||||
    "tap": "homebrew/core",
 | 
			
		||||
    "spec": "stable",
 | 
			
		||||
    "versions": {
 | 
			
		||||
      "stable": "2.14",
 | 
			
		||||
      "head": "HEAD-0000000"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -191,7 +191,7 @@ RSpec.shared_context "integration test" do # rubocop:disable RSpec/ContextWordin
 | 
			
		||||
    keg.mkpath
 | 
			
		||||
 | 
			
		||||
    tab = Tab.for_name(name)
 | 
			
		||||
    tab.tabfile ||= keg/Tab::FILENAME
 | 
			
		||||
    tab.tabfile ||= keg/AbstractTab::FILENAME
 | 
			
		||||
    tab_attributes.each do |key, value|
 | 
			
		||||
      tab.instance_variable_set(:"@#{key}", value)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -18,20 +18,40 @@ RSpec.describe Tab do
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  matcher :be_installed_as_dependency do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.installed_as_dependency == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  matcher :be_installed_on_request do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.installed_on_request == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  matcher :be_loaded_from_api do
 | 
			
		||||
    match do |actual|
 | 
			
		||||
      actual.loaded_from_api == true
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  subject(:tab) do
 | 
			
		||||
    described_class.new(
 | 
			
		||||
      "homebrew_version"     => HOMEBREW_VERSION,
 | 
			
		||||
      "used_options"         => used_options.as_flags,
 | 
			
		||||
      "unused_options"       => unused_options.as_flags,
 | 
			
		||||
      "built_as_bottle"      => false,
 | 
			
		||||
      "poured_from_bottle"   => true,
 | 
			
		||||
      "changed_files"        => [],
 | 
			
		||||
      "time"                 => time,
 | 
			
		||||
      "source_modified_time" => 0,
 | 
			
		||||
      "compiler"             => "clang",
 | 
			
		||||
      "stdlib"               => "libcxx",
 | 
			
		||||
      "runtime_dependencies" => [],
 | 
			
		||||
      "source"               => {
 | 
			
		||||
      "homebrew_version"        => HOMEBREW_VERSION,
 | 
			
		||||
      "used_options"            => used_options.as_flags,
 | 
			
		||||
      "unused_options"          => unused_options.as_flags,
 | 
			
		||||
      "built_as_bottle"         => false,
 | 
			
		||||
      "poured_from_bottle"      => true,
 | 
			
		||||
      "installed_as_dependency" => false,
 | 
			
		||||
      "installed_on_request"    => true,
 | 
			
		||||
      "changed_files"           => [],
 | 
			
		||||
      "time"                    => time,
 | 
			
		||||
      "source_modified_time"    => 0,
 | 
			
		||||
      "compiler"                => "clang",
 | 
			
		||||
      "stdlib"                  => "libcxx",
 | 
			
		||||
      "runtime_dependencies"    => [],
 | 
			
		||||
      "source"                  => {
 | 
			
		||||
        "tap"      => CoreTap.instance.to_s,
 | 
			
		||||
        "path"     => CoreTap.instance.path.to_s,
 | 
			
		||||
        "spec"     => "stable",
 | 
			
		||||
@ -40,8 +60,8 @@ RSpec.describe Tab do
 | 
			
		||||
          "head"   => "HEAD-1111111",
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      "arch"                 => Hardware::CPU.arch,
 | 
			
		||||
      "built_on"             => DevelopmentTools.build_system_info,
 | 
			
		||||
      "arch"                    => Hardware::CPU.arch,
 | 
			
		||||
      "built_on"                => DevelopmentTools.build_system_info,
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -65,6 +85,9 @@ RSpec.describe Tab do
 | 
			
		||||
    expect(tab.changed_files).to be_nil
 | 
			
		||||
    expect(tab).not_to be_built_as_bottle
 | 
			
		||||
    expect(tab).not_to be_poured_from_bottle
 | 
			
		||||
    expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
    expect(tab).not_to be_installed_on_request
 | 
			
		||||
    expect(tab).not_to be_loaded_from_api
 | 
			
		||||
    expect(tab).to be_stable
 | 
			
		||||
    expect(tab).not_to be_head
 | 
			
		||||
    expect(tab.tap).to be_nil
 | 
			
		||||
@ -132,18 +155,69 @@ RSpec.describe Tab do
 | 
			
		||||
    expect(tab.runtime_dependencies).not_to be_nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  specify "::runtime_deps_hash" do
 | 
			
		||||
    runtime_deps = [Dependency.new("foo")]
 | 
			
		||||
    foo = formula("foo") { url "foo-1.0" }
 | 
			
		||||
    stub_formula_loader foo
 | 
			
		||||
    runtime_deps_hash = described_class.runtime_deps_hash(foo, runtime_deps)
 | 
			
		||||
    tab = described_class.new
 | 
			
		||||
    tab.homebrew_version = "1.1.6"
 | 
			
		||||
    tab.runtime_dependencies = runtime_deps_hash
 | 
			
		||||
    expect(tab.runtime_dependencies).to eql(
 | 
			
		||||
      [{ "full_name" => "foo", "version" => "1.0", "revision" => 0, "pkg_version" => "1.0",
 | 
			
		||||
"declared_directly" => false }],
 | 
			
		||||
    )
 | 
			
		||||
  describe "::runtime_deps_hash" do
 | 
			
		||||
    it "handles older Homebrew versions correctly" do
 | 
			
		||||
      runtime_deps = [Dependency.new("foo")]
 | 
			
		||||
      foo = formula("foo") { url "foo-1.0" }
 | 
			
		||||
      stub_formula_loader foo
 | 
			
		||||
      runtime_deps_hash = described_class.runtime_deps_hash(foo, runtime_deps)
 | 
			
		||||
      tab = described_class.new
 | 
			
		||||
      tab.homebrew_version = "1.1.6"
 | 
			
		||||
      tab.runtime_dependencies = runtime_deps_hash
 | 
			
		||||
      expect(tab.runtime_dependencies).to eql(
 | 
			
		||||
        [{ "full_name" => "foo", "version" => "1.0", "revision" => 0, "pkg_version" => "1.0",
 | 
			
		||||
        "declared_directly" => false }],
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "include declared dependencies" do
 | 
			
		||||
      foo = formula("foo") { url "foo-1.0" }
 | 
			
		||||
      stub_formula_loader foo
 | 
			
		||||
 | 
			
		||||
      runtime_deps = [Dependency.new("foo")]
 | 
			
		||||
      formula = instance_double(Formula, deps: runtime_deps)
 | 
			
		||||
 | 
			
		||||
      expected_output = [
 | 
			
		||||
        {
 | 
			
		||||
          "full_name"         => "foo",
 | 
			
		||||
          "version"           => "1.0",
 | 
			
		||||
          "revision"          => 0,
 | 
			
		||||
          "pkg_version"       => "1.0",
 | 
			
		||||
          "declared_directly" => true,
 | 
			
		||||
        },
 | 
			
		||||
      ]
 | 
			
		||||
      expect(described_class.runtime_deps_hash(formula, runtime_deps)).to eq(expected_output)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "includes recursive dependencies" do
 | 
			
		||||
      foo = formula("foo") { url "foo-1.0" }
 | 
			
		||||
      bar = formula("bar") { url "bar-2.0" }
 | 
			
		||||
      stub_formula_loader foo
 | 
			
		||||
      stub_formula_loader bar
 | 
			
		||||
 | 
			
		||||
      # Simulating dependencies formula => foo => bar
 | 
			
		||||
      formula_declared_deps = [Dependency.new("foo")]
 | 
			
		||||
      formula_recursive_deps = [Dependency.new("foo"), Dependency.new("bar")]
 | 
			
		||||
      formula = instance_double(Formula, deps: formula_declared_deps)
 | 
			
		||||
 | 
			
		||||
      expected_output = [
 | 
			
		||||
        {
 | 
			
		||||
          "full_name"         => "foo",
 | 
			
		||||
          "version"           => "1.0",
 | 
			
		||||
          "revision"          => 0,
 | 
			
		||||
          "pkg_version"       => "1.0",
 | 
			
		||||
          "declared_directly" => true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "full_name"         => "bar",
 | 
			
		||||
          "version"           => "2.0",
 | 
			
		||||
          "revision"          => 0,
 | 
			
		||||
          "pkg_version"       => "2.0",
 | 
			
		||||
          "declared_directly" => false,
 | 
			
		||||
        },
 | 
			
		||||
      ]
 | 
			
		||||
      expect(described_class.runtime_deps_hash(formula, formula_recursive_deps)).to eq(expected_output)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  specify "#cxxstdlib" do
 | 
			
		||||
@ -156,10 +230,13 @@ RSpec.describe Tab do
 | 
			
		||||
    expect(tab.time).to eq(time)
 | 
			
		||||
    expect(tab).not_to be_built_as_bottle
 | 
			
		||||
    expect(tab).to be_poured_from_bottle
 | 
			
		||||
    expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
    expect(tab).to be_installed_on_request
 | 
			
		||||
    expect(tab).not_to be_loaded_from_api
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::from_file" do
 | 
			
		||||
    it "parses a Tab from a file" do
 | 
			
		||||
    it "parses a formula Tab from a file" do
 | 
			
		||||
      path = Pathname.new("#{TEST_FIXTURE_DIR}/receipt.json")
 | 
			
		||||
      tab = described_class.from_file(path)
 | 
			
		||||
      source_path = "/usr/local/Library/Taps/homebrew/homebrew-core/Formula/foo.rb"
 | 
			
		||||
@ -171,6 +248,9 @@ RSpec.describe Tab do
 | 
			
		||||
      expect(tab.changed_files).to eq(changed_files)
 | 
			
		||||
      expect(tab).not_to be_built_as_bottle
 | 
			
		||||
      expect(tab).to be_poured_from_bottle
 | 
			
		||||
      expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
      expect(tab).to be_installed_on_request
 | 
			
		||||
      expect(tab).not_to be_loaded_from_api
 | 
			
		||||
      expect(tab).to be_stable
 | 
			
		||||
      expect(tab).not_to be_head
 | 
			
		||||
      expect(tab.tap.name).to eq("homebrew/core")
 | 
			
		||||
@ -186,7 +266,7 @@ RSpec.describe Tab do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::from_file_content" do
 | 
			
		||||
    it "parses a Tab from a file" do
 | 
			
		||||
    it "parses a formula Tab from a file" do
 | 
			
		||||
      path = Pathname.new("#{TEST_FIXTURE_DIR}/receipt.json")
 | 
			
		||||
      tab = described_class.from_file_content(path.read, path)
 | 
			
		||||
      source_path = "/usr/local/Library/Taps/homebrew/homebrew-core/Formula/foo.rb"
 | 
			
		||||
@ -198,6 +278,9 @@ RSpec.describe Tab do
 | 
			
		||||
      expect(tab.changed_files).to eq(changed_files)
 | 
			
		||||
      expect(tab).not_to be_built_as_bottle
 | 
			
		||||
      expect(tab).to be_poured_from_bottle
 | 
			
		||||
      expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
      expect(tab).to be_installed_on_request
 | 
			
		||||
      expect(tab).not_to be_loaded_from_api
 | 
			
		||||
      expect(tab).to be_stable
 | 
			
		||||
      expect(tab).not_to be_head
 | 
			
		||||
      expect(tab.tap.name).to eq("homebrew/core")
 | 
			
		||||
@ -211,7 +294,7 @@ RSpec.describe Tab do
 | 
			
		||||
      expect(tab.source["path"]).to eq(source_path)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "can parse an old Tab file" do
 | 
			
		||||
    it "can parse an old formula Tab file" do
 | 
			
		||||
      path = Pathname.new("#{TEST_FIXTURE_DIR}/receipt_old.json")
 | 
			
		||||
      tab = described_class.from_file_content(path.read, path)
 | 
			
		||||
 | 
			
		||||
@ -219,6 +302,9 @@ RSpec.describe Tab do
 | 
			
		||||
      expect(tab.unused_options.sort).to eq(unused_options.sort)
 | 
			
		||||
      expect(tab).not_to be_built_as_bottle
 | 
			
		||||
      expect(tab).to be_poured_from_bottle
 | 
			
		||||
      expect(tab).not_to be_installed_as_dependency
 | 
			
		||||
      expect(tab).not_to be_installed_on_request
 | 
			
		||||
      expect(tab).not_to be_loaded_from_api
 | 
			
		||||
      expect(tab).to be_stable
 | 
			
		||||
      expect(tab).not_to be_head
 | 
			
		||||
      expect(tab.tap.name).to eq("homebrew/core")
 | 
			
		||||
@ -238,7 +324,7 @@ RSpec.describe Tab do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "::create" do
 | 
			
		||||
    it "creates a Tab" do
 | 
			
		||||
    it "creates a formula Tab" do
 | 
			
		||||
      # < 1.1.7 runtime dependencies were wrong so are ignored
 | 
			
		||||
      stub_const("HOMEBREW_VERSION", "1.1.7")
 | 
			
		||||
 | 
			
		||||
@ -277,7 +363,7 @@ RSpec.describe Tab do
 | 
			
		||||
      expect(tab.source["path"]).to eq(f.path.to_s)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "can create a Tab from an alias" do
 | 
			
		||||
    it "can create a formula Tab from an alias" do
 | 
			
		||||
      alias_path = CoreTap.instance.alias_dir/"bar"
 | 
			
		||||
      f = formula(alias_path:) { url "foo-1.0" }
 | 
			
		||||
      compiler = DevelopmentTools.default_compiler
 | 
			
		||||
@ -395,6 +481,62 @@ RSpec.describe Tab do
 | 
			
		||||
    expect(json_tab.built_on["os"]).to eq(tab.built_on["os"])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#to_s" do
 | 
			
		||||
    let(:time_string) { Time.at(1_720_189_863).strftime("%Y-%m-%d at %H:%M:%S") }
 | 
			
		||||
 | 
			
		||||
    it "returns install information for the Tab" do
 | 
			
		||||
      tab = described_class.new(
 | 
			
		||||
        poured_from_bottle: true,
 | 
			
		||||
        loaded_from_api:    true,
 | 
			
		||||
        time:               1_720_189_863,
 | 
			
		||||
        used_options:       %w[--with-foo --without-bar],
 | 
			
		||||
      )
 | 
			
		||||
      output = "Poured from bottle using the formulae.brew.sh API on #{time_string} " \
 | 
			
		||||
               "with: --with-foo --without-bar"
 | 
			
		||||
      expect(tab.to_s).to eq(output)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "includes 'Poured from bottle' if the formula was installed from a bottle" do
 | 
			
		||||
      tab = described_class.new(poured_from_bottle: true)
 | 
			
		||||
      expect(tab.to_s).to include("Poured from bottle")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "includes 'Built from source' if the formula was not installed from a bottle" do
 | 
			
		||||
      tab = described_class.new(poured_from_bottle: false)
 | 
			
		||||
      expect(tab.to_s).to include("Built from source")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "includes 'using the formulae.brew.sh API' if the formula was installed from the API" do
 | 
			
		||||
      tab = described_class.new(loaded_from_api: true)
 | 
			
		||||
      expect(tab.to_s).to include("using the formulae.brew.sh API")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "does not include 'using the formulae.brew.sh API' if the formula was not installed from the API" do
 | 
			
		||||
      tab = described_class.new(loaded_from_api: false)
 | 
			
		||||
      expect(tab.to_s).not_to include("using the formulae.brew.sh API")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "includes the time value if specified" do
 | 
			
		||||
      tab = described_class.new(time: 1_720_189_863)
 | 
			
		||||
      expect(tab.to_s).to include("on #{time_string}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "does not include the time value if not specified" do
 | 
			
		||||
      tab = described_class.new(time: nil)
 | 
			
		||||
      expect(tab.to_s).not_to match(/on %d+-%d+-%d+ at %d+:%d+:%d+/)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "includes options if specified" do
 | 
			
		||||
      tab = described_class.new(used_options: %w[--with-foo --without-bar])
 | 
			
		||||
      expect(tab.to_s).to include("with: --with-foo --without-bar")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "not to include options if not specified" do
 | 
			
		||||
      tab = described_class.new(used_options: [])
 | 
			
		||||
      expect(tab.to_s).not_to include("with: ")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  specify "::remap_deprecated_options" do
 | 
			
		||||
    deprecated_options = [DeprecatedOption.new("with-foo", "with-foo-new")]
 | 
			
		||||
    remapped_options = described_class.remap_deprecated_options(deprecated_options, tab.used_options)
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ RSpec.describe Homebrew::Uninstall do
 | 
			
		||||
 | 
			
		||||
    tab = Tab.empty
 | 
			
		||||
    tab.homebrew_version = "1.1.6"
 | 
			
		||||
    tab.tabfile = dependent_formula.latest_installed_prefix/Tab::FILENAME
 | 
			
		||||
    tab.tabfile = dependent_formula.latest_installed_prefix/AbstractTab::FILENAME
 | 
			
		||||
    tab.runtime_dependencies = [
 | 
			
		||||
      { "full_name" => "dependency", "version" => "1" },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -106,7 +106,7 @@ module Utils
 | 
			
		||||
 | 
			
		||||
      def load_tab(formula)
 | 
			
		||||
        keg = Keg.new(formula.prefix)
 | 
			
		||||
        tabfile = keg/Tab::FILENAME
 | 
			
		||||
        tabfile = keg/AbstractTab::FILENAME
 | 
			
		||||
        bottle_json_path = formula.local_bottle_path&.sub(/\.(\d+\.)?tar\.gz$/, ".json")
 | 
			
		||||
 | 
			
		||||
        if (tab_attributes = formula.bottle_tab_attributes.presence)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user