diff --git a/Library/Homebrew/cask/audit.rb b/Library/Homebrew/cask/audit.rb index a2af227785..09110a6888 100644 --- a/Library/Homebrew/cask/audit.rb +++ b/Library/Homebrew/cask/audit.rb @@ -22,10 +22,12 @@ module Cask attr_predicate :appcast?, :new_cask?, :strict?, :signing?, :online?, :token_conflicts? - def initialize(cask, appcast: nil, download: nil, quarantine: nil, - token_conflicts: nil, online: nil, strict: nil, signing: nil, - new_cask: nil, only: [], except: []) - + def initialize( + cask, + appcast: nil, download: nil, quarantine: nil, + token_conflicts: nil, online: nil, strict: nil, signing: nil, + new_cask: nil, only: [], except: [] + ) # `new_cask` implies `online`, `token_conflicts`, `strict` and `signing` online = new_cask if online.nil? strict = new_cask if strict.nil? @@ -47,16 +49,16 @@ module Cask @signing = signing @new_cask = new_cask @token_conflicts = token_conflicts - @only = only - @except = except + @only = only || [] + @except = except || [] end def run! only_audits = @only except_audits = @except - private_methods.map(&:to_s).grep(/^check_/).each do |audit_method_name| - name = audit_method_name.delete_prefix("check_") + private_methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name| + name = audit_method_name.delete_prefix("audit_") next if !only_audits.empty? && only_audits&.exclude?(name) next if except_audits&.include?(name) @@ -140,7 +142,7 @@ module Cask private sig { void } - def check_untrusted_pkg + def audit_untrusted_pkg odebug "Auditing pkg stanza: allow_untrusted" return if @cask.sourcefile_path.nil? @@ -155,7 +157,7 @@ module Cask end sig { void } - def check_stanza_requires_uninstall + def audit_stanza_requires_uninstall odebug "Auditing stanzas which require an uninstall" return if cask.artifacts.none? { |k| k.is_a?(Artifact::Pkg) || k.is_a?(Artifact::Installer) } @@ -165,7 +167,7 @@ module Cask end sig { void } - def check_single_pre_postflight + def audit_single_pre_postflight odebug "Auditing preflight and postflight stanzas" if cask.artifacts.count { |k| k.is_a?(Artifact::PreflightBlock) && k.directives.key?(:preflight) } > 1 @@ -182,7 +184,7 @@ module Cask end sig { void } - def check_single_uninstall_zap + def audit_single_uninstall_zap odebug "Auditing single uninstall_* and zap stanzas" count = cask.artifacts.count do |k| @@ -205,7 +207,7 @@ module Cask end sig { void } - def check_required_stanzas + def audit_required_stanzas odebug "Auditing required stanzas" [:version, :sha256, :url, :homepage].each do |sym| add_error "a #{sym} stanza is required" unless cask.send(sym) @@ -218,15 +220,16 @@ module Cask end sig { void } - def check_description_present - # Fonts seldom benefit from descriptions and requiring them disproportionately increases the maintenance burden + def audit_description + # Fonts seldom benefit from descriptions and requiring them disproportionately + # increases the maintenance burden. return if cask.tap == "homebrew/cask-fonts" add_warning "Cask should have a description. Please add a `desc` stanza." if cask.desc.blank? end sig { void } - def check_no_string_version_latest + def audit_no_string_version_latest return unless cask.version odebug "Auditing version :latest does not appear as a string ('latest')" @@ -236,7 +239,7 @@ module Cask end sig { void } - def check_sha256_no_check_if_latest + def audit_sha256_no_check_if_latest return unless cask.sha256 odebug "Auditing sha256 :no_check with version :latest" @@ -247,7 +250,7 @@ module Cask end sig { void } - def check_sha256_no_check_if_unversioned + def audit_sha256_no_check_if_unversioned return unless cask.sha256 return if cask.sha256 == :no_check @@ -255,7 +258,7 @@ module Cask end sig { void } - def check_sha256_actually_256 + def audit_sha256_actually_256 return unless cask.sha256 odebug "Auditing sha256 string is a legal SHA-256 digest" @@ -266,7 +269,7 @@ module Cask end sig { void } - def check_sha256_invalid + def audit_sha256_invalid return unless cask.sha256 odebug "Auditing sha256 is not a known invalid value" @@ -277,7 +280,7 @@ module Cask end sig { void } - def check_appcast_and_livecheck + def audit_appcast_and_livecheck return unless cask.appcast if cask.livecheckable? @@ -288,7 +291,7 @@ module Cask end sig { void } - def check_latest_with_appcast_or_livecheck + def audit_latest_with_appcast_or_livecheck return unless cask.version.latest? add_error "Casks with an `appcast` should not use `version :latest`." if cask.appcast @@ -296,7 +299,7 @@ module Cask end sig { void } - def check_latest_with_auto_updates + def audit_latest_with_auto_updates return unless cask.version.latest? return unless cask.auto_updates @@ -306,7 +309,7 @@ module Cask LIVECHECK_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#stanza-livecheck" sig { params(livecheck_result: T::Boolean).void } - def check_hosting_with_livecheck(livecheck_result: check_livecheck_version) + def audit_hosting_with_livecheck(livecheck_result: audit_livecheck_version) return if cask.discontinued? || cask.version.latest? return if block_url_offline? || cask.appcast || cask.livecheckable? return if livecheck_result == :auto_detected @@ -328,7 +331,7 @@ module Cask SOURCEFORGE_OSDN_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#sourceforgeosdn-urls" sig { void } - def check_download_url_format + def audit_download_url_format return unless cask.url odebug "Auditing URL format" @@ -342,7 +345,7 @@ module Cask VERIFIED_URL_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#when-url-and-homepage-domains-differ-add-verified" sig { void } - def check_unnecessary_verified + def audit_unnecessary_verified return if block_url_offline? return unless verified_present? return unless url_match_homepage? @@ -354,7 +357,7 @@ module Cask end sig { void } - def check_missing_verified + def audit_missing_verified return if block_url_offline? return if file_url? return if url_match_homepage? @@ -366,7 +369,7 @@ module Cask end sig { void } - def check_no_match + def audit_no_match return if block_url_offline? return unless verified_present? return if verified_matches_url? @@ -377,7 +380,7 @@ module Cask end sig { void } - def check_generic_artifacts + def audit_generic_artifacts cask.artifacts.select { |a| a.is_a?(Artifact::Artifact) }.each do |artifact| unless artifact.target.absolute? add_error "target must be absolute path for #{artifact.class.english_name} #{artifact.source}" @@ -386,7 +389,7 @@ module Cask end sig { void } - def check_languages + def audit_languages @cask.languages.each do |language| Locale.parse(language) rescue Locale::ParserError @@ -395,7 +398,7 @@ module Cask end sig { void } - def check_token_conflicts + def audit_token_conflicts return unless token_conflicts? return unless core_formula_names.include?(cask.token) @@ -404,7 +407,7 @@ module Cask end sig { void } - def check_token_valid + def audit_token_valid 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_error "cask token whitespace should be replaced by hyphens" if cask.token.include? " " @@ -422,7 +425,7 @@ module Cask end sig { void } - def check_token_bad_words + def audit_token_bad_words return unless new_cask? token = cask.token @@ -450,7 +453,7 @@ module Cask end sig { void } - def check_download + def audit_download return if download.blank? || cask.url.blank? odebug "Auditing download" @@ -460,7 +463,7 @@ module Cask end sig { void } - def check_signing + def audit_signing return if !signing? || download.blank? || cask.url.blank? odebug "Auditing signing" @@ -504,7 +507,7 @@ module Cask end sig { returns(T.nilable(T.any(T::Boolean, Symbol))) } - def check_livecheck_version + def audit_livecheck_version return unless appcast? referenced_cask, = Homebrew::Livecheck.resolve_livecheck_reference(cask) @@ -541,7 +544,7 @@ module Cask false end - def check_livecheck_min_os + def audit_livecheck_min_os return unless online? return unless cask.livecheckable? return unless cask.livecheck.strategy == :sparkle @@ -588,7 +591,7 @@ module Cask end sig { void } - def check_appcast_contains_version + def audit_appcast_contains_version return unless appcast? return if cask.appcast.to_s.empty? return if cask.appcast.must_contain == :no_check @@ -615,7 +618,7 @@ module Cask end sig { void } - def check_github_prerelease_version + def audit_github_prerelease_version return if cask.tap == "homebrew/cask-versions" odebug "Auditing GitHub prerelease" @@ -629,7 +632,7 @@ module Cask end sig { void } - def check_gitlab_prerelease_version + def audit_gitlab_prerelease_version return if cask.tap == "homebrew/cask-versions" user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if online? @@ -644,7 +647,7 @@ module Cask end sig { void } - def check_github_repository_archived + def audit_github_repository_archived user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if online? return if user.nil? @@ -665,7 +668,7 @@ module Cask end sig { void } - def check_gitlab_repository_archived + def audit_gitlab_repository_archived user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if online? return if user.nil? @@ -686,7 +689,7 @@ module Cask end sig { void } - def check_github_repository + def audit_github_repository return unless new_cask? user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) @@ -699,7 +702,7 @@ module Cask end sig { void } - def check_gitlab_repository + def audit_gitlab_repository return unless new_cask? user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) @@ -712,7 +715,7 @@ module Cask end sig { void } - def check_bitbucket_repository + def audit_bitbucket_repository return unless new_cask? user, repo = get_repo_data(%r{https?://bitbucket\.org/([^/]+)/([^/]+)/?.*}) @@ -725,7 +728,7 @@ module Cask end sig { void } - def check_denylist + def audit_denylist return unless cask.tap return unless cask.tap.official? return unless (reason = Denylist.reason(cask.token)) @@ -734,7 +737,7 @@ module Cask end sig { void } - def check_reverse_migration + def audit_reverse_migration return unless new_cask? return unless cask.tap return unless cask.tap.official? @@ -744,7 +747,7 @@ module Cask end sig { void } - def check_https_availability + def audit_https_availability return unless download if cask.url && !cask.url.using diff --git a/Library/Homebrew/cask/auditor.rb b/Library/Homebrew/cask/auditor.rb index daae35b9b4..a450040dcc 100644 --- a/Library/Homebrew/cask/auditor.rb +++ b/Library/Homebrew/cask/auditor.rb @@ -8,36 +8,8 @@ module Cask # # @api private class Auditor - def self.audit( - cask, - audit_download: nil, - audit_appcast: nil, - audit_online: nil, - audit_new_cask: nil, - audit_strict: nil, - audit_signing: nil, - audit_token_conflicts: nil, - quarantine: nil, - any_named_args: nil, - language: nil, - display_passes: nil, - display_failures_only: nil - ) - new( - cask, - audit_download: audit_download, - audit_appcast: audit_appcast, - audit_online: audit_online, - audit_new_cask: audit_new_cask, - audit_strict: audit_strict, - audit_signing: audit_signing, - audit_token_conflicts: audit_token_conflicts, - quarantine: quarantine, - any_named_args: any_named_args, - language: language, - display_passes: display_passes, - display_failures_only: display_failures_only, - ).audit + def self.audit(cask, **options) + new(cask, **options).audit end attr_reader :cask, :language @@ -55,7 +27,9 @@ module Cask any_named_args: nil, language: nil, display_passes: nil, - display_failures_only: nil + display_failures_only: nil, + only: [], + except: [] ) @cask = cask @audit_download = audit_download @@ -70,6 +44,8 @@ module Cask @language = language @display_passes = display_passes @display_failures_only = display_failures_only + @only = only + @except = except end def audit @@ -142,6 +118,8 @@ module Cask token_conflicts: @audit_token_conflicts, download: @audit_download, quarantine: @quarantine, + only: @only, + except: @except, ) audit.run! end diff --git a/Library/Homebrew/cask/cmd/audit.rb b/Library/Homebrew/cask/cmd/audit.rb index be8a6abeb4..6326446fcf 100644 --- a/Library/Homebrew/cask/cmd/audit.rb +++ b/Library/Homebrew/cask/cmd/audit.rb @@ -60,6 +60,8 @@ module Cask language: args.language, display_passes: args.verbose? || args.named.count == 1, display_failures_only: args.display_failures_only?, + only: [], + except: [], ) failed_casks = results.reject { |_, result| result[:errors].empty? }.map(&:first) @@ -70,18 +72,20 @@ module Cask def self.audit_casks( *casks, - download: nil, - appcast: nil, - online: nil, - strict: nil, - signing: nil, - new_cask: nil, - token_conflicts: nil, - quarantine: nil, - any_named_args: nil, - language: nil, - display_passes: nil, - display_failures_only: nil + download:, + appcast:, + online:, + strict:, + signing:, + new_cask:, + token_conflicts:, + quarantine:, + any_named_args:, + language:, + display_passes:, + display_failures_only:, + only:, + except: ) options = { audit_download: download, @@ -96,6 +100,8 @@ module Cask any_named_args: any_named_args, display_passes: display_passes, display_failures_only: display_failures_only, + only: only, + except: except, }.compact options[:quarantine] = true if options[:quarantine].nil? diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 62cf434b25..a04c19d3c7 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -252,6 +252,8 @@ module Homebrew language: nil, display_passes: args.verbose? || args.named.count == 1, display_failures_only: args.display_failures_only?, + only: args.only, + except: args.except, ) end diff --git a/Library/Homebrew/test/cask/audit_spec.rb b/Library/Homebrew/test/cask/audit_spec.rb index 7c2d13603e..19cb15265a 100644 --- a/Library/Homebrew/test/cask/audit_spec.rb +++ b/Library/Homebrew/test/cask/audit_spec.rb @@ -34,6 +34,7 @@ describe Cask::Audit, :cask do let(:new_cask) { nil } let(:online) { nil } let(:only) { [] } + let(:except) { [] } let(:strict) { nil } let(:token_conflicts) { nil } let(:audit) { @@ -41,7 +42,8 @@ describe Cask::Audit, :cask do strict: strict, new_cask: new_cask, token_conflicts: token_conflicts, - only: only) + only: only, + except: except) } describe "#new" do @@ -965,7 +967,7 @@ describe Cask::Audit, :cask do context "when an exception is raised" do let(:cask) { instance_double(Cask::Cask) } - let(:only) { ["description_present"] } + let(:only) { ["description"] } it "fails the audit" do expect(cask).to receive(:tap).and_raise(StandardError.new) @@ -974,7 +976,7 @@ describe Cask::Audit, :cask do end describe "checking description" do - let(:only) { ["description_present"] } + let(:only) { ["description"] } let(:cask_token) { "without-description" } let(:cask) do tmp_cask cask_token.to_s, <<~RUBY diff --git a/Library/Homebrew/test/cask/cmd/audit_spec.rb b/Library/Homebrew/test/cask/cmd/audit_spec.rb index fae56a4d53..fb5d2b3873 100644 --- a/Library/Homebrew/test/cask/cmd/audit_spec.rb +++ b/Library/Homebrew/test/cask/cmd/audit_spec.rb @@ -21,8 +21,12 @@ describe Cask::Cmd::Audit, :cask do expect(Cask::CaskLoader).to receive(:load).with(cask_token, any_args).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_new_cask: false, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_new_cask: false, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run(cask_token) @@ -32,8 +36,12 @@ describe Cask::Cmd::Audit, :cask do it "does not pass anything if no flags are specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_new_cask: false, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_new_cask: false, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken") @@ -42,8 +50,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `audit_download` if the `--download` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_download: true, audit_new_cask: false, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_download: true, audit_new_cask: false, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--download") @@ -52,8 +64,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `audit_token_conflicts` if the `--token-conflicts` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_token_conflicts: true, audit_new_cask: false, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_token_conflicts: true, audit_new_cask: false, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--token-conflicts") @@ -62,8 +78,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `audit_strict` if the `--strict` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_strict: true, audit_new_cask: false, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_strict: true, audit_new_cask: false, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--strict") @@ -72,8 +92,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `audit_online` if the `--online` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_online: true, audit_new_cask: false, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_online: true, audit_new_cask: false, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--online") @@ -82,8 +106,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `audit_new_cask` if the `--new-cask` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_new_cask: true, quarantine: true, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_new_cask: true, quarantine: true, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--new-cask") @@ -92,8 +120,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `language` if the `--language` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_new_cask: false, quarantine: true, language: ["de-AT"], any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_new_cask: false, quarantine: true, language: ["de-AT"], any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--language=de-AT") @@ -102,8 +134,12 @@ describe Cask::Cmd::Audit, :cask do it "passes `quarantine` if the `--no-quarantine` flag is specified" do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_new_cask: false, quarantine: false, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_new_cask: false, quarantine: false, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken", "--no-quarantine") @@ -114,8 +150,12 @@ describe Cask::Cmd::Audit, :cask do allow(Cask::CaskLoader).to receive(:load).and_return(cask) expect(Cask::Auditor).to receive(:audit) - .with(cask, audit_new_cask: false, quarantine: false, any_named_args: true, - display_failures_only: false, display_passes: true) + .with( + cask, + audit_new_cask: false, quarantine: false, any_named_args: true, + display_failures_only: false, display_passes: true, + only: [], except: [] + ) .and_return(result) described_class.run("casktoken")