🔨 Refactor Cask exceptions.

This commit is contained in:
Markus Reiter 2017-06-11 02:00:59 +02:00
parent 054ed10cb1
commit 66ce575301
12 changed files with 69 additions and 101 deletions

View File

@ -32,9 +32,9 @@ module Hbc
end end
def load def load
raise CaskError, "'#{@path}' does not exist." unless @path.exist? raise CaskUnavailableError.new(@token, "'#{@path}' does not exist.") unless @path.exist?
raise CaskError, "'#{@path}' is not readable." unless @path.readable? raise CaskUnavailableError.new(@token, "'#{@path}' is not readable.") unless @path.readable?
raise CaskError, "'#{@path}' is not a file." unless @path.file? raise CaskUnavailableError.new(@token, "'#{@path}' is not a file.") unless @path.file?
@content = IO.read(@path) @content = IO.read(@path)
@ -45,7 +45,7 @@ module Hbc
def cask(header_token, &block) def cask(header_token, &block)
if @token != header_token if @token != header_token
raise CaskTokenDoesNotMatchError.new(@token, header_token) raise CaskTokenMismatchError.new(@token, header_token)
end end
Cask.new(header_token, sourcefile_path: @path, &block) Cask.new(header_token, sourcefile_path: @path, &block)
@ -57,10 +57,11 @@ module Hbc
ref.to_s.match?(::URI.regexp) ref.to_s.match?(::URI.regexp)
end end
attr_reader :url
def initialize(url) def initialize(url)
@url = url @url = URI(url)
uri = URI(url) super Hbc.cache/File.basename(@url.path)
super Hbc.cache/File.basename(uri.path)
end end
def load def load
@ -71,7 +72,7 @@ module Hbc
ohai "Downloading #{@url}." ohai "Downloading #{@url}."
curl @url, "-o", @path curl @url, "-o", @path
rescue ErrorDuringExecution rescue ErrorDuringExecution
raise CaskUnavailableError, @url raise CaskUnavailableError.new(@token, "Failed to download #{Formatter.url(@url)}.")
end end
super super
@ -108,7 +109,7 @@ module Hbc
end end
def load def load
raise CaskUnavailableError, @token raise CaskUnavailableError.new(@token, "No Cask with this name exists.")
end end
end end

View File

@ -166,7 +166,7 @@ module Hbc
Hbc.default_tap.install unless Hbc.default_tap.installed? Hbc.default_tap.install unless Hbc.default_tap.installed?
Hbc.init if self.class.should_init?(command) Hbc.init if self.class.should_init?(command)
self.class.run_command(command, *args) self.class.run_command(command, *args)
rescue CaskError, CaskSha256MismatchError, ArgumentError, OptionParser::InvalidOption => e rescue CaskError, ArgumentError, OptionParser::InvalidOption => e
msg = e.message msg = e.message
msg << e.backtrace.join("\n").prepend("\n") if ARGV.debug? msg << e.backtrace.join("\n").prepend("\n") if ARGV.debug?
onoe msg onoe msg

View File

