Skip to content

Commit

Permalink
Merge pull request #3396 from amyspark/hacktoberfest-upgrade
Browse files Browse the repository at this point in the history
Implement `brew cask upgrade`
  • Loading branch information
reitermarkus authored Nov 27, 2017
2 parents f7baa3b + 8abe60d commit f50ae44
Show file tree
Hide file tree
Showing 12 changed files with 397 additions and 24 deletions.
31 changes: 28 additions & 3 deletions Library/Homebrew/cask/lib/hbc/artifact/moved.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def install_phase(**options)
end

def uninstall_phase(**options)
delete(**options)
move_back(**options)
end

def summarize_installed
Expand All @@ -30,7 +30,7 @@ def move(force: false, command: nil, **options)
message = "It seems there is already #{self.class.english_article} #{self.class.english_name} at '#{target}'"
raise CaskError, "#{message}." unless force
opoo "#{message}; overwriting."
delete(force: force, command: command, **options)
delete(target, force: force, command: command, **options)
end

unless source.exist?
Expand All @@ -49,7 +49,32 @@ def move(force: false, command: nil, **options)
add_altname_metadata(target, source.basename, command: command)
end

def delete(force: false, command: nil, **_)
def move_back(skip: false, force: false, command: nil, **options)
if Utils.path_occupied?(source)
message = "It seems there is already #{self.class.english_article} #{self.class.english_name} at '#{source}'"
raise CaskError, "#{message}." unless force
opoo "#{message}; overwriting."
delete(source, force: force, command: command, **options)
end

unless target.exist?
return if skip
raise CaskError, "It seems the #{self.class.english_name} source '#{target}' is not there."
end

ohai "Moving #{self.class.english_name} '#{target.basename}' back to '#{source}'."
source.dirname.mkpath

if source.parent.writable?
FileUtils.move(target, source)
else
command.run("/bin/mv", args: [target, source], sudo: true)
end

add_altname_metadata(target, source.basename, command: command)
end

def delete(target, force: false, command: nil, **_)
ohai "Removing #{self.class.english_name} '#{target}'."
raise CaskError, "Cannot remove undeletable #{self.class.english_name}." if MacOS.undeletable?(target)

Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/lib/hbc/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
require "hbc/cli/search"
require "hbc/cli/style"
require "hbc/cli/uninstall"
require "hbc/cli/upgrade"
require "hbc/cli/--version"
require "hbc/cli/zap"

Expand Down
87 changes: 87 additions & 0 deletions Library/Homebrew/cask/lib/hbc/cli/upgrade.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
module Hbc
class CLI
class Upgrade < AbstractCommand
option "--greedy", :greedy, false
option "--quiet", :quiet, false
option "--force", :force, false
option "--skip-cask-deps", :skip_cask_deps, false

def initialize(*)
super
self.verbose = ($stdout.tty? || verbose?) && !quiet?
end

def run
outdated_casks = casks(alternative: lambda {
Hbc.installed.select do |cask|
cask.outdated?(greedy?)
end
}).select { |cask| cask.outdated?(true) }

if outdated_casks.empty?
oh1 "No Casks to upgrade"
return
end

oh1 "Upgrading #{Formatter.pluralize(outdated_casks.length, "outdated package")}, with result:"
puts outdated_casks.map { |f| "#{f.full_name} #{f.version}" } * ", "

outdated_casks.each do |old_cask|
odebug "Started upgrade process for Cask #{old_cask}"
raise CaskNotInstalledError, old_cask unless old_cask.installed? || force?

raise CaskUnavailableError.new(old_cask, "The Caskfile is missing!") if old_cask.installed_caskfile.nil?

old_cask = CaskLoader.load(old_cask.installed_caskfile)

old_cask_installer = Installer.new(old_cask, binaries: binaries?, verbose: verbose?, force: force?, upgrade: true)

new_cask = CaskLoader.load(old_cask.to_s)

new_cask_installer =
Installer.new(new_cask, binaries: binaries?,
verbose: verbose?,
force: force?,
skip_cask_deps: skip_cask_deps?,
require_sha: require_sha?,
upgrade: true)

started_upgrade = false
new_artifacts_installed = false

begin
# Start new Cask's installation steps
new_cask_installer.check_conflicts

new_cask_installer.fetch

new_cask_installer.stage

# Move the old Cask's artifacts back to staging
old_cask_installer.start_upgrade
# And flag it so in case of error
started_upgrade = true

# Install the new Cask
new_cask_installer.install_artifacts
new_artifacts_installed = true

new_cask_installer.enable_accessibility_access

# If successful, wipe the old Cask from staging
old_cask_installer.finalize_upgrade
rescue CaskError => e
new_cask_installer.uninstall_artifacts if new_artifacts_installed
new_cask_installer.purge_versioned_files
old_cask_installer.revert_upgrade if started_upgrade
raise e
end
end
end

