diff --git a/Library/Homebrew/cask/cmd/upgrade.rb b/Library/Homebrew/cask/cmd/upgrade.rb index 408d8dc545..709f64aa31 100644 --- a/Library/Homebrew/cask/cmd/upgrade.rb +++ b/Library/Homebrew/cask/cmd/upgrade.rb @@ -31,72 +31,74 @@ module Cask ohai "Casks with `auto_updates` or `version :latest` will not be upgraded" if args.empty? && !greedy? oh1 "Upgrading #{outdated_casks.count} #{"outdated package".pluralize(outdated_casks.count)}:" - cask_upgrades = outdated_casks.map do |cask| - if cask.installed_caskfile.nil? - "#{cask.full_name} #{cask.version}" - else - "#{cask.full_name} #{CaskLoader.load(cask.installed_caskfile).version} -> #{cask.version}" + caught_exceptions = [] + outdated_casks.each do |cask| + begin + old_cask = CaskLoader.load(cask.installed_caskfile) + puts "#{cask.full_name} #{old_cask.version} -> #{cask.version}" + upgrade_cask(old_cask) + rescue CaskError => e + caught_exceptions << e + next end end - puts cask_upgrades.join(", ") + return if caught_exceptions.empty? + raise MultipleCaskErrors, caught_exceptions if caught_exceptions.count > 1 + raise caught_exceptions.first if caught_exceptions.count == 1 + end - outdated_casks.each do |old_cask| - odebug "Started upgrade process for Cask #{old_cask}" - raise CaskUnavailableError.new(old_cask, "The Caskfile is missing!") if old_cask.installed_caskfile.nil? + def upgrade_cask(old_cask) + odebug "Started upgrade process for Cask #{old_cask}" + old_config = old_cask.config - old_cask = CaskLoader.load(old_cask.installed_caskfile) + old_cask_installer = + Installer.new(old_cask, binaries: binaries?, + verbose: verbose?, + force: force?, + upgrade: true) - old_config = old_cask.config + new_cask = CaskLoader.load(old_cask.token) - old_cask_installer = - Installer.new(old_cask, binaries: binaries?, - verbose: verbose?, - force: force?, - upgrade: true) + new_cask.config = Config.global.merge(old_config) - new_cask = CaskLoader.load(old_cask.token) + 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, + quarantine: quarantine?) - new_cask.config = Config.global.merge(old_config) + started_upgrade = false + new_artifacts_installed = false - 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, - quarantine: quarantine?) + begin + # Start new Cask's installation steps + new_cask_installer.check_conflicts - started_upgrade = false - new_artifacts_installed = false + puts new_cask_installer.caveats - begin - # Start new Cask's installation steps - new_cask_installer.check_conflicts + new_cask_installer.fetch - puts new_cask_installer.caveats + # 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 - new_cask_installer.fetch + # Install the new Cask + 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 + new_cask_installer.install_artifacts + new_artifacts_installed = true - # Install the new Cask - new_cask_installer.stage - - new_cask_installer.install_artifacts - new_artifacts_installed = true - - # 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 + # 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 diff --git a/Library/Homebrew/cask/exceptions.rb b/Library/Homebrew/cask/exceptions.rb index b3e8519743..576e3f2806 100644 --- a/Library/Homebrew/cask/exceptions.rb +++ b/Library/Homebrew/cask/exceptions.rb @@ -1,6 +1,19 @@ module Cask class CaskError < RuntimeError; end + class MultipleCaskErrors < CaskError + def initialize(errors) + @errors = errors + end + + def to_s + <<~EOS + Problems with multiple casks: + #{@errors.map(&:to_s).join("\n")} + EOS + end + end + class AbstractCaskErrorWithToken < CaskError attr_reader :token attr_reader :reason diff --git a/Library/Homebrew/test/cask/cmd/upgrade_spec.rb b/Library/Homebrew/test/cask/cmd/upgrade_spec.rb index 16b2b82049..704c8ca3e0 100644 --- a/Library/Homebrew/test/cask/cmd/upgrade_spec.rb +++ b/Library/Homebrew/test/cask/cmd/upgrade_spec.rb @@ -222,4 +222,61 @@ describe Cask::Cmd::Upgrade, :cask do expect(bad_checksum.staged_path).not_to exist end end + + context "multiple failures" do + let(:installed) { + [ + "outdated/bad-checksum", + "outdated/local-transmission", + "outdated/bad-checksum2", + ] + } + + before do + installed.each { |cask| Cask::Cmd::Install.run(cask) } + + allow_any_instance_of(described_class).to receive(:verbose?).and_return(true) + end + + it "will not end the upgrade process" do + bad_checksum = Cask::CaskLoader.load("bad-checksum") + bad_checksum_path = bad_checksum.config.appdir.join("Caffeine.app") + + local_transmission = Cask::CaskLoader.load("local-transmission") + local_transmission_path = Cask::Config.global.appdir.join("Transmission.app") + + bad_checksum_2 = Cask::CaskLoader.load("bad-checksum2") + bad_checksum_2_path = bad_checksum_2.config.appdir.join("container") + + expect(bad_checksum).to be_installed + expect(bad_checksum_path).to be_a_directory + expect(bad_checksum.versions).to include("1.2.2") + + expect(local_transmission).to be_installed + expect(local_transmission_path).to be_a_directory + expect(local_transmission.versions).to include("2.60") + + expect(bad_checksum_2).to be_installed + expect(bad_checksum_2_path).to be_a_file + expect(bad_checksum_2.versions).to include("1.2.2") + + expect { + described_class.run + }.to raise_error(Cask::MultipleCaskErrors) + + expect(bad_checksum).to be_installed + expect(bad_checksum_path).to be_a_directory + expect(bad_checksum.versions).to include("1.2.2") + expect(bad_checksum.staged_path).not_to exist + + expect(local_transmission).to be_installed + expect(local_transmission_path).to be_a_directory + expect(local_transmission.versions).to include("2.61") + + expect(bad_checksum_2).to be_installed + expect(bad_checksum_2_path).to be_a_file + expect(bad_checksum_2.versions).to include("1.2.2") + expect(bad_checksum_2.staged_path).not_to exist + end + end end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/bad-checksum2.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/bad-checksum2.rb new file mode 100644 index 0000000000..6fe4729e47 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/bad-checksum2.rb @@ -0,0 +1,9 @@ +cask 'bad-checksum2' do + version '1.2.3' + sha256 'badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb' + + url "file://#{TEST_FIXTURE_DIR}/cask/container.tar.gz" + homepage 'https://brew.sh/container-tar-gz' + + app 'container' +end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/bad-checksum2.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/bad-checksum2.rb new file mode 100644 index 0000000000..efeb1e32ad --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/bad-checksum2.rb @@ -0,0 +1,9 @@ +cask 'bad-checksum2' do + version '1.2.2' + sha256 'fab685fabf73d5a9382581ce8698fce9408f5feaa49fa10d9bc6c510493300f5' + + url "file://#{TEST_FIXTURE_DIR}/cask/container.tar.gz" + homepage 'https://brew.sh/container-tar-gz' + + app 'container' +end