@ -12,7 +12,7 @@ module Hbc
cask_path = CaskLoader.path(cask_token) cask_path = CaskLoader.path(cask_token)
unless cask_path.exist? unless cask_path.exist?
raise CaskUnavailableError, %Q(#{cask_token}, run "brew cask create #{cask_token}" to create a new Cask) raise CaskUnavailableError.new(cask_token, "Run #{Formatter.identifier("brew cask create #{cask_token}")} to create a new Cask.")
end end
odebug "Opening editor for Cask #{cask_token}" odebug "Opening editor for Cask #{cask_token}"

View File

@ -31,14 +31,8 @@ module Hbc
rescue CaskAlreadyInstalledError => e rescue CaskAlreadyInstalledError => e
opoo e.message opoo e.message
count += 1 count += 1
rescue CaskAlreadyInstalledAutoUpdatesError => e
opoo e.message
count += 1
rescue CaskUnavailableError => e rescue CaskUnavailableError => e
self.class.warn_unavailable_with_suggestion cask_token, e self.class.warn_unavailable_with_suggestion cask_token, e
rescue CaskNoShasumError => e
opoo e.message
count += 1
rescue CaskError => e rescue CaskError => e
onoe e.message onoe e.message
end end
@ -51,9 +45,9 @@ module Hbc
exact_match, partial_matches = Search.search(cask_token) exact_match, partial_matches = Search.search(cask_token)
error_message = e.message error_message = e.message
if exact_match if exact_match
error_message.concat(". Did you mean:\n#{exact_match}") error_message.concat(" Did you mean:\n#{exact_match}")
elsif !partial_matches.empty? elsif !partial_matches.empty?
error_message.concat(". Did you mean one of:\n") error_message.concat(" Did you mean one of:\n")
.concat(Formatter.columns(partial_matches.take(20))) .concat(Formatter.columns(partial_matches.take(20)))
end end
onoe error_message onoe error_message

View File

@ -17,9 +17,8 @@ module Hbc
count += 1 count += 1
rescue CaskUnavailableError => e rescue CaskUnavailableError => e
self.class.warn_unavailable_with_suggestion cask_token, e self.class.warn_unavailable_with_suggestion cask_token, e
rescue CaskNoShasumError => e rescue CaskError => e
opoo e.message onoe e.message
count += 1
end end
end end

View File

@ -3,59 +3,43 @@ module Hbc
class AbstractCaskErrorWithToken < CaskError class AbstractCaskErrorWithToken < CaskError
attr_reader :token attr_reader :token
attr_reader :reason
def initialize(token) def initialize(token, reason = nil)
@token = token @token = token
@reason = reason.to_s
end end
end end
class CaskNotInstalledError < AbstractCaskErrorWithToken class CaskNotInstalledError < AbstractCaskErrorWithToken
def to_s def to_s
"#{token} is not installed" "Cask '#{token}' is not installed."
end end
end end
class CaskUnavailableError < AbstractCaskErrorWithToken class CaskUnavailableError < AbstractCaskErrorWithToken
def to_s def to_s
"No available Cask for #{token}" "Cask '#{token}' is unavailable" << (reason.empty? ? "." : ": #{reason}")
end end
end end
class CaskAlreadyCreatedError < AbstractCaskErrorWithToken class CaskAlreadyCreatedError < AbstractCaskErrorWithToken
def to_s def to_s
%Q(A Cask for #{token} already exists. Run "brew cask cat #{token}" to see it.) %Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew cask cat #{token}")} to edit it.)
end end
end end
class CaskAlreadyInstalledError < AbstractCaskErrorWithToken class CaskAlreadyInstalledError < AbstractCaskErrorWithToken
def to_s def to_s
s = <<-EOS.undent
A Cask for #{token} is already installed.
EOS
s.concat("\n").concat(reinstall_message)
end
private
def reinstall_message
<<-EOS.undent <<-EOS.undent
Cask '#{token}' is already installed.
To re-install #{token}, run: To re-install #{token}, run:
brew cask reinstall #{token} #{Formatter.identifier("brew cask reinstall #{token}")}
EOS EOS
end end
end end
class CaskAlreadyInstalledAutoUpdatesError < CaskAlreadyInstalledError
def to_s
s = <<-EOS.undent
A Cask for #{token} is already installed and using auto-updates.
EOS
s.concat("\n").concat(reinstall_message)
end
end
class CaskCommandFailedError < CaskError class CaskCommandFailedError < CaskError
def initialize(cmd, stdout, stderr, status) def initialize(cmd, stdout, stderr, status)
@cmd = cmd @cmd = cmd
@ -84,8 +68,8 @@ module Hbc
class CaskX11DependencyError < AbstractCaskErrorWithToken class CaskX11DependencyError < AbstractCaskErrorWithToken
def to_s def to_s
<<-EOS.undent <<-EOS.undent
#{token} requires XQuartz/X11, which can be installed using Homebrew-Cask by running Cask '#{token}' requires XQuartz/X11, which can be installed using Homebrew-Cask by running
brew cask install xquartz #{Formatter.identifier("brew cask install xquartz")}
or manually, by downloading the package from or manually, by downloading the package from
#{Formatter.url("https://www.xquartz.org/")} #{Formatter.url("https://www.xquartz.org/")}
@ -101,60 +85,67 @@ module Hbc
class CaskUnspecifiedError < CaskError class CaskUnspecifiedError < CaskError
def to_s def to_s
"This command requires a Cask token" "This command requires a Cask token."
end end
end end
class CaskInvalidError < AbstractCaskErrorWithToken class CaskInvalidError < AbstractCaskErrorWithToken
attr_reader :submsg
def initialize(token, *submsg)
super(token)
@submsg = submsg.join(" ")
end
def to_s def to_s
"Cask '#{token}' definition is invalid#{": #{submsg}" unless submsg.empty?}" "Cask '#{token}' definition is invalid" << (reason.empty? ? ".": ": #{reason}")
end end
end end
class CaskTokenDoesNotMatchError < CaskInvalidError class CaskTokenMismatchError < CaskInvalidError
def initialize(token, header_token) def initialize(token, header_token)
super(token, "Bad header line: '#{header_token}' does not match file name") super(token, "Token '#{header_token}' in header line does not match the file name.")
end end
end end
class CaskSha256MissingError < ArgumentError class CaskSha256Error < AbstractCaskErrorWithToken
end attr_reader :expected, :actual
class CaskSha256MismatchError < RuntimeError def initialize(token, expected = nil, actual = nil)
attr_reader :path, :expected, :actual super(token)
def initialize(path, expected, actual)
@path = path
@expected = expected @expected = expected
@actual = actual @actual = actual
end end
end
class CaskSha256MissingError < CaskSha256Error
def to_s
<<-EOS.undent
Cask '#{token}' requires a checksum:
#{Formatter.identifier("sha256 '#{actual}'")}
EOS
end
end
class CaskSha256MismatchError < CaskSha256Error
attr_reader :path
def initialize(token, expected, actual, path)
super(token, expected, actual)
@path = path
end
def to_s def to_s
<<-EOS.undent <<-EOS.undent
sha256 mismatch Checksum for Cask '#{token}' does not match.
Expected: #{expected}
Actual: #{actual} Expected: #{Formatter.success(expected.to_s)}
Actual: #{Formatter.error(actual.to_s)}
File: #{path} File: #{path}
To retry an incomplete download, remove the file above. To retry an incomplete download, remove the file above.
EOS EOS
end end
end end
class CaskNoShasumError < CaskError class CaskNoShasumError < CaskSha256Error
attr_reader :token
def initialize(token)
@token = token
end
def to_s def to_s
<<-EOS.undent <<-EOS.undent
Cask '#{token}' does not have a sha256 checksum defined and was not installed. Cask '#{token}' does not have a sha256 checksum defined and was not installed.
This means you have the "--require-sha" option set, perhaps in your HOMEBREW_CASK_OPTS. This means you have the #{Formatter.identifier("--require-sha")} option set, perhaps in your HOMEBREW_CASK_OPTS.
EOS EOS
end end
end end

View File

@ -94,7 +94,6 @@ module Hbc
odebug "Hbc::Installer#install" odebug "Hbc::Installer#install"
if @cask.installed? && !force? && !@reinstall if @cask.installed? && !force? && !@reinstall
raise CaskAlreadyInstalledAutoUpdatesError, @cask if @cask.auto_updates
raise CaskAlreadyInstalledError, @cask raise CaskAlreadyInstalledError, @cask
end end
@ -143,7 +142,7 @@ module Hbc
def verify_has_sha def verify_has_sha
odebug "Checking cask has checksum" odebug "Checking cask has checksum"
return unless @cask.sha256 == :no_check return unless @cask.sha256 == :no_check
raise CaskNoShasumError, @cask raise CaskNoShasumError, @cask.token
end end
def verify def verify

View File

@ -33,13 +33,13 @@ module Hbc
end end
def verify_checksum def verify_checksum
raise CaskSha256MissingError, "sha256 required: sha256 '#{computed}'" if expected.nil? || expected.empty? raise CaskSha256MissingError.new(cask.token, expected, computed) if expected.nil? || expected.empty?
if expected == computed if expected == computed
odebug "SHA256 checksums match" odebug "SHA256 checksums match"
else else
ohai 'Note: running "brew update" may fix sha256 checksum errors' ohai 'Note: running "brew update" may fix sha256 checksum errors'
raise CaskSha256MismatchError.new(downloaded_path, expected, computed) raise CaskSha256MismatchError.new(cask.token, expected, computed, downloaded_path)
end end
end end
end end

View File

@ -41,7 +41,7 @@ describe Hbc::CLI::Install, :cask do
expect { expect {
Hbc::CLI::Install.run("local-transmission") Hbc::CLI::Install.run("local-transmission")
}.to output(/Warning: A Cask for local-transmission is already installed./).to_stderr }.to output(/Warning: Cask 'local-transmission' is already installed./).to_stderr
end end
it "allows double install with --force" do it "allows double install with --force" do
@ -80,7 +80,7 @@ describe Hbc::CLI::Install, :cask do
rescue Hbc::CaskError rescue Hbc::CaskError
nil nil
end end
}.to output(/No available Cask for localcaffeine\. Did you mean:\nlocal-caffeine/).to_stderr }.to output(/Cask 'localcaffeine' is unavailable\. Did you mean:\nlocal-caffeine/).to_stderr
end end
it "returns multiple suggestions for a Cask fragment" do it "returns multiple suggestions for a Cask fragment" do
@ -90,7 +90,7 @@ describe Hbc::CLI::Install, :cask do
rescue Hbc::CaskError rescue Hbc::CaskError
nil nil
end end
}.to output(/No available Cask for local-caf\. Did you mean one of:\nlocal-caffeine/).to_stderr }.to output(/Cask 'local-caf' is unavailable\. Did you mean one of:\nlocal-caffeine/).to_stderr
end end
describe "when no Cask is specified" do describe "when no Cask is specified" do

