mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Merge pull request #20134 from Homebrew/homebrew_core_cask_name_audit
audit: ensure that official formula and cask names don't conflict.
This commit is contained in:
commit
4759ca9430
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -166,7 +166,7 @@ jobs:
|
|||||||
id: set-up-homebrew
|
id: set-up-homebrew
|
||||||
uses: Homebrew/actions/setup-homebrew@main
|
uses: Homebrew/actions/setup-homebrew@main
|
||||||
with:
|
with:
|
||||||
core: false
|
core: true
|
||||||
cask: true
|
cask: true
|
||||||
test-bot: false
|
test-bot: false
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ module Cask
|
|||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(
|
params(
|
||||||
cask: ::Cask::Cask, download: T::Boolean, quarantine: T::Boolean, token_conflicts: T.nilable(T::Boolean),
|
cask: ::Cask::Cask, download: T::Boolean, quarantine: T::Boolean,
|
||||||
online: T.nilable(T::Boolean), strict: T.nilable(T::Boolean), signing: T.nilable(T::Boolean),
|
online: T.nilable(T::Boolean), strict: T.nilable(T::Boolean), signing: T.nilable(T::Boolean),
|
||||||
new_cask: T.nilable(T::Boolean), only: T::Array[String], except: T::Array[String]
|
new_cask: T.nilable(T::Boolean), only: T::Array[String], except: T::Array[String]
|
||||||
).void
|
).void
|
||||||
@ -35,14 +35,13 @@ module Cask
|
|||||||
def initialize(
|
def initialize(
|
||||||
cask,
|
cask,
|
||||||
download: false, quarantine: false,
|
download: false, quarantine: false,
|
||||||
token_conflicts: nil, online: nil, strict: nil, signing: nil,
|
online: nil, strict: nil, signing: nil,
|
||||||
new_cask: nil, only: [], except: []
|
new_cask: nil, only: [], except: []
|
||||||
)
|
)
|
||||||
# `new_cask` implies `online`, `token_conflicts`, `strict` and `signing`
|
# `new_cask` implies `online`, `strict` and `signing`
|
||||||
online = new_cask if online.nil?
|
online = new_cask if online.nil?
|
||||||
strict = new_cask if strict.nil?
|
strict = new_cask if strict.nil?
|
||||||
signing = new_cask if signing.nil?
|
signing = new_cask if signing.nil?
|
||||||
token_conflicts = new_cask if token_conflicts.nil?
|
|
||||||
|
|
||||||
# `online` and `signing` imply `download`
|
# `online` and `signing` imply `download`
|
||||||
download ||= online || signing
|
download ||= online || signing
|
||||||
@ -53,7 +52,6 @@ module Cask
|
|||||||
@strict = strict
|
@strict = strict
|
||||||
@signing = signing
|
@signing = signing
|
||||||
@new_cask = new_cask
|
@new_cask = new_cask
|
||||||
@token_conflicts = token_conflicts
|
|
||||||
@only = only
|
@only = only
|
||||||
@except = except
|
@except = except
|
||||||
end
|
end
|
||||||
@ -70,9 +68,6 @@ module Cask
|
|||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def strict? = !!@strict
|
def strict? = !!@strict
|
||||||
|
|
||||||
sig { returns(T::Boolean) }
|
|
||||||
def token_conflicts? = !!@token_conflicts
|
|
||||||
|
|
||||||
sig { returns(::Cask::Audit) }
|
sig { returns(::Cask::Audit) }
|
||||||
def run!
|
def run!
|
||||||
only_audits = @only
|
only_audits = @only
|
||||||
@ -430,15 +425,10 @@ module Cask
|
|||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
def audit_token_conflicts
|
def audit_token_conflicts
|
||||||
return unless token_conflicts?
|
|
||||||
|
|
||||||
Homebrew.with_no_api_env do
|
Homebrew.with_no_api_env do
|
||||||
return unless core_formula_names.include?(cask.token)
|
return unless core_formula_names.include?(cask.token)
|
||||||
|
|
||||||
add_error(
|
add_error("cask token conflicts with an existing homebrew/core formula: #{Formatter.url(core_formula_url)}")
|
||||||
"possible duplicate, cask token conflicts with Homebrew core formula: #{Formatter.url(core_formula_url)}",
|
|
||||||
strict_only: true,
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@ module Cask
|
|||||||
params(
|
params(
|
||||||
cask: ::Cask::Cask, audit_download: T::Boolean, audit_online: T.nilable(T::Boolean),
|
cask: ::Cask::Cask, audit_download: T::Boolean, audit_online: T.nilable(T::Boolean),
|
||||||
audit_strict: T.nilable(T::Boolean), audit_signing: T.nilable(T::Boolean),
|
audit_strict: T.nilable(T::Boolean), audit_signing: T.nilable(T::Boolean),
|
||||||
audit_token_conflicts: T.nilable(T::Boolean), audit_new_cask: T.nilable(T::Boolean), quarantine: T::Boolean,
|
audit_new_cask: T.nilable(T::Boolean), quarantine: T::Boolean,
|
||||||
any_named_args: T::Boolean, language: T.nilable(String), only: T::Array[String], except: T::Array[String]
|
any_named_args: T::Boolean, language: T.nilable(String), only: T::Array[String], except: T::Array[String]
|
||||||
).returns(T::Set[String])
|
).returns(T::Set[String])
|
||||||
}
|
}
|
||||||
def self.audit(
|
def self.audit(
|
||||||
cask, audit_download: false, audit_online: nil, audit_strict: nil, audit_signing: nil,
|
cask, audit_download: false, audit_online: nil, audit_strict: nil, audit_signing: nil,
|
||||||
audit_token_conflicts: nil, audit_new_cask: nil, quarantine: false, any_named_args: false, language: nil,
|
audit_new_cask: nil, quarantine: false, any_named_args: false, language: nil,
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
new(
|
new(
|
||||||
cask, audit_download:, audit_online:, audit_strict:, audit_signing:, audit_token_conflicts:,
|
cask, audit_download:, audit_online:, audit_strict:, audit_signing:,
|
||||||
audit_new_cask:, quarantine:, any_named_args:, language:, only:, except:
|
audit_new_cask:, quarantine:, any_named_args:, language:, only:, except:
|
||||||
).audit
|
).audit
|
||||||
end
|
end
|
||||||
@ -36,7 +36,7 @@ module Cask
|
|||||||
params(
|
params(
|
||||||
cask: ::Cask::Cask, audit_download: T::Boolean, audit_online: T.nilable(T::Boolean),
|
cask: ::Cask::Cask, audit_download: T::Boolean, audit_online: T.nilable(T::Boolean),
|
||||||
audit_strict: T.nilable(T::Boolean), audit_signing: T.nilable(T::Boolean),
|
audit_strict: T.nilable(T::Boolean), audit_signing: T.nilable(T::Boolean),
|
||||||
audit_token_conflicts: T.nilable(T::Boolean), audit_new_cask: T.nilable(T::Boolean), quarantine: T::Boolean,
|
audit_new_cask: T.nilable(T::Boolean), quarantine: T::Boolean,
|
||||||
any_named_args: T::Boolean, language: T.nilable(String), only: T::Array[String], except: T::Array[String]
|
any_named_args: T::Boolean, language: T.nilable(String), only: T::Array[String], except: T::Array[String]
|
||||||
).void
|
).void
|
||||||
}
|
}
|
||||||
@ -46,7 +46,6 @@ module Cask
|
|||||||
audit_online: nil,
|
audit_online: nil,
|
||||||
audit_strict: nil,
|
audit_strict: nil,
|
||||||
audit_signing: nil,
|
audit_signing: nil,
|
||||||
audit_token_conflicts: nil,
|
|
||||||
audit_new_cask: nil,
|
audit_new_cask: nil,
|
||||||
quarantine: false,
|
quarantine: false,
|
||||||
any_named_args: false,
|
any_named_args: false,
|
||||||
@ -61,7 +60,6 @@ module Cask
|
|||||||
@audit_strict = audit_strict
|
@audit_strict = audit_strict
|
||||||
@audit_signing = audit_signing
|
@audit_signing = audit_signing
|
||||||
@quarantine = quarantine
|
@quarantine = quarantine
|
||||||
@audit_token_conflicts = audit_token_conflicts
|
|
||||||
@any_named_args = any_named_args
|
@any_named_args = any_named_args
|
||||||
@language = language
|
@language = language
|
||||||
@only = only
|
@only = only
|
||||||
@ -127,15 +125,14 @@ module Cask
|
|||||||
def audit_cask_instance(cask)
|
def audit_cask_instance(cask)
|
||||||
audit = Audit.new(
|
audit = Audit.new(
|
||||||
cask,
|
cask,
|
||||||
online: @audit_online,
|
online: @audit_online,
|
||||||
strict: @audit_strict,
|
strict: @audit_strict,
|
||||||
signing: @audit_signing,
|
signing: @audit_signing,
|
||||||
new_cask: @audit_new_cask,
|
new_cask: @audit_new_cask,
|
||||||
token_conflicts: @audit_token_conflicts,
|
download: @audit_download,
|
||||||
download: @audit_download,
|
quarantine: @quarantine,
|
||||||
quarantine: @quarantine,
|
only: @only,
|
||||||
only: @only,
|
except: @except,
|
||||||
except: @except,
|
|
||||||
)
|
)
|
||||||
audit.run!
|
audit.run!
|
||||||
end
|
end
|
||||||
|
@ -58,8 +58,10 @@ module Homebrew
|
|||||||
hidden: true
|
hidden: true
|
||||||
switch "--[no-]signing",
|
switch "--[no-]signing",
|
||||||
description: "Audit for app signatures, which are required by macOS on ARM."
|
description: "Audit for app signatures, which are required by macOS on ARM."
|
||||||
|
# should be odeprecated in future
|
||||||
switch "--token-conflicts",
|
switch "--token-conflicts",
|
||||||
description: "Audit for token conflicts."
|
description: "Audit for token conflicts.",
|
||||||
|
hidden: true
|
||||||
flag "--tap=",
|
flag "--tap=",
|
||||||
description: "Check formulae and casks within the given tap, specified as <user>`/`<repo>."
|
description: "Check formulae and casks within the given tap, specified as <user>`/`<repo>."
|
||||||
switch "--fix",
|
switch "--fix",
|
||||||
@ -251,18 +253,17 @@ module Homebrew
|
|||||||
# For switches, we add `|| nil` so that `nil` will be passed
|
# For switches, we add `|| nil` so that `nil` will be passed
|
||||||
# instead of `false` if they aren't set.
|
# instead of `false` if they aren't set.
|
||||||
# This way, we can distinguish between "not set" and "set to false".
|
# This way, we can distinguish between "not set" and "set to false".
|
||||||
audit_online: args.online? || nil,
|
audit_online: args.online? || nil,
|
||||||
audit_strict: args.strict? || nil,
|
audit_strict: args.strict? || nil,
|
||||||
|
|
||||||
# No need for `|| nil` for `--[no-]signing`
|
# No need for `|| nil` for `--[no-]signing`
|
||||||
# because boolean switches are already `nil` if not passed
|
# because boolean switches are already `nil` if not passed
|
||||||
audit_signing: args.signing?,
|
audit_signing: args.signing?,
|
||||||
audit_new_cask: args.new? || nil,
|
audit_new_cask: args.new? || nil,
|
||||||
audit_token_conflicts: args.token_conflicts? || nil,
|
quarantine: true,
|
||||||
quarantine: true,
|
any_named_args: !no_named_args,
|
||||||
any_named_args: !no_named_args,
|
only: args.only || [],
|
||||||
only: args.only || [],
|
except: args.except || [],
|
||||||
except: args.except || [],
|
|
||||||
).to_a
|
).to_a
|
||||||
end
|
end
|
||||||
end.uniq
|
end.uniq
|
||||||
|
@ -274,10 +274,7 @@ module Homebrew
|
|||||||
bitbucket_repository]
|
bitbucket_repository]
|
||||||
end
|
end
|
||||||
|
|
||||||
if labels.include?("ci-skip-token")
|
audit_exceptions << %w[token_valid token_bad_words] if labels.include?("ci-skip-token")
|
||||||
audit_exceptions << %w[token_conflicts token_valid
|
|
||||||
token_bad_words]
|
|
||||||
end
|
|
||||||
|
|
||||||
audit_args << "--except" << audit_exceptions.join(",") if audit_exceptions.any?
|
audit_args << "--except" << audit_exceptions.join(",") if audit_exceptions.any?
|
||||||
|
|
||||||
|
@ -170,9 +170,15 @@ module Homebrew
|
|||||||
last_word_connector: " or ")}."
|
last_word_connector: " or ")}."
|
||||||
end
|
end
|
||||||
|
|
||||||
return unless @strict
|
|
||||||
return unless @core_tap
|
return unless @core_tap
|
||||||
|
|
||||||
|
if CoreCaskTap.instance.cask_tokens.include?(name)
|
||||||
|
problem "Formula name conflicts with an existing Homebrew/cask cask's token."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
return unless @strict
|
||||||
|
|
||||||
problem "'#{name}' is not allowed in homebrew/core." if MissingFormula.disallowed_reason(name)
|
problem "'#{name}' is not allowed in homebrew/core." if MissingFormula.disallowed_reason(name)
|
||||||
|
|
||||||
if Formula.aliases.include? name
|
if Formula.aliases.include? name
|
||||||
@ -185,6 +191,11 @@ module Homebrew
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if CoreCaskTap.instance.cask_tokens.include?(name)
|
||||||
|
problem "Formula name conflicts with an existing Homebrew/cask cask's token."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
return if formula.core_formula?
|
return if formula.core_formula?
|
||||||
return unless Formula.core_names.include?(name)
|
return unless Formula.core_names.include?(name)
|
||||||
|
|
||||||
|
@ -49,13 +49,11 @@ RSpec.describe Cask::Audit, :cask do
|
|||||||
let(:only) { [] }
|
let(:only) { [] }
|
||||||
let(:except) { [] }
|
let(:except) { [] }
|
||||||
let(:strict) { nil }
|
let(:strict) { nil }
|
||||||
let(:token_conflicts) { nil }
|
|
||||||
let(:signing) { nil }
|
let(:signing) { nil }
|
||||||
let(:audit) do
|
let(:audit) do
|
||||||
described_class.new(cask, online:,
|
described_class.new(cask, online:,
|
||||||
strict:,
|
strict:,
|
||||||
new_cask:,
|
new_cask:,
|
||||||
token_conflicts:,
|
|
||||||
signing:,
|
signing:,
|
||||||
only:,
|
only:,
|
||||||
except:)
|
except:)
|
||||||
@ -72,10 +70,6 @@ RSpec.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
|
||||||
@ -949,34 +943,15 @@ RSpec.describe Cask::Audit, :cask do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "token conflicts" do
|
describe "token conflicts" do
|
||||||
let(:only) { ["token_conflicts"] }
|
|
||||||
let(:cask_token) { "with-binary" }
|
let(:cask_token) { "with-binary" }
|
||||||
let(:token_conflicts) { true }
|
|
||||||
|
|
||||||
context "when cask token conflicts with a core formula" do
|
context "when cask token conflicts with a core formula" do
|
||||||
let(:formula_names) { %w[with-binary other-formula] }
|
let(:formula_names) { %w[with-binary other-formula] }
|
||||||
|
|
||||||
context "when `--strict` is passed" do
|
it "warns about conflicts" do
|
||||||
let(:strict) { true }
|
expect(audit).to receive(:core_formula_names).and_return(formula_names)
|
||||||
|
expect(run).to error_with(/cask token conflicts/)
|
||||||
it "warns about duplicates" do
|
|
||||||
expect(audit).to receive(:core_formula_names).and_return(formula_names)
|
|
||||||
expect(run).to error_with(/possible duplicate/)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when `--strict` is not passed" do
|
|
||||||
it "does not warn about duplicates" do
|
|
||||||
expect(audit).to receive(:core_formula_names).and_return(formula_names)
|
|
||||||
expect(run).not_to error_with(/possible duplicate/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when cask token does not conflict with a core formula" do
|
|
||||||
let(:formula_names) { %w[other-formula] }
|
|
||||||
|
|
||||||
it { is_expected.to pass }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1321,6 +1321,7 @@ Software vendors are often inconsistent with their naming. By enforcing strict n
|
|||||||
* Prevent duplicate submissions
|
* Prevent duplicate submissions
|
||||||
* Minimize renaming events
|
* Minimize renaming events
|
||||||
* Unambiguously boil down the name of the software into a unique identifier
|
* Unambiguously boil down the name of the software into a unique identifier
|
||||||
|
* Avoid conflicts with Homebrew/homebrew-core formulae
|
||||||
|
|
||||||
Details of software names and brands will inevitably be lost in the conversion to a minimal token. To capture the vendor’s full name for a distribution, use the [`name`](#stanza-name) within a cask, which accepts an unrestricted UTF-8 string.
|
Details of software names and brands will inevitably be lost in the conversion to a minimal token. To capture the vendor’s full name for a distribution, use the [`name`](#stanza-name) within a cask, which accepts an unrestricted UTF-8 string.
|
||||||
|
|
||||||
@ -1362,7 +1363,9 @@ Details of software names and brands will inevitably be lost in the conversion t
|
|||||||
|
|
||||||
* If the result of this process is a generic term, such as “Macintosh Installer”, try prepending the name of the vendor or developer, followed by a hyphen. If that doesn’t work, then just create the best name you can, based on the vendor’s web page.
|
* If the result of this process is a generic term, such as “Macintosh Installer”, try prepending the name of the vendor or developer, followed by a hyphen. If that doesn’t work, then just create the best name you can, based on the vendor’s web page.
|
||||||
|
|
||||||
* If the result conflicts with the name of an existing cask, make yours unique by prepending the name of the vendor or developer, followed by a hyphen. Example: [unison.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/u/unison.rb) and [panic-unison.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/p/panic-unison.rb).
|
* If the result conflicts with the name of an existing cask or Homebrew/homebrew-core formula, make yours unique by prepending the name of the vendor or developer, followed by a hyphen. Example: [unison.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/u/unison.rb) and [panic-unison.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/p/panic-unison.rb).
|
||||||
|
|
||||||
|
* If the result still conflicts with the name of an existing Homebrew/homebrew-core formula, adjust the name to better describe the difference by e.g. appending `-app`. Example: `appium` formula and `appium-desktop` cask, `angband` formula and `angband-app` cask.
|
||||||
|
|
||||||
* Inevitably, there are a small number of exceptions not covered by the rules. Don’t hesitate to [use the forum](https://github.com/orgs/Homebrew/discussions) if you have a problem.
|
* Inevitably, there are a small number of exceptions not covered by the rules. Don’t hesitate to [use the forum](https://github.com/orgs/Homebrew/discussions) if you have a problem.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user