Merge pull request #8719 from reitermarkus/cask-audit-errors

Refactor `cask audit` warnings.
This commit is contained in:
Markus Reiter 2020-09-14 13:43:52 +02:00 committed by GitHub
commit 7589a350cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 194 additions and 178 deletions

View File

@ -30,8 +30,8 @@ module Cask
appcast = online if appcast.nil? appcast = online if appcast.nil?
download = online if download.nil? download = online if download.nil?
# `strict` implies `token_conflicts` # `new_cask` implies `token_conflicts`
token_conflicts = strict if token_conflicts.nil? token_conflicts = new_cask if token_conflicts.nil?
@cask = cask @cask = cask
@appcast = appcast @appcast = appcast
@ -91,7 +91,11 @@ module Cask
end end
def add_warning(message) def add_warning(message)
warnings << message if strict?
add_error message
else
warnings << message
end
end end
def errors? def errors?
@ -143,7 +147,7 @@ module Cask
return unless cask.artifacts.any? { |k| k.is_a?(Artifact::Pkg) && k.stanza_options.key?(:allow_untrusted) } return unless cask.artifacts.any? { |k| k.is_a?(Artifact::Pkg) && k.stanza_options.key?(:allow_untrusted) }
add_warning "allow_untrusted is not permitted in official Homebrew Cask taps" add_error "allow_untrusted is not permitted in official Homebrew Cask taps"
end end
def check_stanza_requires_uninstall def check_stanza_requires_uninstall
@ -152,14 +156,14 @@ module Cask
return if cask.artifacts.none? { |k| k.is_a?(Artifact::Pkg) || k.is_a?(Artifact::Installer) } return if cask.artifacts.none? { |k| k.is_a?(Artifact::Pkg) || k.is_a?(Artifact::Installer) }
return if cask.artifacts.any? { |k| k.is_a?(Artifact::Uninstall) } return if cask.artifacts.any? { |k| k.is_a?(Artifact::Uninstall) }
add_warning "installer and pkg stanzas require an uninstall stanza" add_error "installer and pkg stanzas require an uninstall stanza"
end end
def check_single_pre_postflight def check_single_pre_postflight
odebug "Auditing preflight and postflight stanzas" odebug "Auditing preflight and postflight stanzas"
if cask.artifacts.count { |k| k.is_a?(Artifact::PreflightBlock) && k.directives.key?(:preflight) } > 1 if cask.artifacts.count { |k| k.is_a?(Artifact::PreflightBlock) && k.directives.key?(:preflight) } > 1
add_warning "only a single preflight stanza is allowed" add_error "only a single preflight stanza is allowed"
end end
count = cask.artifacts.count do |k| count = cask.artifacts.count do |k|
@ -168,14 +172,14 @@ module Cask
end end
return unless count > 1 return unless count > 1
add_warning "only a single postflight stanza is allowed" add_error "only a single postflight stanza is allowed"
end end
def check_single_uninstall_zap def check_single_uninstall_zap
odebug "Auditing single uninstall_* and zap stanzas" odebug "Auditing single uninstall_* and zap stanzas"
if cask.artifacts.count { |k| k.is_a?(Artifact::Uninstall) } > 1 if cask.artifacts.count { |k| k.is_a?(Artifact::Uninstall) } > 1
add_warning "only a single uninstall stanza is allowed" add_error "only a single uninstall stanza is allowed"
end end
count = cask.artifacts.count do |k| count = cask.artifacts.count do |k|
@ -183,18 +187,18 @@ module Cask
k.directives.key?(:uninstall_preflight) k.directives.key?(:uninstall_preflight)
end end
add_warning "only a single uninstall_preflight stanza is allowed" if count > 1 add_error "only a single uninstall_preflight stanza is allowed" if count > 1
count = cask.artifacts.count do |k| count = cask.artifacts.count do |k|
k.is_a?(Artifact::PostflightBlock) && k.is_a?(Artifact::PostflightBlock) &&
k.directives.key?(:uninstall_postflight) k.directives.key?(:uninstall_postflight)
end end
add_warning "only a single uninstall_postflight stanza is allowed" if count > 1 add_error "only a single uninstall_postflight stanza is allowed" if count > 1
return unless cask.artifacts.count { |k| k.is_a?(Artifact::Zap) } > 1 return unless cask.artifacts.count { |k| k.is_a?(Artifact::Zap) } > 1
add_warning "only a single zap stanza is allowed" add_error "only a single zap stanza is allowed"
end end
def check_required_stanzas def check_required_stanzas
@ -267,14 +271,14 @@ module Cask
return unless cask.version.latest? return unless cask.version.latest?
return unless cask.appcast return unless cask.appcast
add_warning "Casks with an appcast should not use version :latest" add_error "Casks with an appcast should not use version :latest"
end end
def check_latest_with_auto_updates def check_latest_with_auto_updates
return unless cask.version.latest? return unless cask.version.latest?
return unless cask.auto_updates return unless cask.auto_updates
add_warning "Casks with `version :latest` should not use `auto_updates`" add_error "Casks with `version :latest` should not use `auto_updates`"
end end
def check_hosting_with_appcast def check_hosting_with_appcast
@ -286,21 +290,19 @@ module Cask
when %r{github.com/([^/]+)/([^/]+)/releases/download/(\S+)} when %r{github.com/([^/]+)/([^/]+)/releases/download/(\S+)}
return if cask.version.latest? return if cask.version.latest?
add_warning "Download uses GitHub releases, #{add_appcast}" add_error "Download uses GitHub releases, #{add_appcast}"
when %r{sourceforge.net/(\S+)} when %r{sourceforge.net/(\S+)}
return if cask.version.latest? return if cask.version.latest?
add_warning "Download is hosted on SourceForge, #{add_appcast}" add_error "Download is hosted on SourceForge, #{add_appcast}"
when %r{dl.devmate.com/(\S+)} when %r{dl.devmate.com/(\S+)}
add_warning "Download is hosted on DevMate, #{add_appcast}" add_error "Download is hosted on DevMate, #{add_appcast}"
when %r{rink.hockeyapp.net/(\S+)} when %r{rink.hockeyapp.net/(\S+)}
add_warning "Download is hosted on HockeyApp, #{add_appcast}" add_error "Download is hosted on HockeyApp, #{add_appcast}"
end end
end end
def check_desc def check_desc
return unless new_cask?
return if cask.desc.present? return if cask.desc.present?
add_warning "Cask should have a description. Please add a `desc` stanza." add_warning "Cask should have a description. Please add a `desc` stanza."
@ -315,9 +317,9 @@ module Cask
def check_download_url_format def check_download_url_format
odebug "Auditing URL format" odebug "Auditing URL format"
if bad_sourceforge_url? if bad_sourceforge_url?
add_warning "SourceForge URL format incorrect. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#sourceforgeosdn-urls" add_error "SourceForge URL format incorrect. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#sourceforgeosdn-urls"
elsif bad_osdn_url? elsif bad_osdn_url?
add_warning "OSDN URL format incorrect. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#sourceforgeosdn-urls" add_error "OSDN URL format incorrect. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#sourceforgeosdn-urls"
end end
end end
@ -363,42 +365,33 @@ module Cask
end end
def check_token_valid def check_token_valid
return unless strict? add_error "cask token contains non-ascii characters" unless cask.token.ascii_only?
add_error "cask token + should be replaced by -plus-" if cask.token.include? "+"
add_warning "cask token is not lowercase" if cask.token.downcase! add_error "cask token whitespace should be replaced by hyphens" if cask.token.include? " "
add_error "cask token @ should be replaced by -at-" if cask.token.include? "@"
add_warning "cask token contains non-ascii characters" unless cask.token.ascii_only? add_error "cask token underscores should be replaced by hyphens" if cask.token.include? "_"
add_error "cask token should not contain double hyphens" if cask.token.include? "--"
add_warning "cask token + should be replaced by -plus-" if cask.token.include? "+"
add_warning "cask token @ should be replaced by -at-" if cask.token.include? "@"
add_warning "cask token whitespace should be replaced by hyphens" if cask.token.include? " "
add_warning "cask token underscores should be replaced by hyphens" if cask.token.include? "_"
if cask.token.match?(/[^a-z0-9\-]/) if cask.token.match?(/[^a-z0-9\-]/)
add_warning "cask token should only contain alphanumeric characters and hyphens" add_error "cask token should only contain lowercase alphanumeric characters and hyphens"
end end
add_warning "cask token should not contain double hyphens" if cask.token.include? "--" return unless cask.token.start_with?("-") || cask.token.end_with?("-")
return unless cask.token.end_with?("-") || cask.token.start_with?("-") add_error "cask token should not have leading or trailing hyphens"
add_warning "cask token should not have leading or trailing hyphens"
end end
def check_token_bad_words def check_token_bad_words
return unless strict? return unless new_cask?
token = cask.token token = cask.token
add_warning "cask token contains .app" if token.end_with? ".app" add_error "cask token contains .app" if token.end_with? ".app"
if /-(?<designation>alpha|beta|rc|release-candidate)$/ =~ cask.token && if /-(?<designation>alpha|beta|rc|release-candidate)$/ =~ cask.token &&
cask.tap&.official? && cask.tap&.official? &&
cask.tap != "homebrew/cask-versions" cask.tap != "homebrew/cask-versions"
add_warning "cask token contains version designation '#{designation}'" add_error "cask token contains version designation '#{designation}'"
end end
add_warning "cask token mentions launcher" if token.end_with? "launcher" add_warning "cask token mentions launcher" if token.end_with? "launcher"
@ -433,7 +426,7 @@ module Cask
downloaded_path = download.perform downloaded_path = download.perform
Verify.all(cask, downloaded_path) Verify.all(cask, downloaded_path)
rescue => e rescue => e
add_error "download not possible: #{e.message}" add_error "download not possible: #{e}"
end end
def check_appcast_contains_version def check_appcast_contains_version
@ -458,7 +451,7 @@ module Cask
end end
return if appcast_contents.include? adjusted_version_stanza return if appcast_contents.include? adjusted_version_stanza
add_warning "appcast at URL '#{appcast_stanza}' does not contain"\ add_error "appcast at URL '#{appcast_stanza}' does not contain"\
" the version number '#{adjusted_version_stanza}':\n#{appcast_contents}" " the version number '#{adjusted_version_stanza}':\n#{appcast_contents}"
end end