View File

@ -57,7 +57,7 @@ describe Hbc::DSL, :cask do
it "raises an error" do it "raises an error" do
expect { expect {
cask cask
}.to raise_error(Hbc::CaskTokenDoesNotMatchError, /Bad header line:.*does not match file name/) }.to raise_error(Hbc::CaskTokenMismatchError, /header line does not match the file name/)
end end
end end

View File

@ -145,22 +145,6 @@ describe Hbc::Installer, :cask do
expect(with_macosx_dir.staged_path.join("__MACOSX")).not_to be_a_directory expect(with_macosx_dir.staged_path.join("__MACOSX")).not_to be_a_directory
end end
it "installer method raises an exception when already-installed Casks which auto-update are attempted" do
with_auto_updates = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/auto-updates.rb")
expect(with_auto_updates).not_to be_installed
installer = Hbc::Installer.new(with_auto_updates)
shutup do
installer.install
end
expect {
installer.install
}.to raise_error(Hbc::CaskAlreadyInstalledAutoUpdatesError)
end
it "allows already-installed Casks which auto-update to be installed if force is provided" do it "allows already-installed Casks which auto-update to be installed if force is provided" do
with_auto_updates = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/auto-updates.rb") with_auto_updates = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/auto-updates.rb")

View File

@ -1,5 +1,5 @@
describe Hbc::Verify::Checksum, :cask do describe Hbc::Verify::Checksum, :cask do
let(:cask) { double("cask") } let(:cask) { double("cask", token: "cask") }
let(:downloaded_path) { double("downloaded_path") } let(:downloaded_path) { double("downloaded_path") }
let(:verification) { described_class.new(cask, downloaded_path) } let(:verification) { described_class.new(cask, downloaded_path) }