From 94148c3bc8a17432c58aee0f41414a0a3c53f87c Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 16 May 2022 17:27:13 -0400 Subject: [PATCH] Fix handling unreadable casks When casks are unreadable (e.g. have invalid syntax, the cask file cannot be found) then it's not been possible to uninstall them, list them or perform any operation which iterates through all casks. Handle these various cases by falling back to creating a `Cask::Cask` object using just the name/token and latest installed version on disk. This provides enough functionality to be able to verbosely list these casks, not error on listing and, most importantly, uninstall/reinstall them. Fixes https://github.com/Homebrew/homebrew-cask/issues/62223 --- Library/Homebrew/cask/caskroom.rb | 19 ++++++++++++------- Library/Homebrew/cask/cmd/uninstall.rb | 9 +-------- Library/Homebrew/cask/cmd/zap.rb | 9 +-------- Library/Homebrew/cli/named_args.rb | 10 ++++++++++ 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Library/Homebrew/cask/caskroom.rb b/Library/Homebrew/cask/caskroom.rb index a6012c19c2..29558f7476 100644 --- a/Library/Homebrew/cask/caskroom.rb +++ b/Library/Homebrew/cask/caskroom.rb @@ -46,14 +46,19 @@ module Cask path.children.select(&:directory?).sort.map do |path| token = path.basename.to_s - if (tap_path = CaskLoader.tap_paths(token).first) - CaskLoader::FromTapPathLoader.new(tap_path).load(config: config) - elsif (caskroom_path = Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first) - CaskLoader::FromPathLoader.new(caskroom_path).load(config: config) - else - CaskLoader.load(token, config: config) + begin + if (tap_path = CaskLoader.tap_paths(token).first) + CaskLoader::FromTapPathLoader.new(tap_path).load(config: config) + elsif (caskroom_path = Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first) + CaskLoader::FromPathLoader.new(caskroom_path).load(config: config) + else + CaskLoader.load(token, config: config) + end + rescue CaskUnavailableError + # Don't blow up because of a single unavailable cask. + nil end - end + end.compact end end end diff --git a/Library/Homebrew/cask/cmd/uninstall.rb b/Library/Homebrew/cask/cmd/uninstall.rb index 40a70ca56c..1ab2c8ce2d 100644 --- a/Library/Homebrew/cask/cmd/uninstall.rb +++ b/Library/Homebrew/cask/cmd/uninstall.rb @@ -39,14 +39,7 @@ module Cask casks.each do |cask| odebug "Uninstalling Cask #{cask}" - if cask.installed? - if (installed_caskfile = cask.installed_caskfile) && installed_caskfile.exist? - # Use the same cask file that was used for installation, if possible. - cask = CaskLoader.load(installed_caskfile) - end - else - raise CaskNotInstalledError, cask unless force - end + raise CaskNotInstalledError, cask if !cask.installed? && !force Installer.new(cask, **options).uninstall diff --git a/Library/Homebrew/cask/cmd/zap.rb b/Library/Homebrew/cask/cmd/zap.rb index 34f80fe7e1..b06bfa4223 100644 --- a/Library/Homebrew/cask/cmd/zap.rb +++ b/Library/Homebrew/cask/cmd/zap.rb @@ -32,14 +32,7 @@ module Cask casks.each do |cask| odebug "Zapping Cask #{cask}" - if cask.installed? - if (installed_caskfile = cask.installed_caskfile) && installed_caskfile.exist? - # Use the same cask file that was used for installation, if possible. - cask = CaskLoader.load(installed_caskfile) - end - else - raise CaskNotInstalledError, cask unless force - end + raise CaskNotInstalledError, cask if !cask.installed? && !force Installer.new(cask, verbose: verbose, force: force).zap end diff --git a/Library/Homebrew/cli/named_args.rb b/Library/Homebrew/cli/named_args.rb index 3e1fd33e68..0d06f1274e 100644 --- a/Library/Homebrew/cli/named_args.rb +++ b/Library/Homebrew/cli/named_args.rb @@ -148,6 +148,16 @@ module Homebrew return cask rescue Cask::CaskUnreadableError => e + # If we're trying to get a keg-like Cask, do our best to handle it + # not being readable and return something that can be used. + if [:latest_kegs, :default_kegs, :kegs].include?(method) + cask_version = Cask::Cask.new(name, config: config).versions.first + cask = Cask::Cask.new(name, config: config) do + version cask_version if cask_version + end + return cask + end + # Need to rescue before `CaskUnavailableError` (superclass of this) # The cask was found, but there's a problem with its implementation unreadable_error ||= e