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
This commit is contained in:
Mike McQuaid 2022-05-16 17:27:13 -04:00
parent 8a5f6645b8
commit 94148c3bc8
No known key found for this signature in database
GPG Key ID: 3338A31AFDB1D829
4 changed files with 24 additions and 23 deletions

View File

@ -46,6 +46,7 @@ module Cask
path.children.select(&:directory?).sort.map do |path|
token = path.basename.to_s
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)
@ -53,7 +54,11 @@ module Cask
else
CaskLoader.load(token, config: config)
end
rescue CaskUnavailableError
# Don't blow up because of a single unavailable cask.
nil
end
end.compact
end
end
end

View File

@ -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

View File

@ -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

View File

@ -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