View File

@ -59,8 +59,6 @@ module Cask
odebug "Auditing Cask #{cask}" odebug "Auditing Cask #{cask}"
result = Auditor.audit(cask, **options) result = Auditor.audit(cask, **options)
next true if result[:warnings].empty? && result[:errors].empty?
if ENV["GITHUB_ACTIONS"] if ENV["GITHUB_ACTIONS"]
cask_path = cask.sourcefile_path cask_path = cask.sourcefile_path
annotations = (result[:warnings].map { |w| [:warning, w] } + result[:errors].map { |e| [:error, e] }) annotations = (result[:warnings].map { |w| [:warning, w] } + result[:errors].map { |e| [:error, e] })
@ -71,7 +69,7 @@ module Cask
end end
end end
false result[:errors].empty?
end end
return if failed_casks.empty? return if failed_casks.empty?

View File

@ -17,15 +17,15 @@ describe Cask::Audit, :cask do
end end
end end
matcher :fail_with do |error_msg| matcher :fail_with do |message|
match do |audit| match do |audit|
include_msg?(audit.errors, error_msg) include_msg?(audit.errors, message)
end end
end end
matcher :warn_with do |warning_msg| matcher :warn_with do |message|
match do |audit| match do |audit|
include_msg?(audit.warnings, warning_msg) include_msg?(audit.warnings, message)
end end
end end
@ -53,6 +53,10 @@ describe Cask::Audit, :cask do
it "implies `strict`" do it "implies `strict`" do
expect(audit).to be_strict expect(audit).to be_strict
end end
it "implies `token_conflicts`" do
expect(audit.token_conflicts?).to be true
end
end end
context "when `online` is specified" do context "when `online` is specified" do
@ -66,14 +70,6 @@ describe Cask::Audit, :cask do
expect(audit.download).to be_truthy expect(audit.download).to be_truthy
end end
end end
context "when `strict` is specified" do
let(:strict) { true }
it "implies `token_conflicts`" do
expect(audit.token_conflicts?).to be true
end
end
end end
describe "#result" do describe "#result" do
@ -152,78 +148,87 @@ describe Cask::Audit, :cask do
context "when cask token is not lowercase" do context "when cask token is not lowercase" do
let(:cask_token) { "Upper-Case" } let(:cask_token) { "Upper-Case" }
it "warns about lowercase" do it "fails" do
expect(subject).to warn_with(/token is not lowercase/) expect(subject).to fail_with(/lowercase/)
end end
end end
context "when cask token is not ascii" do context "when cask token is not ascii" do
let(:cask_token) { "ascii⌘" } let(:cask_token) { "ascii⌘" }
it "warns about ascii" do it "fails" do
expect(subject).to warn_with(/contains non-ascii characters/) expect(subject).to fail_with(/contains non-ascii characters/)
end end
end end
context "when cask token has +" do context "when cask token has +" do
let(:cask_token) { "app++" } let(:cask_token) { "app++" }
it "warns about +" do it "fails" do
expect(subject).to warn_with(/\+ should be replaced by -plus-/) expect(subject).to fail_with(/\+ should be replaced by -plus-/)
end end
end end
context "when cask token has @" do context "when cask token has @" do
let(:cask_token) { "app@stuff" } let(:cask_token) { "app@stuff" }
it "warns about +" do it "fails" do
expect(subject).to warn_with(/@ should be replaced by -at-/) expect(subject).to fail_with(/@ should be replaced by -at-/)
end end
end end
context "when cask token has whitespace" do context "when cask token has whitespace" do
let(:cask_token) { "app stuff" } let(:cask_token) { "app stuff" }
it "warns about whitespace" do it "fails" do
expect(subject).to warn_with(/whitespace should be replaced by hyphens/) expect(subject).to fail_with(/whitespace should be replaced by hyphens/)
end end
end end
context "when cask token has underscores" do context "when cask token has underscores" do
let(:cask_token) { "app_stuff" } let(:cask_token) { "app_stuff" }
it "warns about underscores" do it "fails" do
expect(subject).to warn_with(/underscores should be replaced by hyphens/) expect(subject).to fail_with(/underscores should be replaced by hyphens/)
end end
end end
context "when cask token has non-alphanumeric characters" do context "when cask token has non-alphanumeric characters" do
let(:cask_token) { "app(stuff)" } let(:cask_token) { "app(stuff)" }
it "warns about non-alphanumeric characters" do it "fails" do
expect(subject).to warn_with(/should only contain alphanumeric characters and hyphens/) expect(subject).to fail_with(/alphanumeric characters and hyphens/)
end end
end end
context "when cask token has double hyphens" do context "when cask token has double hyphens" do
let(:cask_token) { "app--stuff" } let(:cask_token) { "app--stuff" }
it "warns about double hyphens" do it "fails" do
expect(subject).to warn_with(/should not contain double hyphens/) expect(subject).to fail_with(/should not contain double hyphens/)
end
end
context "when cask token has leading hyphens" do
let(:cask_token) { "-app" }
it "fails" do
expect(subject).to fail_with(/should not have leading or trailing hyphens/)
end end
end end
context "when cask token has trailing hyphens" do context "when cask token has trailing hyphens" do
let(:cask_token) { "app-" } let(:cask_token) { "app-" }
it "warns about trailing hyphens" do it "fails" do
expect(subject).to warn_with(/should not have leading or trailing hyphens/) expect(subject).to fail_with(/should not have leading or trailing hyphens/)
end end
end end
end end
describe "token bad words" do describe "token bad words" do
let(:strict) { true } let(:new_cask) { true }
let(:online) { false }
let(:cask) do let(:cask) do
tmp_cask cask_token.to_s, <<~RUBY tmp_cask cask_token.to_s, <<~RUBY
cask '#{cask_token}' do cask '#{cask_token}' do
@ -231,6 +236,7 @@ describe Cask::Audit, :cask do
sha256 '8dd95daa037ac02455435446ec7bc737b34567afe9156af7d20b2a83805c1d8a' sha256 '8dd95daa037ac02455435446ec7bc737b34567afe9156af7d20b2a83805c1d8a'
url "https://brew.sh/" url "https://brew.sh/"
name 'Audit' name 'Audit'
desc 'Cask for testing tokens'
homepage 'https://brew.sh/' homepage 'https://brew.sh/'
app 'Audit.app' app 'Audit.app'
end end
@ -240,72 +246,72 @@ describe Cask::Audit, :cask do
context "when cask token contains .app" do context "when cask token contains .app" do
let(:cask_token) { "token.app" } let(:cask_token) { "token.app" }
it "warns about .app" do it "fails" do
expect(subject).to warn_with(/token contains .app/) expect(subject).to fail_with(/token contains .app/)
end end
end end
context "when cask token contains version designation" do context "when cask token contains version designation" do
let(:cask_token) { "token-beta" } let(:cask_token) { "token-beta" }
it "warns about version in token if the cask is from an official tap" do it "fails if the cask is from an official tap" do
allow(cask).to receive(:tap).and_return(Tap.fetch("homebrew/cask")) allow(cask).to receive(:tap).and_return(Tap.fetch("homebrew/cask"))
expect(subject).to warn_with(/token contains version designation/) expect(subject).to fail_with(/token contains version designation/)
end end
it "does not warn about version in token if the cask is from the `cask-versions` tap" do it "does not fail if the cask is from the `cask-versions` tap" do
allow(cask).to receive(:tap).and_return(Tap.fetch("homebrew/cask-versions")) allow(cask).to receive(:tap).and_return(Tap.fetch("homebrew/cask-versions"))
expect(subject).not_to warn_with(/token contains version designation/) expect(subject).to pass
end end
end end
context "when cask token contains launcher" do context "when cask token contains launcher" do
let(:cask_token) { "token-launcher" } let(:cask_token) { "token-launcher" }
it "warns about launcher in token" do it "fails" do
expect(subject).to warn_with(/token mentions launcher/) expect(subject).to fail_with(/token mentions launcher/)
end end
end end
context "when cask token contains desktop" do context "when cask token contains desktop" do
let(:cask_token) { "token-desktop" } let(:cask_token) { "token-desktop" }
it "warns about desktop in token" do it "fails" do
expect(subject).to warn_with(/token mentions desktop/) expect(subject).to fail_with(/token mentions desktop/)
end end
end end
context "when cask token contains platform" do context "when cask token contains platform" do
let(:cask_token) { "token-osx" } let(:cask_token) { "token-osx" }
it "warns about platform in token" do it "fails" do
expect(subject).to warn_with(/token mentions platform/) expect(subject).to fail_with(/token mentions platform/)
end end
end end
context "when cask token contains architecture" do context "when cask token contains architecture" do
let(:cask_token) { "token-x86" } let(:cask_token) { "token-x86" }
it "warns about architecture in token" do it "fails" do
expect(subject).to warn_with(/token mentions architecture/) expect(subject).to fail_with(/token mentions architecture/)
end end
end end
context "when cask token contains framework" do context "when cask token contains framework" do
let(:cask_token) { "token-java" } let(:cask_token) { "token-java" }
it "warns about framework in token" do it "fails" do
expect(subject).to warn_with(/cask token mentions framework/) expect(subject).to fail_with(/cask token mentions framework/)
end end
end end
context "when cask token is framework" do context "when cask token is framework" do
let(:cask_token) { "java" } let(:cask_token) { "java" }
it "does not warn about framework" do it "does not fail" do
expect(subject).not_to warn_with(/token contains version/) expect(subject).to pass
end end
end end
end end
@ -358,206 +364,206 @@ describe Cask::Audit, :cask do
end end
describe "pkg allow_untrusted checks" do describe "pkg allow_untrusted checks" do
let(:warning_msg) { "allow_untrusted is not permitted in official Homebrew Cask taps" } let(:message) { "allow_untrusted is not permitted in official Homebrew Cask taps" }
context "when the Cask has no pkg stanza" do context "when the Cask has no pkg stanza" do
let(:cask_token) { "basic-cask" } let(:cask_token) { "basic-cask" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask does not have allow_untrusted" do context "when the Cask does not have allow_untrusted" do
let(:cask_token) { "with-uninstall-pkgutil" } let(:cask_token) { "with-uninstall-pkgutil" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has allow_untrusted" do context "when the Cask has allow_untrusted" do
let(:cask_token) { "with-allow-untrusted" } let(:cask_token) { "with-allow-untrusted" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "when the Cask stanza requires uninstall" do describe "when the Cask stanza requires uninstall" do
let(:warning_msg) { "installer and pkg stanzas require an uninstall stanza" } let(:message) { "installer and pkg stanzas require an uninstall stanza" }
context "when the Cask does not require an uninstall" do context "when the Cask does not require an uninstall" do
let(:cask_token) { "basic-cask" } let(:cask_token) { "basic-cask" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the pkg Cask has an uninstall" do context "when the pkg Cask has an uninstall" do
let(:cask_token) { "with-uninstall-pkgutil" } let(:cask_token) { "with-uninstall-pkgutil" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the installer Cask has an uninstall" do context "when the installer Cask has an uninstall" do
let(:cask_token) { "installer-with-uninstall" } let(:cask_token) { "installer-with-uninstall" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the installer Cask does not have an uninstall" do context "when the installer Cask does not have an uninstall" do
let(:cask_token) { "with-installer-manual" } let(:cask_token) { "with-installer-manual" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
context "when the pkg Cask does not have an uninstall" do context "when the pkg Cask does not have an uninstall" do
let(:cask_token) { "pkg-without-uninstall" } let(:cask_token) { "pkg-without-uninstall" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "preflight stanza checks" do describe "preflight stanza checks" do
let(:warning_msg) { "only a single preflight stanza is allowed" } let(:message) { "only a single preflight stanza is allowed" }
context "when the Cask has no preflight stanza" do context "when the Cask has no preflight stanza" do
let(:cask_token) { "with-zap-rmdir" } let(:cask_token) { "with-zap-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has only one preflight stanza" do context "when the Cask has only one preflight stanza" do
let(:cask_token) { "with-preflight" } let(:cask_token) { "with-preflight" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has multiple preflight stanzas" do context "when the Cask has multiple preflight stanzas" do
let(:cask_token) { "with-preflight-multi" } let(:cask_token) { "with-preflight-multi" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "uninstall_postflight stanza checks" do describe "postflight stanza checks" do
let(:warning_msg) { "only a single postflight stanza is allowed" } let(:message) { "only a single postflight stanza is allowed" }
context "when the Cask has no postflight stanza" do context "when the Cask has no postflight stanza" do
let(:cask_token) { "with-zap-rmdir" } let(:cask_token) { "with-zap-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has only one postflight stanza" do context "when the Cask has only one postflight stanza" do
let(:cask_token) { "with-postflight" } let(:cask_token) { "with-postflight" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has multiple postflight stanzas" do context "when the Cask has multiple postflight stanzas" do
let(:cask_token) { "with-postflight-multi" } let(:cask_token) { "with-postflight-multi" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "uninstall stanza checks" do describe "uninstall stanza checks" do
let(:warning_msg) { "only a single uninstall stanza is allowed" } let(:message) { "only a single uninstall stanza is allowed" }
context "when the Cask has no uninstall stanza" do context "when the Cask has no uninstall stanza" do
let(:cask_token) { "with-zap-rmdir" } let(:cask_token) { "with-zap-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has only one uninstall stanza" do context "when the Cask has only one uninstall stanza" do
let(:cask_token) { "with-uninstall-rmdir" } let(:cask_token) { "with-uninstall-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has multiple uninstall stanzas" do context "when the Cask has multiple uninstall stanzas" do
let(:cask_token) { "with-uninstall-multi" } let(:cask_token) { "with-uninstall-multi" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "uninstall_preflight stanza checks" do describe "uninstall_preflight stanza checks" do
let(:warning_msg) { "only a single uninstall_preflight stanza is allowed" } let(:message) { "only a single uninstall_preflight stanza is allowed" }
context "when the Cask has no uninstall_preflight stanza" do context "when the Cask has no uninstall_preflight stanza" do
let(:cask_token) { "with-zap-rmdir" } let(:cask_token) { "with-zap-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has only one uninstall_preflight stanza" do context "when the Cask has only one uninstall_preflight stanza" do
let(:cask_token) { "with-uninstall-preflight" } let(:cask_token) { "with-uninstall-preflight" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has multiple uninstall_preflight stanzas" do context "when the Cask has multiple uninstall_preflight stanzas" do
let(:cask_token) { "with-uninstall-preflight-multi" } let(:cask_token) { "with-uninstall-preflight-multi" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "uninstall_postflight stanza checks" do describe "uninstall_postflight stanza checks" do
let(:warning_msg) { "only a single uninstall_postflight stanza is allowed" } let(:message) { "only a single uninstall_postflight stanza is allowed" }
context "when the Cask has no uninstall_postflight stanza" do context "when the Cask has no uninstall_postflight stanza" do
let(:cask_token) { "with-zap-rmdir" } let(:cask_token) { "with-zap-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has only one uninstall_postflight stanza" do context "when the Cask has only one uninstall_postflight stanza" do
let(:cask_token) { "with-uninstall-postflight" } let(:cask_token) { "with-uninstall-postflight" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has multiple uninstall_postflight stanzas" do context "when the Cask has multiple uninstall_postflight stanzas" do
let(:cask_token) { "with-uninstall-postflight-multi" } let(:cask_token) { "with-uninstall-postflight-multi" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "zap stanza checks" do describe "zap stanza checks" do
let(:warning_msg) { "only a single zap stanza is allowed" } let(:message) { "only a single zap stanza is allowed" }
context "when the Cask has no zap stanza" do context "when the Cask has no zap stanza" do
let(:cask_token) { "with-uninstall-rmdir" } let(:cask_token) { "with-uninstall-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has only one zap stanza" do context "when the Cask has only one zap stanza" do
let(:cask_token) { "with-zap-rmdir" } let(:cask_token) { "with-zap-rmdir" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask has multiple zap stanzas" do context "when the Cask has multiple zap stanzas" do
let(:cask_token) { "with-zap-multi" } let(:cask_token) { "with-zap-multi" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "version checks" do describe "version checks" do
let(:error_msg) { "you should use version :latest instead of version 'latest'" } let(:message) { "you should use version :latest instead of version 'latest'" }
context "when version is 'latest'" do context "when version is 'latest'" do
let(:cask_token) { "version-latest-string" } let(:cask_token) { "version-latest-string" }
it { is_expected.to fail_with(error_msg) } it { is_expected.to fail_with(message) }
end end
context "when version is :latest" do context "when version is :latest" do
let(:cask_token) { "version-latest-with-checksum" } let(:cask_token) { "version-latest-with-checksum" }
it { is_expected.not_to fail_with(error_msg) } it { is_expected.not_to fail_with(message) }
end end
end end
@ -582,93 +588,93 @@ describe Cask::Audit, :cask do
end end
describe "hosting with appcast checks" do describe "hosting with appcast checks" do
let(:appcast_warning) { /please add an appcast/ } let(:message) { /please add an appcast/ }
context "when the download does not use hosting with an appcast" do context "when the download does not use hosting with an appcast" do
let(:cask_token) { "basic-cask" } let(:cask_token) { "basic-cask" }
it { is_expected.not_to warn_with(appcast_warning) } it { is_expected.not_to fail_with(message) }
end end
context "when the download uses GitHub releases and has an appcast" do context "when the download uses GitHub releases and has an appcast" do
let(:cask_token) { "github-with-appcast" } let(:cask_token) { "github-with-appcast" }
it { is_expected.not_to warn_with(appcast_warning) } it { is_expected.not_to fail_with(message) }
end end
context "when the download uses GitHub releases and does not have an appcast" do context "when the download uses GitHub releases and does not have an appcast" do
let(:cask_token) { "github-without-appcast" } let(:cask_token) { "github-without-appcast" }
it { is_expected.to warn_with(appcast_warning) } it { is_expected.to fail_with(message) }
end end
context "when the download is hosted on SourceForge and has an appcast" do context "when the download is hosted on SourceForge and has an appcast" do
let(:cask_token) { "sourceforge-with-appcast" } let(:cask_token) { "sourceforge-with-appcast" }
it { is_expected.not_to warn_with(appcast_warning) } it { is_expected.not_to fail_with(message) }
end end
context "when the download is hosted on SourceForge and does not have an appcast" do context "when the download is hosted on SourceForge and does not have an appcast" do
let(:cask_token) { "sourceforge-correct-url-format" } let(:cask_token) { "sourceforge-correct-url-format" }
it { is_expected.to warn_with(appcast_warning) } it { is_expected.to fail_with(message) }
end end
context "when the download is hosted on DevMate and has an appcast" do context "when the download is hosted on DevMate and has an appcast" do
let(:cask_token) { "devmate-with-appcast" } let(:cask_token) { "devmate-with-appcast" }
it { is_expected.not_to warn_with(appcast_warning) } it { is_expected.not_to fail_with(message) }
end end
context "when the download is hosted on DevMate and does not have an appcast" do context "when the download is hosted on DevMate and does not have an appcast" do
let(:cask_token) { "devmate-without-appcast" } let(:cask_token) { "devmate-without-appcast" }
it { is_expected.to warn_with(appcast_warning) } it { is_expected.to fail_with(message) }
end end
context "when the download is hosted on HockeyApp and has an appcast" do context "when the download is hosted on HockeyApp and has an appcast" do
let(:cask_token) { "hockeyapp-with-appcast" } let(:cask_token) { "hockeyapp-with-appcast" }
it { is_expected.not_to warn_with(appcast_warning) } it { is_expected.not_to fail_with(message) }
end end
context "when the download is hosted on HockeyApp and does not have an appcast" do context "when the download is hosted on HockeyApp and does not have an appcast" do
let(:cask_token) { "hockeyapp-without-appcast" } let(:cask_token) { "hockeyapp-without-appcast" }
it { is_expected.to warn_with(appcast_warning) } it { is_expected.to fail_with(message) }
end end
end end
describe "latest with appcast checks" do describe "latest with appcast checks" do
let(:warning_msg) { "Casks with an appcast should not use version :latest" } let(:message) { "Casks with an appcast should not use version :latest" }
context "when the Cask is :latest and does not have an appcast" do context "when the Cask is :latest and does not have an appcast" do
let(:cask_token) { "version-latest" } let(:cask_token) { "version-latest" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask is versioned and has an appcast" do context "when the Cask is versioned and has an appcast" do
let(:cask_token) { "with-appcast" } let(:cask_token) { "with-appcast" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "when the Cask is :latest and has an appcast" do context "when the Cask is :latest and has an appcast" do
let(:cask_token) { "latest-with-appcast" } let(:cask_token) { "latest-with-appcast" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "denylist checks" do describe "denylist checks" do
context "when the Cask isn't disallowed" do context "when the Cask is not on the denylist" do
let(:cask_token) { "adobe-air" } let(:cask_token) { "adobe-air" }
it { is_expected.to pass } it { is_expected.to pass }
end end
context "when the Cask is disallowed" do context "when the Cask is on the denylist" do
context "and it's in the official Homebrew tap" do context "and it's in the official Homebrew tap" do
let(:cask_token) { "adobe-illustrator" } let(:cask_token) { "adobe-illustrator" }
@ -684,64 +690,64 @@ describe Cask::Audit, :cask do
end end
describe "latest with auto_updates checks" do describe "latest with auto_updates checks" do
let(:warning_msg) { "Casks with `version :latest` should not use `auto_updates`" } let(:message) { "Casks with `version :latest` should not use `auto_updates`" }
context "when the Cask is :latest and does not have auto_updates" do context "when the Cask is :latest and does not have auto_updates" do
let(:cask_token) { "version-latest" } let(:cask_token) { "version-latest" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.to pass }
end end
context "when the Cask is versioned and does not have auto_updates" do context "when the Cask is versioned and does not have auto_updates" do
let(:cask_token) { "basic-cask" } let(:cask_token) { "basic-cask" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.to pass }
end end
context "when the Cask is versioned and has auto_updates" do context "when the Cask is versioned and has auto_updates" do
let(:cask_token) { "auto-updates" } let(:cask_token) { "auto-updates" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.to pass }
end end
context "when the Cask is :latest and has auto_updates" do context "when the Cask is :latest and has auto_updates" do
let(:cask_token) { "latest-with-auto-updates" } let(:cask_token) { "latest-with-auto-updates" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
end end
describe "preferred download URL formats" do describe "preferred download URL formats" do
let(:warning_msg) { /URL format incorrect/ } let(:message) { /URL format incorrect/ }
context "with incorrect SourceForge URL format" do context "with incorrect SourceForge URL format" do
let(:cask_token) { "sourceforge-incorrect-url-format" } let(:cask_token) { "sourceforge-incorrect-url-format" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
context "with correct SourceForge URL format" do context "with correct SourceForge URL format" do
let(:cask_token) { "sourceforge-correct-url-format" } let(:cask_token) { "sourceforge-correct-url-format" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "with correct SourceForge URL format for version :latest" do context "with correct SourceForge URL format for version :latest" do
let(:cask_token) { "sourceforge-version-latest-correct-url-format" } let(:cask_token) { "sourceforge-version-latest-correct-url-format" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
context "with incorrect OSDN URL format" do context "with incorrect OSDN URL format" do
let(:cask_token) { "osdn-incorrect-url-format" } let(:cask_token) { "osdn-incorrect-url-format" }
it { is_expected.to warn_with(warning_msg) } it { is_expected.to fail_with(message) }
end end
context "with correct OSDN URL format" do context "with correct OSDN URL format" do
let(:cask_token) { "osdn-correct-url-format" } let(:cask_token) { "osdn-correct-url-format" }
it { is_expected.not_to warn_with(warning_msg) } it { is_expected.not_to fail_with(message) }
end end
end end
@ -793,7 +799,7 @@ describe Cask::Audit, :cask do
context "when cask token does not conflict with a core formula" do context "when cask token does not conflict with a core formula" do
let(:formula_names) { %w[other-formula] } let(:formula_names) { %w[other-formula] }
it { is_expected.not_to warn_with(/possible duplicate/) } it { is_expected.to pass }
end end
end end
@ -802,27 +808,28 @@ describe Cask::Audit, :cask do
let(:cask) { Cask::CaskLoader.load(cask_token) } let(:cask) { Cask::CaskLoader.load(cask_token) }
let(:download_double) { instance_double(Cask::Download) } let(:download_double) { instance_double(Cask::Download) }
let(:verify) { class_double(Cask::Verify).as_stubbed_const } let(:verify) { class_double(Cask::Verify).as_stubbed_const }
let(:error_msg) { "Download Failed" } let(:message) { "Download Failed" }
before do before do
allow(audit).to receive(:download).and_return(download_double) allow(audit).to receive(:download).and_return(download_double)
allow(audit).to receive(:check_https_availability)
end end
it "when download and verification succeed it does not fail" do it "when download and verification succeed it does not fail" do
expect(download_double).to receive(:perform) expect(download_double).to receive(:perform)
expect(verify).to receive(:all) expect(verify).to receive(:all)
expect(subject).not_to fail_with(/#{error_msg}/) expect(subject).to pass
end end
it "when download fails it does not fail" do it "when download fails it fails" do
expect(download_double).to receive(:perform).and_raise(StandardError.new(error_msg)) expect(download_double).to receive(:perform).and_raise(StandardError.new(message))
expect(subject).to fail_with(/#{error_msg}/) expect(subject).to fail_with(/#{message}/)
end end
it "when verification fails it does not fail" do it "when verification fails it fails" do
expect(download_double).to receive(:perform) expect(download_double).to receive(:perform)
expect(verify).to receive(:all).and_raise(StandardError.new(error_msg)) expect(verify).to receive(:all).and_raise(StandardError.new(message))
expect(subject).to fail_with(/#{error_msg}/) expect(subject).to fail_with(/#{message}/)
end end
end end
@ -853,16 +860,16 @@ describe Cask::Audit, :cask do
context "when `new_cask` is true" do context "when `new_cask` is true" do
let(:new_cask) { true } let(:new_cask) { true }
it "warns" do it "fails" do
expect(subject).to warn_with(/should have a description/) expect(subject).to fail_with(/should have a description/)
end end
end end
context "when `new_cask` is true" do context "when `new_cask` is false" do
let(:new_cask) { false } let(:new_cask) { false }
it "does not warn" do it "warns" do
expect(subject).not_to warn_with(/should have a description/) expect(subject).to warn_with(/should have a description/)
end end
end end
end end
@ -883,8 +890,8 @@ describe Cask::Audit, :cask do
RUBY RUBY
end end
it "does not warn" do it "passes" do
expect(subject).not_to warn_with(/should have a description/) expect(subject).to pass
end end
end end
end end

View File

@ -15,6 +15,8 @@ describe Cask::Cmd::Cat, :cask do
sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
url "https://brew.sh/TestCask.dmg" url "https://brew.sh/TestCask.dmg"
name "Basic Cask"
desc "Cask for testing basic functionality"
homepage "https://brew.sh/" homepage "https://brew.sh/"
app "TestCask.app" app "TestCask.app"

View File

@ -3,7 +3,8 @@ cask "adobe-air" do
sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
url "https://brew.sh/TestCask.dmg" url "https://brew.sh/TestCask.dmg"
name "Adobe Air" name "Adobe AIR"
desc "Cross-platform application runtime"
homepage "https://brew.sh/" homepage "https://brew.sh/"
app "TestCask.app" app "TestCask.app"

View File

@ -3,6 +3,8 @@ cask "auto-updates" do
sha256 "5633c3a0f2e572cbf021507dec78c50998b398c343232bdfc7e26221d0a5db4d" sha256 "5633c3a0f2e572cbf021507dec78c50998b398c343232bdfc7e26221d0a5db4d"
url "file://#{TEST_FIXTURE_DIR}/cask/MyFancyApp.zip" url "file://#{TEST_FIXTURE_DIR}/cask/MyFancyApp.zip"
name "Auto-Updates"
desc "Cask which auto-updates"
homepage "https://brew.sh/MyFancyApp" homepage "https://brew.sh/MyFancyApp"
auto_updates true auto_updates true

View File

@ -3,6 +3,8 @@ cask "basic-cask" do
sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
url "https://brew.sh/TestCask.dmg" url "https://brew.sh/TestCask.dmg"
name "Basic Cask"
desc "Cask for testing basic functionality"
homepage "https://brew.sh/" homepage "https://brew.sh/"
app "TestCask.app" app "TestCask.app"

View File

@ -3,6 +3,8 @@ cask "latest-with-auto-updates" do
sha256 :no_check sha256 :no_check
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
name "Latest with Auto-Updates"
desc "Unversioned cask which auto-updates"
homepage "https://brew.sh/latest-with-auto-updates" homepage "https://brew.sh/latest-with-auto-updates"
auto_updates true auto_updates true

View File

@ -3,6 +3,8 @@ cask "pkg-without-uninstall" do
sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
url "file://#{TEST_FIXTURE_DIR}/cask/MyFancyPkg.zip" url "file://#{TEST_FIXTURE_DIR}/cask/MyFancyPkg.zip"
name "PKG without Uninstall"
desc "Cask with a package installer and no uninstall stanza"
homepage "https://brew.sh/fancy-pkg" homepage "https://brew.sh/fancy-pkg"
pkg "Fancy.pkg" pkg "Fancy.pkg"

View File

@ -3,6 +3,8 @@ cask "version-latest" do
sha256 :no_check sha256 :no_check
url "file://#{TEST_FIXTURE_DIR}/cask/caffeines.zip" url "file://#{TEST_FIXTURE_DIR}/cask/caffeines.zip"
name "Version Latest"
desc "Unversioned cask"
homepage "https://brew.sh/" homepage "https://brew.sh/"
app "Caffeine Mini.app" app "Caffeine Mini.app"

View File

@ -3,6 +3,8 @@ cask "with-binary" do
sha256 "d5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2" sha256 "d5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2"
url "file://#{TEST_FIXTURE_DIR}/cask/AppWithBinary.zip" url "file://#{TEST_FIXTURE_DIR}/cask/AppWithBinary.zip"
name "With Binary"
desc "Cask with a binary stanza"
homepage "https://brew.sh/with-binary" homepage "https://brew.sh/with-binary"
app "App.app" app "App.app"

View File

@ -3,6 +3,8 @@ cask "with-installer-manual" do
sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94" sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94"
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
name "With Installer Manual"
desc "Cask with a manual installer"
homepage "https://brew.sh/" homepage "https://brew.sh/"
installer manual: "Caffeine.app" installer manual: "Caffeine.app"

View File

@ -4,6 +4,7 @@ cask "pharo" do
url "https://brew.sh/ThirdParty.dmg" url "https://brew.sh/ThirdParty.dmg"
name "Pharo" name "Pharo"
desc "Cask from a third-party tap"
homepage "https://brew.sh/" homepage "https://brew.sh/"
app "ThirdParty.app" app "ThirdParty.app"