diff --git a/Library/Homebrew/cask/audit.rb b/Library/Homebrew/cask/audit.rb index f2805902ef0f6..7acc8c0f6978a 100644 --- a/Library/Homebrew/cask/audit.rb +++ b/Library/Homebrew/cask/audit.rb @@ -410,17 +410,48 @@ def audit_token_valid 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? "--" - if cask.token.match?(/[^@a-z0-9-]/) - add_error "cask token should only contain lowercase alphanumeric characters, hyphens and @" - end - if cask.token.start_with?("-", "@") || cask.token.end_with?("-", "@") add_error "cask token should not have leading or trailing hyphens and/or @" end - add_error "cask token @ unrelated to versioning should be replaced by -at-" if cask.token.count("@") > 1 - add_error "cask token should not contain a hyphen followed by @" if cask.token.include? "-@" - add_error "cask token should not contain @ followed by a hyphen" if cask.token.include? "@-" + unversioned_token, _, version_designation = cask.token.rpartition("@") + if unversioned_token.empty? + match_data = /-(?alpha|beta|rc|release-candidate)$/.match(cask.token) + if match_data && cask.tap&.official? && cask.tap != "homebrew/cask-versions" + add_error "cask token should use @ before version designation '#{match_data[:designation]}'" + end + unversioned_token = cask.token + version_designation = "" + end + + add_error "unversioned cask token @ should be replaced by -at-" if unversioned_token.include? "@" + + if unversioned_token.match?(/[^a-z0-9-]/) + add_error "unversioned cask token should only contain lowercase alphanumeric characters and hyphens" + end + + return if version_designation.empty? + + add_error "unversioned cask token should not have trailing hyphens" if unversioned_token.end_with?("-") + + if version_designation.match?(/[^.a-z0-9-]/) + add_error "cask token version designation should only contain " \ + "lowercase alphanumeric characters, hyphens and '.'" + end + + if version_designation.start_with?("-", ".") || version_designation.end_with?(".") + add_error "cask token version designation should not have leading or trailing hyphens and/or '.'" + end + + return unless cask.tap&.official? + return if cask.tap&.audit_exception(:versioned_no_unversioned_allowlist, cask.token) + + unversioned_full_token = "#{cask.tap}/#{unversioned_token}" + begin + CaskLoader.load(unversioned_full_token) + rescue CaskUnavailableError + add_error "versioned cask but no #{unversioned_full_token} cask exists" + end end sig { void } @@ -431,11 +462,6 @@ def audit_token_bad_words add_error "cask token contains .app" if token.end_with? ".app" - match_data = /-(?alpha|beta|rc|release-candidate)$/.match(cask.token) - if match_data && cask.tap&.official? && cask.tap != "homebrew/cask-versions" - add_error "cask token contains version designation '#{match_data[:designation]}'" - end - add_error("cask token mentions launcher", strict_only: true) if token.end_with? "launcher" add_error("cask token mentions desktop", strict_only: true) if token.end_with? "desktop" diff --git a/Library/Homebrew/test/cask/audit_spec.rb b/Library/Homebrew/test/cask/audit_spec.rb index 3c0ae7db0b872..1ef0782661435 100644 --- a/Library/Homebrew/test/cask/audit_spec.rb +++ b/Library/Homebrew/test/cask/audit_spec.rb @@ -220,8 +220,8 @@ def tmp_cask(name, text) end end - context "when cask token is @-versioned with number" do - let(:cask_token) { "app@10" } + context "when cask token is @-versioned with major-minor version number" do + let(:cask_token) { "app@10.2" } it "does not fail" do expect(run).to pass @@ -240,7 +240,7 @@ def tmp_cask(name, text) let(:cask_token) { "app@stuff@beta" } it "fails" do - expect(run).to error_with(/@ unrelated to versioning should be replaced by -at-/) + expect(run).to error_with(/unversioned cask token @ should be replaced by -at-/) end end @@ -248,7 +248,7 @@ def tmp_cask(name, text) let(:cask_token) { "app-@beta" } it "fails" do - expect(run).to error_with(/should not contain a hyphen followed by @/) + expect(run).to error_with(/unversioned cask token should not have trailing hyphens/) end end @@ -256,7 +256,7 @@ def tmp_cask(name, text) let(:cask_token) { "app@-beta" } it "fails" do - expect(run).to error_with(/should not contain @ followed by a hyphen/) + expect(run).to error_with(%r{should not have leading or trailing hyphens and/or '.'}) end end @@ -280,7 +280,15 @@ def tmp_cask(name, text) let(:cask_token) { "app(stuff)" } it "fails" do - expect(run).to error_with(/alphanumeric characters, hyphens and @/) + expect(run).to error_with(/alphanumeric characters and hyphens/) + end + end + + context "when cask token version designation has non-alphanumeric characters" do + let(:cask_token) { "app@(stuff)" } + + it "fails" do + expect(run).to error_with(/alphanumeric characters, hyphens and '.'/) end end @@ -307,6 +315,22 @@ def tmp_cask(name, text) expect(run).to error_with(/should not have leading or trailing hyphens/) end end + + context "when cask token contains version designation without @" do + let(:cask_token) { "token-beta" } + + it "fails if the cask is from an official tap" do + allow(cask).to receive(:tap).and_return(CoreCaskTap.instance) + + expect(run).to error_with(/should use @ before version designation 'beta'/) + end + + 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")) + + expect(run).to pass + end + end end describe "token bad words" do @@ -335,22 +359,6 @@ def tmp_cask(name, text) end end - context "when cask token contains version designation" do - let(:cask_token) { "token-beta" } - - it "fails if the cask is from an official tap" do - allow(cask).to receive(:tap).and_return(CoreCaskTap.instance) - - expect(run).to error_with(/token contains version designation/) - end - - 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")) - - expect(run).to pass - end - end - context "when cask token contains launcher" do let(:cask_token) { "token-launcher" }