brew/Library/Homebrew/cask/installer.rb

575 lines
17 KiB
Ruby
Raw Normal View History

2023-03-18 16:03:10 -07:00
# typed: true
# frozen_string_literal: true
2017-06-28 17:53:59 +02:00
require "formula_installer"
require "unpack_strategy"
require "utils/topological_hash"
2017-06-28 17:53:59 +02:00
require "cask/config"
require "cask/download"
require "cask/quarantine"
2016-08-18 22:11:42 +03:00
require "cgi"
2018-09-06 08:29:14 +02:00
module Cask
2020-08-24 23:33:12 +02:00
# Installer for a {Cask}.
#
# @api private
2016-09-24 13:52:43 +02:00
class Installer
extend Predicable
2016-09-24 13:52:43 +02:00
def initialize(cask, command: SystemCommand, force: false, adopt: false,
skip_cask_deps: false, binaries: true, verbose: false,
cask/reinstall: Support `--zap` for entirely purging cask files - The `brew uninstall` command has `--zap`, so let's make `brew reinstall` have parity here for a better user experience. (Requested in issue 12983.) - It feels weird that to get my new reinstall test to pass I had to add `--zap` to `cask/cmd/install.rb`, not `cask/cmd/reinstall.rb` to get the tests to pass. But the `brew reinstall --cask caffeine --zap` command worked fine all the time. The CLI argument parser from the test run was complaining about not knowing what `zap` was. As a result, `--zap` now shows up as a switch in `brew install --help` which I'm not 100% convinced is the desired UX. But I've edited the description accordingly to specify that it will only work on `reinstall` operations (and `--zap` on `install` is a no-op). ``` issyl0 at pictor in /opt/homebrew on reinstall-cask-zap ❯ brew reinstall --cask caffeine --zap ==> Downloading https://github.com/IntelliScape/caffeine/releases/download/1.1.3/Caffeine.dmg Already downloaded: /Users/issyl0/Library/Caches/Homebrew/downloads/3d6ccfdd3b8d0ab37d1c2468d6e69078c2d31d3b12bf51947c4db21e5f376af2--Caffeine.dmg ==> Implied `brew uninstall --cask caffeine` ==> Backing App 'Caffeine.app' up to '/opt/homebrew/Caskroom/caffeine/1.1.3/Caffeine.app' ==> Removing App '/Applications/Caffeine.app' ==> Dispatching zap stanza ==> Trashing files: ~/Library/Application Support/com.intelliscapesolutions.caffeine ~/Library/Preferences/com.intelliscapesolutions.caffeine.plist ~/Library/Caches/com.intelliscapesolutions.caffeine ~/Library/HTTPStoages/com.intelliscapesolutions.caffeine.binarycookies ==> Removing all staged versions of Cask 'caffeine' ==> Installing Cask caffeine ==> Moving App 'Caffeine.app' to '/Applications/Caffeine.app' 🍺 caffeine was successfully installed! ```
2022-04-05 00:57:33 +01:00
zap: false, require_sha: false, upgrade: false,
2020-12-04 00:07:02 +01:00
installed_as_dependency: false, quarantine: true,
2021-09-10 23:11:41 +09:00
verify_download_integrity: true, quiet: false)
2016-09-24 13:52:43 +02:00
@cask = cask
@command = command
@force = force
@adopt = adopt
2016-09-24 13:52:43 +02:00
@skip_cask_deps = skip_cask_deps
2017-05-19 19:13:23 +02:00
@binaries = binaries
2017-05-21 00:15:56 +02:00
@verbose = verbose
cask/reinstall: Support `--zap` for entirely purging cask files - The `brew uninstall` command has `--zap`, so let's make `brew reinstall` have parity here for a better user experience. (Requested in issue 12983.) - It feels weird that to get my new reinstall test to pass I had to add `--zap` to `cask/cmd/install.rb`, not `cask/cmd/reinstall.rb` to get the tests to pass. But the `brew reinstall --cask caffeine --zap` command worked fine all the time. The CLI argument parser from the test run was complaining about not knowing what `zap` was. As a result, `--zap` now shows up as a switch in `brew install --help` which I'm not 100% convinced is the desired UX. But I've edited the description accordingly to specify that it will only work on `reinstall` operations (and `--zap` on `install` is a no-op). ``` issyl0 at pictor in /opt/homebrew on reinstall-cask-zap ❯ brew reinstall --cask caffeine --zap ==> Downloading https://github.com/IntelliScape/caffeine/releases/download/1.1.3/Caffeine.dmg Already downloaded: /Users/issyl0/Library/Caches/Homebrew/downloads/3d6ccfdd3b8d0ab37d1c2468d6e69078c2d31d3b12bf51947c4db21e5f376af2--Caffeine.dmg ==> Implied `brew uninstall --cask caffeine` ==> Backing App 'Caffeine.app' up to '/opt/homebrew/Caskroom/caffeine/1.1.3/Caffeine.app' ==> Removing App '/Applications/Caffeine.app' ==> Dispatching zap stanza ==> Trashing files: ~/Library/Application Support/com.intelliscapesolutions.caffeine ~/Library/Preferences/com.intelliscapesolutions.caffeine.plist ~/Library/Caches/com.intelliscapesolutions.caffeine ~/Library/HTTPStoages/com.intelliscapesolutions.caffeine.binarycookies ==> Removing all staged versions of Cask 'caffeine' ==> Installing Cask caffeine ==> Moving App 'Caffeine.app' to '/Applications/Caffeine.app' 🍺 caffeine was successfully installed! ```
2022-04-05 00:57:33 +01:00
@zap = zap
2016-09-24 13:52:43 +02:00
@require_sha = require_sha
@reinstall = false
@upgrade = upgrade
@installed_as_dependency = installed_as_dependency
@quarantine = quarantine
2020-12-04 00:07:02 +01:00
@verify_download_integrity = verify_download_integrity
2021-09-10 23:11:41 +09:00
@quiet = quiet
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
attr_predicate :binaries?, :force?, :adopt?, :skip_cask_deps?, :require_sha?,
cask/reinstall: Support `--zap` for entirely purging cask files - The `brew uninstall` command has `--zap`, so let's make `brew reinstall` have parity here for a better user experience. (Requested in issue 12983.) - It feels weird that to get my new reinstall test to pass I had to add `--zap` to `cask/cmd/install.rb`, not `cask/cmd/reinstall.rb` to get the tests to pass. But the `brew reinstall --cask caffeine --zap` command worked fine all the time. The CLI argument parser from the test run was complaining about not knowing what `zap` was. As a result, `--zap` now shows up as a switch in `brew install --help` which I'm not 100% convinced is the desired UX. But I've edited the description accordingly to specify that it will only work on `reinstall` operations (and `--zap` on `install` is a no-op). ``` issyl0 at pictor in /opt/homebrew on reinstall-cask-zap ❯ brew reinstall --cask caffeine --zap ==> Downloading https://github.com/IntelliScape/caffeine/releases/download/1.1.3/Caffeine.dmg Already downloaded: /Users/issyl0/Library/Caches/Homebrew/downloads/3d6ccfdd3b8d0ab37d1c2468d6e69078c2d31d3b12bf51947c4db21e5f376af2--Caffeine.dmg ==> Implied `brew uninstall --cask caffeine` ==> Backing App 'Caffeine.app' up to '/opt/homebrew/Caskroom/caffeine/1.1.3/Caffeine.app' ==> Removing App '/Applications/Caffeine.app' ==> Dispatching zap stanza ==> Trashing files: ~/Library/Application Support/com.intelliscapesolutions.caffeine ~/Library/Preferences/com.intelliscapesolutions.caffeine.plist ~/Library/Caches/com.intelliscapesolutions.caffeine ~/Library/HTTPStoages/com.intelliscapesolutions.caffeine.binarycookies ==> Removing all staged versions of Cask 'caffeine' ==> Installing Cask caffeine ==> Moving App 'Caffeine.app' to '/Applications/Caffeine.app' 🍺 caffeine was successfully installed! ```
2022-04-05 00:57:33 +01:00
:reinstall?, :upgrade?, :verbose?, :zap?, :installed_as_dependency?,
2021-09-10 23:11:41 +09:00
:quarantine?, :quiet?
2017-05-21 00:15:56 +02:00
def self.caveats(cask)
2016-09-24 13:52:43 +02:00
odebug "Printing caveats"
2016-10-24 17:07:57 +02:00
caveats = cask.caveats
return if caveats.empty?
2016-10-24 17:07:57 +02:00
Homebrew.messages.record_caveats(cask.token, caveats)
<<~EOS
#{ohai_title "Caveats"}
#{caveats}
EOS
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
sig { params(quiet: T.nilable(T::Boolean), timeout: T.nilable(T.any(Integer, Float))).void }
def fetch(quiet: nil, timeout: nil)
2018-09-06 08:29:14 +02:00
odebug "Cask::Installer#fetch"
load_cask_from_source_api! if @cask.loaded_from_api? && @cask.caskfile_only?
verify_has_sha if require_sha? && !force?
download(quiet: quiet, timeout: timeout)
satisfy_dependencies
end
def stage
2018-09-06 08:29:14 +02:00
odebug "Cask::Installer#stage"
2018-06-09 11:18:40 +02:00
Caskroom.ensure_caskroom_exists
extract_primary_container
save_caskfile
rescue => e
purge_versioned_files
raise e
end
2016-09-24 13:52:43 +02:00
def install
start_time = Time.now
2018-09-06 08:29:14 +02:00
odebug "Cask::Installer#install"
2016-08-18 22:11:42 +03:00
old_config = @cask.config
2021-09-10 23:11:41 +09:00
if @cask.installed? && !force? && !reinstall? && !upgrade?
return if quiet?
2021-09-10 23:11:41 +09:00
raise CaskAlreadyInstalledError, @cask
end
2016-08-18 22:11:42 +03:00
2017-08-05 15:56:34 +02:00
check_conflicts
print caveats
fetch
2019-02-02 17:11:37 +01:00
uninstall_existing_cask if reinstall?
backup if force? && @cask.staged_path.exist? && @cask.metadata_versioned_path.exist?
2018-02-09 11:31:51 +09:00
oh1 "Installing Cask #{Formatter.identifier(@cask)}"
opoo "macOS's Gatekeeper has been disabled for this Cask" unless quarantine?
stage
2020-09-29 23:46:30 +02:00
@cask.config = @cask.default_config.merge(old_config)
install_artifacts
2016-08-18 22:11:42 +03:00
if (tap = @cask.tap) && tap.should_report_analytics?
::Utils::Analytics.report_event(:cask_install, package_name: @cask.token, tap_name: tap.name,
on_request: true)
end
2018-08-06 12:21:48 -07:00
purge_backed_up_versioned_files
2016-09-24 13:52:43 +02:00
puts summary
end_time = Time.now
Homebrew.messages.package_installed(@cask.token, end_time - start_time)
rescue
restore_backup
raise
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2017-08-05 15:56:34 +02:00
def check_conflicts
return unless @cask.conflicts_with
@cask.conflicts_with[:cask].each do |conflicting_cask|
if (match = conflicting_cask.match(HOMEBREW_TAP_CASK_REGEX))
conflicting_cask_tap = Tap.fetch(match[1], match[2])
next unless conflicting_cask_tap.installed?
end
conflicting_cask = CaskLoader.load(conflicting_cask)
raise CaskConflictError.new(@cask, conflicting_cask) if conflicting_cask.installed?
rescue CaskUnavailableError
next # Ignore conflicting Casks that do not exist.
2017-08-05 15:56:34 +02:00
end
end
def reinstall
2018-09-06 08:29:14 +02:00
odebug "Cask::Installer#reinstall"
@reinstall = true
install
end
2017-04-17 17:21:02 -07:00
def uninstall_existing_cask
return unless @cask.installed?
# Always force uninstallation, ignore method parameter
cask_installer = Installer.new(@cask, verbose: verbose?, force: true, upgrade: upgrade?)
cask/reinstall: Support `--zap` for entirely purging cask files - The `brew uninstall` command has `--zap`, so let's make `brew reinstall` have parity here for a better user experience. (Requested in issue 12983.) - It feels weird that to get my new reinstall test to pass I had to add `--zap` to `cask/cmd/install.rb`, not `cask/cmd/reinstall.rb` to get the tests to pass. But the `brew reinstall --cask caffeine --zap` command worked fine all the time. The CLI argument parser from the test run was complaining about not knowing what `zap` was. As a result, `--zap` now shows up as a switch in `brew install --help` which I'm not 100% convinced is the desired UX. But I've edited the description accordingly to specify that it will only work on `reinstall` operations (and `--zap` on `install` is a no-op). ``` issyl0 at pictor in /opt/homebrew on reinstall-cask-zap ❯ brew reinstall --cask caffeine --zap ==> Downloading https://github.com/IntelliScape/caffeine/releases/download/1.1.3/Caffeine.dmg Already downloaded: /Users/issyl0/Library/Caches/Homebrew/downloads/3d6ccfdd3b8d0ab37d1c2468d6e69078c2d31d3b12bf51947c4db21e5f376af2--Caffeine.dmg ==> Implied `brew uninstall --cask caffeine` ==> Backing App 'Caffeine.app' up to '/opt/homebrew/Caskroom/caffeine/1.1.3/Caffeine.app' ==> Removing App '/Applications/Caffeine.app' ==> Dispatching zap stanza ==> Trashing files: ~/Library/Application Support/com.intelliscapesolutions.caffeine ~/Library/Preferences/com.intelliscapesolutions.caffeine.plist ~/Library/Caches/com.intelliscapesolutions.caffeine ~/Library/HTTPStoages/com.intelliscapesolutions.caffeine.binarycookies ==> Removing all staged versions of Cask 'caffeine' ==> Installing Cask caffeine ==> Moving App 'Caffeine.app' to '/Applications/Caffeine.app' 🍺 caffeine was successfully installed! ```
2022-04-05 00:57:33 +01:00
zap? ? cask_installer.zap : cask_installer.uninstall
end
2020-10-20 12:03:48 +02:00
sig { returns(String) }
2016-09-24 13:52:43 +02:00
def summary
2019-04-20 14:07:29 +09:00
s = +""
2020-04-05 15:44:50 +01:00
s << "#{Homebrew::EnvConfig.install_badge} " unless Homebrew::EnvConfig.no_emoji?
s << "#{@cask} was successfully #{upgrade? ? "upgraded" : "installed"}!"
2019-04-20 14:07:29 +09:00
s.freeze
2016-08-18 22:11:42 +03:00
end
sig { returns(Download) }
def downloader
@downloader ||= Download.new(@cask, quarantine: quarantine?)
end
sig { params(quiet: T.nilable(T::Boolean), timeout: T.nilable(T.any(Integer, Float))).returns(Pathname) }
def download(quiet: nil, timeout: nil)
# Store cask download path in cask to prevent multiple downloads in a row when checking if it's outdated
@cask.download ||= downloader.fetch(quiet: quiet, verify_download_integrity: @verify_download_integrity,
timeout: timeout)
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def verify_has_sha
odebug "Checking cask has checksum"
2023-04-18 15:06:50 -07:00
return if @cask.sha256 != :no_check
2018-09-17 02:45:00 +02:00
2020-11-19 19:44:23 +01:00
raise CaskError, <<~EOS
Cask '#{@cask}' does not have a sha256 checksum defined and was not installed.
This means you have the #{Formatter.identifier("--require-sha")} option set, perhaps in your HOMEBREW_CASK_OPTS.
EOS
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
def primary_container
@primary_container ||= begin
downloaded_path = download(quiet: true)
2020-12-04 00:07:02 +01:00
UnpackStrategy.detect(downloaded_path, type: @cask.container&.type, merge_xattrs: true)
end
end
2020-12-04 00:07:02 +01:00
def extract_primary_container(to: @cask.staged_path)
odebug "Extracting primary container"
2020-12-04 00:07:02 +01:00
odebug "Using container class #{primary_container.class} for #{primary_container.path}"
basename = downloader.basename
if (nested_container = @cask.container&.nested)
Dir.mktmpdir do |tmpdir|
tmpdir = Pathname(tmpdir)
primary_container.extract(to: tmpdir, basename: basename, verbose: verbose?)
FileUtils.chmod_R "+rw", tmpdir/nested_container, force: true, verbose: verbose?
UnpackStrategy.detect(tmpdir/nested_container, merge_xattrs: true)
2020-12-04 00:07:02 +01:00
.extract_nestedly(to: to, verbose: verbose?)
end
else
2020-12-04 00:07:02 +01:00
primary_container.extract_nestedly(to: to, basename: basename, verbose: verbose?)
end
return unless quarantine?
return unless Quarantine.available?
2020-12-04 00:07:02 +01:00
Quarantine.propagate(from: primary_container.path, to: to)
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def install_artifacts
2020-07-06 15:41:58 -04:00
artifacts = @cask.artifacts
already_installed_artifacts = []
2016-09-24 13:52:43 +02:00
odebug "Installing artifacts"
2016-09-24 13:52:43 +02:00
artifacts.each do |artifact|
2017-02-05 07:47:54 +01:00
next unless artifact.respond_to?(:install_phase)
2018-09-17 02:45:00 +02:00
odebug "Installing artifact of class #{artifact.class}"
2017-05-19 19:13:23 +02:00
2020-09-01 14:05:52 +01:00
next if artifact.is_a?(Artifact::Binary) && !binaries?
2017-05-19 19:13:23 +02:00
artifact.install_phase(command: @command, verbose: verbose?, adopt: adopt?, force: force?)
already_installed_artifacts.unshift(artifact)
2016-09-24 13:52:43 +02:00
end
2019-02-02 17:11:37 +01:00
save_config_file
save_download_sha if @cask.version.latest?
rescue => e
begin
2023-03-18 16:03:10 -07:00
already_installed_artifacts&.each do |artifact|
2020-09-01 14:05:52 +01:00
if artifact.respond_to?(:uninstall_phase)
odebug "Reverting installation of artifact of class #{artifact.class}"
artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?)
end
next unless artifact.respond_to?(:post_uninstall_phase)
odebug "Reverting installation of artifact of class #{artifact.class}"
artifact.post_uninstall_phase(command: @command, verbose: verbose?, force: force?)
end
ensure
purge_versioned_files
raise e
end
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
# TODO: move dependencies to a separate class,
# dependencies should also apply for `brew cask stage`,
# override dependencies with `--force` or perhaps `--force-deps`
2016-09-24 13:52:43 +02:00
def satisfy_dependencies
return unless @cask.depends_on
macos_dependencies
arch_dependencies
cask_and_formula_dependencies
2016-08-18 22:11:42 +03:00
end
2016-09-24 13:52:43 +02:00
def macos_dependencies
return unless @cask.depends_on.macos
2019-08-15 10:14:10 +02:00
return if @cask.depends_on.macos.satisfied?
2018-09-17 02:45:00 +02:00
2019-08-15 10:14:10 +02:00
raise CaskError, @cask.depends_on.macos.message(type: :cask)
2016-08-18 22:11:42 +03:00
end
2016-09-24 13:52:43 +02:00
def arch_dependencies
return if @cask.depends_on.arch.nil?
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
@current_arch ||= { type: Hardware::CPU.type, bits: Hardware::CPU.bits }
2016-10-23 14:44:14 +02:00
return if @cask.depends_on.arch.any? do |arch|
2016-09-24 13:52:43 +02:00
arch[:type] == @current_arch[:type] &&
Array(arch[:bits]).include?(@current_arch[:bits])
2016-10-23 14:44:14 +02:00
end
2018-09-17 02:45:00 +02:00
raise CaskError,
2019-04-30 08:44:35 +01:00
"Cask #{@cask} depends on hardware architecture being one of " \
"[#{@cask.depends_on.arch.map(&:to_s).join(", ")}], " \
2021-01-26 15:21:24 -05:00
"but you are running #{@current_arch}."
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
def collect_cask_and_formula_dependencies
return @cask_and_formula_dependencies if @cask_and_formula_dependencies
2019-10-31 20:20:55 +01:00
graph = ::Utils::TopologicalHash.graph_package_dependencies(@cask)
2023-03-18 16:03:10 -07:00
raise CaskSelfReferencingDependencyError, @cask.token if graph[@cask].include?(@cask)
::Utils::TopologicalHash.graph_package_dependencies(primary_container.dependencies, graph)
begin
2019-11-02 00:33:08 +01:00
@cask_and_formula_dependencies = graph.tsort - [@cask]
2019-10-31 20:20:55 +01:00
rescue TSort::Cyclic
strongly_connected_components = graph.strongly_connected_components.sort_by(&:count)
cyclic_dependencies = strongly_connected_components.last - [@cask]
raise CaskCyclicDependencyError.new(@cask.token, cyclic_dependencies.to_sentence)
end
end
def missing_cask_and_formula_dependencies
collect_cask_and_formula_dependencies.reject do |cask_or_formula|
installed = if cask_or_formula.respond_to?(:any_version_installed?)
cask_or_formula.any_version_installed?
else
cask_or_formula.try(:installed?)
end
installed && (cask_or_formula.respond_to?(:optlinked?) ? cask_or_formula.optlinked? : true)
2019-10-31 20:20:55 +01:00
end
end
def cask_and_formula_dependencies
return if installed_as_dependency?
formulae_and_casks = collect_cask_and_formula_dependencies
return if formulae_and_casks.empty?
missing_formulae_and_casks = missing_cask_and_formula_dependencies
2019-10-31 20:20:55 +01:00
if missing_formulae_and_casks.empty?
puts "All formula dependencies satisfied."
2019-10-31 20:20:55 +01:00
return
end
ohai "Installing dependencies: #{missing_formulae_and_casks.map(&:to_s).join(", ")}"
missing_formulae_and_casks.each do |cask_or_formula|
2019-10-31 20:20:55 +01:00
if cask_or_formula.is_a?(Cask)
if skip_cask_deps?
2021-09-27 18:25:39 -07:00
opoo "`--skip-cask-deps` is set; skipping installation of #{cask_or_formula}."
2019-10-31 20:20:55 +01:00
next
end
Installer.new(
cask_or_formula,
adopt: adopt?,
2019-10-31 20:20:55 +01:00
binaries: binaries?,
verbose: verbose?,
installed_as_dependency: true,
force: false,
).install
else
2020-11-18 05:37:12 +01:00
fi = FormulaInstaller.new(
cask_or_formula,
**{
show_header: true,
installed_as_dependency: true,
installed_on_request: false,
verbose: verbose?,
}.compact,
)
fi.prelude
fi.fetch
fi.install
fi.finish
2019-10-31 20:20:55 +01:00
end
2016-09-24 13:52:43 +02:00
end
end
2016-08-18 22:11:42 +03:00
def caveats
self.class.caveats(@cask)
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
def metadata_subdir
@metadata_subdir ||= @cask.metadata_subdir("Casks", timestamp: :now, create: true)
end
2016-09-24 13:52:43 +02:00
def save_caskfile
old_savedir = @cask.metadata_timestamped_path
return if @cask.source.blank?
extension = @cask.loaded_from_api? ? "json" : "rb"
(metadata_subdir/"#{@cask.token}.#{extension}").write @cask.source
2017-09-24 19:24:46 +01:00
old_savedir&.rmtree
2016-08-18 22:11:42 +03:00
end
2019-02-02 17:11:37 +01:00
def save_config_file
metadata_subdir
2019-02-09 02:19:01 +01:00
@cask.config_path.atomic_write(@cask.config.to_json)
2019-02-02 17:11:37 +01:00
end
def save_download_sha
@cask.download_sha_path.atomic_write(@cask.new_download_sha) if @cask.checksumable?
end
2016-09-24 13:52:43 +02:00
def uninstall
load_installed_caskfile!
2018-02-09 11:31:51 +09:00
oh1 "Uninstalling Cask #{Formatter.identifier(@cask)}"
2017-11-10 10:05:18 -03:00
uninstall_artifacts(clear: true)
if !reinstall? && !upgrade?
remove_download_sha
remove_config_file
end
purge_versioned_files
purge_caskroom_path if force?
end
2019-02-02 17:11:37 +01:00
def remove_config_file
FileUtils.rm_f @cask.config_path
@cask.config_path.parent.rmdir_if_possible
end
def remove_download_sha
FileUtils.rm_f @cask.download_sha_path if @cask.download_sha_path.exist?
end
def start_upgrade
uninstall_artifacts
backup
end
def backup
@cask.staged_path.rename backup_path
2017-11-28 21:00:16 +00:00
@cask.metadata_versioned_path.rename backup_metadata_path
end
def restore_backup
return if !backup_path.directory? || !backup_metadata_path.directory?
Pathname.new(@cask.staged_path).rmtree if @cask.staged_path.exist?
2017-11-28 21:00:16 +00:00
Pathname.new(@cask.metadata_versioned_path).rmtree if @cask.metadata_versioned_path.exist?
backup_path.rename @cask.staged_path
2017-11-28 21:00:16 +00:00
backup_metadata_path.rename @cask.metadata_versioned_path
end
def revert_upgrade
opoo "Reverting upgrade for Cask #{@cask}"
restore_backup
install_artifacts
end
def finalize_upgrade
ohai "Purging files for version #{@cask.version} of Cask #{@cask}"
purge_backed_up_versioned_files
2017-11-11 17:21:13 -03:00
puts summary
2016-08-18 22:11:42 +03:00
end
2017-11-10 10:05:18 -03:00
def uninstall_artifacts(clear: false)
2017-10-04 17:08:35 +02:00
artifacts = @cask.artifacts
2020-07-06 15:41:58 -04:00
odebug "Uninstalling artifacts"
odebug "#{::Utils.pluralize("artifact", artifacts.length, include_count: true)} defined", artifacts
2016-09-24 13:52:43 +02:00
artifacts.each do |artifact|
2020-09-01 14:05:52 +01:00
if artifact.respond_to?(:uninstall_phase)
odebug "Uninstalling artifact of class #{artifact.class}"
artifact.uninstall_phase(
command: @command, verbose: verbose?, skip: clear, force: force?, upgrade: upgrade?,
)
end
next unless artifact.respond_to?(:post_uninstall_phase)
odebug "Post-uninstalling artifact of class #{artifact.class}"
artifact.post_uninstall_phase(
command: @command, verbose: verbose?, skip: clear, force: force?, upgrade: upgrade?,
)
end
2016-08-18 22:11:42 +03:00
end
2016-09-24 13:52:43 +02:00
def zap
load_installed_caskfile!
ohai "Implied `brew uninstall --cask #{@cask}`"
2016-09-24 13:52:43 +02:00
uninstall_artifacts
2017-10-04 17:08:35 +02:00
if (zap_stanzas = @cask.artifacts.select { |a| a.is_a?(Artifact::Zap) }).empty?
2016-09-24 13:52:43 +02:00
opoo "No zap stanza present for Cask '#{@cask}'"
else
ohai "Dispatching zap stanza"
zap_stanzas.each do |stanza|
stanza.zap_phase(command: @command, verbose: verbose?, force: force?)
end
2016-09-24 13:52:43 +02:00
end
ohai "Removing all staged versions of Cask '#{@cask}'"
purge_caskroom_path
2016-08-18 22:11:42 +03:00
end
def backup_path
return if @cask.staged_path.nil?
2018-09-17 02:45:00 +02:00
2018-04-29 05:34:32 +02:00
Pathname("#{@cask.staged_path}.upgrading")
end
2017-11-28 21:00:16 +00:00
def backup_metadata_path
return if @cask.metadata_versioned_path.nil?
2018-09-17 02:45:00 +02:00
2018-04-29 05:34:32 +02:00
Pathname("#{@cask.metadata_versioned_path}.upgrading")
2017-11-28 21:00:16 +00:00
end
2016-09-24 13:52:43 +02:00
def gain_permissions_remove(path)
Utils.gain_permissions_remove(path, command: @command)
2016-08-18 22:11:42 +03:00
end
def purge_backed_up_versioned_files
2016-09-24 13:52:43 +02:00
# versioned staged distribution
2018-04-29 05:34:32 +02:00
gain_permissions_remove(backup_path) if backup_path&.exist?
2018-09-03 20:12:29 +01:00
# Homebrew Cask metadata
2018-04-29 05:34:32 +02:00
return unless backup_metadata_path.directory?
backup_metadata_path.children.each do |subdir|
gain_permissions_remove(subdir)
2017-11-28 21:00:16 +00:00
end
backup_metadata_path.rmdir_if_possible
end
2017-11-10 10:05:18 -03:00
def purge_versioned_files
ohai "Purging files for version #{@cask.version} of Cask #{@cask}"
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
# versioned staged distribution
2018-04-29 05:34:32 +02:00
gain_permissions_remove(@cask.staged_path) if @cask.staged_path&.exist?
2016-08-18 22:11:42 +03:00
2018-09-03 20:12:29 +01:00
# Homebrew Cask metadata
2018-04-29 05:34:32 +02:00
if @cask.metadata_versioned_path.directory?
@cask.metadata_versioned_path.children.each do |subdir|
2018-04-29 05:34:32 +02:00
gain_permissions_remove(subdir)
2016-08-18 22:11:42 +03:00
end
2018-04-29 05:34:32 +02:00
@cask.metadata_versioned_path.rmdir_if_possible
2016-08-18 22:11:42 +03:00
end
@cask.metadata_main_container_path.rmdir_if_possible unless upgrade?
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
# toplevel staged distribution
@cask.caskroom_path.rmdir_if_possible unless upgrade?
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def purge_caskroom_path
odebug "Purging all staged versions of Cask #{@cask}"
gain_permissions_remove(@cask.caskroom_path)
end
private
# load the same cask file that was used for installation, if possible
def load_installed_caskfile!
installed_caskfile = @cask.installed_caskfile
if installed_caskfile.exist?
begin
@cask = CaskLoader.load(installed_caskfile)
return
rescue CaskInvalidError
# could be caused by trying to load outdated caskfile
end
end
load_cask_from_source_api! if @cask.loaded_from_api? && @cask.caskfile_only?
# otherwise we default to the current cask
end
def load_cask_from_source_api!
2023-04-07 14:06:47 +02:00
cask_source = Homebrew::API::Cask.fetch_source(
@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
2016-08-18 22:11:42 +03:00
end
end