def self.help
"upgrades all outdated casks"
end
end
end
end
42 changes: 31 additions & 11 deletions Library/Homebrew/cask/lib/hbc/installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Installer

PERSISTENT_METADATA_SUBDIRS = ["gpg"].freeze

def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, binaries: true, verbose: false, require_sha: false)
def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, binaries: true, verbose: false, require_sha: false, upgrade: false)
@cask = cask
@command = command
@force = force
Expand All @@ -28,9 +28,10 @@ def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false
@verbose = verbose
@require_sha = require_sha
@reinstall = false
@upgrade = upgrade
end

attr_predicate :binaries?, :force?, :skip_cask_deps?, :require_sha?, :verbose?
attr_predicate :binaries?, :force?, :skip_cask_deps?, :require_sha?, :upgrade?, :verbose?

def self.print_caveats(cask)
odebug "Printing caveats"
Expand Down Expand Up @@ -82,7 +83,7 @@ def stage
def install
odebug "Hbc::Installer#install"

if @cask.installed? && !force? && !@reinstall
if @cask.installed? && !force? && !@reinstall && !upgrade?
raise CaskAlreadyInstalledError, @cask
end

Expand Down Expand Up @@ -129,13 +130,13 @@ def uninstall_existing_cask
installed_cask = installed_caskfile.exist? ? CaskLoader.load(installed_caskfile) : @cask

# Always force uninstallation, ignore method parameter
Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true).uninstall
Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true, upgrade: upgrade?).uninstall
end

def summary
s = ""
s << "#{Emoji.install_badge} " if Emoji.enabled?
s << "#{@cask} was successfully installed!"
s << "#{@cask} was successfully #{upgrade? ? "upgraded" : "installed"}!"
end

def download
Expand Down Expand Up @@ -367,12 +368,31 @@ def save_caskfile
def uninstall
oh1 "Uninstalling Cask #{@cask}"
disable_accessibility_access
uninstall_artifacts
uninstall_artifacts(clear: true)
purge_versioned_files
purge_caskroom_path if force?
end

def uninstall_artifacts
def start_upgrade
oh1 "Starting upgrade for Cask #{@cask}"

disable_accessibility_access
uninstall_artifacts
end

def revert_upgrade
opoo "Reverting upgrade for Cask #{@cask}"
install_artifacts
enable_accessibility_access
end

def finalize_upgrade
purge_versioned_files

puts summary
end

def uninstall_artifacts(clear: false)
odebug "Un-installing artifacts"
artifacts = @cask.artifacts

Expand All @@ -381,7 +401,7 @@ def uninstall_artifacts
artifacts.each do |artifact|
next unless artifact.respond_to?(:uninstall_phase)
odebug "Un-installing artifact of class #{artifact.class}"
artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?)
artifact.uninstall_phase(command: @command, verbose: verbose?, skip: clear, force: force?)
end
end

Expand All @@ -405,7 +425,7 @@ def gain_permissions_remove(path)
end

def purge_versioned_files
odebug "Purging files for version #{@cask.version} of Cask #{@cask}"
ohai "Purging files for version #{@cask.version} of Cask #{@cask}"

# versioned staged distribution
gain_permissions_remove(@cask.staged_path) if !@cask.staged_path.nil? && @cask.staged_path.exist?
Expand All @@ -420,10 +440,10 @@ def purge_versioned_files
end
end
@cask.metadata_versioned_path.rmdir_if_possible
@cask.metadata_master_container_path.rmdir_if_possible
@cask.metadata_master_container_path.rmdir_if_possible unless upgrade?

# toplevel staged distribution
@cask.caskroom_path.rmdir_if_possible
@cask.caskroom_path.rmdir_if_possible unless upgrade?
end

def purge_caskroom_path
Expand Down
3 changes: 2 additions & 1 deletion Library/Homebrew/test/cask/cli/reinstall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
Already downloaded: .*local-caffeine--1.2.3.zip
==> Verifying checksum for Cask local-caffeine
==> Uninstalling Cask local-caffeine
==> Removing App '.*Caffeine.app'.
==> Moving App 'Caffeine.app' back to '.*Caffeine.app'.
==> Purging files for version 1.2.3 of Cask local-caffeine
==> Installing Cask local-caffeine
==> Moving App 'Caffeine.app' to '.*Caffeine.app'.
.*local-caffeine was successfully installed!
Expand Down
3 changes: 2 additions & 1 deletion Library/Homebrew/test/cask/cli/uninstall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

output = Regexp.new <<~EOS
==> Uninstalling Cask local-caffeine
==> Removing App '.*Caffeine.app'.
==> Moving App 'Caffeine.app' back to '.*Caffeine.app'.
==> Purging files for version 1.2.3 of Cask local-caffeine
EOS

expect {
Expand Down
Loading

0 comments on commit f50ae44

Please sign in to comment.