
We were selectively requiring the tap.rb file in a few places for performance reasons. The main method we were referencing was the `Tap.cmd_directories` method which uses `Pathname` and the `TAP_DIRECTORY` constant internally. `Tap.cmd_directories` is mostly used in the `Commands` module and that is loaded very early on in the program so it made sense to move that command to that module. To facilitate that I moved the `TAP_DIRECTORY` constant to the top-level and renamed it to `HOMEBREW_TAP_DIRECTORY`. It now lies in the tap_constants.rb file. A nice bonus of this refactor is that it speeds up loading external commands since the tap.rb file is no longer required by default in those cases.
1316 lines
41 KiB
Ruby
1316 lines
41 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "formula_auditor"
|
|
|
|
RSpec.describe Homebrew::FormulaAuditor do
|
|
include FileUtils
|
|
|
|
let(:dir) { mktmpdir }
|
|
let(:foo_version) do
|
|
@count ||= 0
|
|
@count += 1
|
|
end
|
|
let(:formula_subpath) { "Formula/foo#{foo_version}.rb" }
|
|
let(:origin_tap_path) { HOMEBREW_TAP_DIRECTORY/"homebrew/homebrew-foo" }
|
|
let(:origin_formula_path) { origin_tap_path/formula_subpath }
|
|
let(:tap_path) { HOMEBREW_TAP_DIRECTORY/"homebrew/homebrew-bar" }
|
|
let(:formula_path) { tap_path/formula_subpath }
|
|
|
|
def formula_auditor(name, text, options = {})
|
|
path = Pathname.new "#{dir}/#{name}.rb"
|
|
path.open("w") do |f|
|
|
f.write text
|
|
end
|
|
|
|
formula = Formulary.factory(path)
|
|
|
|
if options.key? :tap_audit_exceptions
|
|
tap = Tap.fetch("test/tap")
|
|
allow(tap).to receive(:audit_exceptions).and_return(options[:tap_audit_exceptions])
|
|
allow(formula).to receive(:tap).and_return(tap)
|
|
options.delete :tap_audit_exceptions
|
|
end
|
|
|
|
described_class.new(formula, options)
|
|
end
|
|
|
|
def formula_gsub(before, after = "")
|
|
text = formula_path.read
|
|
text.gsub! before, after
|
|
formula_path.unlink
|
|
formula_path.write text
|
|
end
|
|
|
|
def formula_gsub_origin_commit(before, after = "")
|
|
text = origin_formula_path.read
|
|
text.gsub!(before, after)
|
|
origin_formula_path.unlink
|
|
origin_formula_path.write text
|
|
|
|
origin_tap_path.cd do
|
|
system "git", "commit", "-am", "commit"
|
|
end
|
|
|
|
tap_path.cd do
|
|
system "git", "fetch"
|
|
system "git", "reset", "--hard", "origin/HEAD"
|
|
end
|
|
end
|
|
|
|
describe "#problems" do
|
|
it "is empty by default" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_license" do
|
|
let(:spdx_license_data) { SPDX.license_data }
|
|
let(:spdx_exception_data) { SPDX.exception_data }
|
|
|
|
let(:deprecated_spdx_id) { "GPL-1.0" }
|
|
let(:license_all_custom_id) { 'all_of: ["MIT", "zzz"]' }
|
|
let(:deprecated_spdx_exception) { "Nokia-Qt-exception-1.1" }
|
|
let(:license_any) { 'any_of: ["0BSD", "GPL-3.0-only"]' }
|
|
let(:license_any_with_plus) { 'any_of: ["0BSD+", "GPL-3.0-only"]' }
|
|
let(:license_nested_conditions) { 'any_of: ["0BSD", { all_of: ["GPL-3.0-only", "MIT"] }]' }
|
|
let(:license_any_mismatch) { 'any_of: ["0BSD", "MIT"]' }
|
|
let(:license_any_nonstandard) { 'any_of: ["0BSD", "zzz", "MIT"]' }
|
|
let(:license_any_deprecated) { 'any_of: ["0BSD", "GPL-1.0", "MIT"]' }
|
|
|
|
it "does not check if the formula is not a new formula" do
|
|
fa = formula_auditor "foo", <<~RUBY, new_formula: false
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "detects no license info" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true, core_tap: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match "Formulae in homebrew/core must specify a license."
|
|
end
|
|
|
|
it "detects if license is not a standard spdx-id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license "zzz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match <<~EOS
|
|
Formula foo contains non-standard SPDX licenses: ["zzz"].
|
|
For a list of valid licenses check: https://spdx.org/licenses/
|
|
EOS
|
|
end
|
|
|
|
it "detects if license is a deprecated spdx-id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true, strict: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license "#{deprecated_spdx_id}"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to eq <<~EOS
|
|
Formula foo contains deprecated SPDX licenses: ["GPL-1.0"].
|
|
You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
|
|
For a list of valid licenses check: https://spdx.org/licenses/
|
|
EOS
|
|
end
|
|
|
|
it "detects if license with AND contains a non-standard spdx-id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license #{license_all_custom_id}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match <<~EOS
|
|
Formula foo contains non-standard SPDX licenses: ["zzz"].
|
|
For a list of valid licenses check: https://spdx.org/licenses/
|
|
EOS
|
|
end
|
|
|
|
it "detects if license array contains a non-standard spdx-id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license #{license_any_nonstandard}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match <<~EOS
|
|
Formula foo contains non-standard SPDX licenses: ["zzz"].
|
|
For a list of valid licenses check: https://spdx.org/licenses/
|
|
EOS
|
|
end
|
|
|
|
it "detects if license array contains a deprecated spdx-id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true, strict: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license #{license_any_deprecated}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to eq <<~EOS
|
|
Formula foo contains deprecated SPDX licenses: ["GPL-1.0"].
|
|
You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
|
|
For a list of valid licenses check: https://spdx.org/licenses/
|
|
EOS
|
|
end
|
|
|
|
it "verifies that a license info is a standard spdx id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license "0BSD"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license info with plus is a standard spdx id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license "0BSD+"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "allows :public_domain license" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license :public_domain
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license info with multiple licenses are standard spdx ids" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license any_of: ["0BSD", "MIT"]
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license info with exceptions are standard spdx ids" do
|
|
formula_text = <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license "Apache-2.0" => { with: "LLVM-exception" }
|
|
end
|
|
RUBY
|
|
fa = formula_auditor("foo", formula_text, new_formula: true,
|
|
spdx_license_data:, spdx_exception_data:)
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license array contains only standard spdx id" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license #{license_any}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license array contains only standard spdx id with plus" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license #{license_any_with_plus}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license array with AND contains only standard spdx ids" do
|
|
fa = formula_auditor "foo", <<~RUBY, spdx_license_data:, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
license #{license_nested_conditions}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "checks online and verifies that a standard license id is the same " \
|
|
"as what is indicated on its GitHub repo", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "GPL-3.0"
|
|
end
|
|
RUBY
|
|
fa = formula_auditor "cask", formula_text, spdx_license_data:,
|
|
online: true, core_tap: true, new_formula: true
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "checks online and verifies that a standard license id with AND is the same " \
|
|
"as what is indicated on its GitHub repo", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license all_of: ["GPL-3.0-or-later", "MIT"]
|
|
end
|
|
RUBY
|
|
fa = formula_auditor "cask", formula_text, spdx_license_data:,
|
|
online: true, core_tap: true, new_formula: true
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "checks online and verifies that a standard license id with WITH is the same " \
|
|
"as what is indicated on its GitHub repo", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "GPL-3.0-or-later" => { with: "LLVM-exception" }
|
|
end
|
|
RUBY
|
|
fa = formula_auditor("cask", formula_text, online: true, core_tap: true, new_formula: true,
|
|
spdx_license_data:, spdx_exception_data:)
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "verifies that a license exception has standard spdx ids", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "GPL-3.0-or-later" => { with: "zzz" }
|
|
end
|
|
RUBY
|
|
fa = formula_auditor("cask", formula_text, core_tap: true, new_formula: true,
|
|
spdx_license_data:, spdx_exception_data:)
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match <<~EOS
|
|
Formula cask contains invalid or deprecated SPDX license exceptions: ["zzz"].
|
|
For a list of valid license exceptions check:
|
|
https://spdx.org/licenses/exceptions-index.html
|
|
EOS
|
|
end
|
|
|
|
it "verifies that a license exception has non-deprecated spdx ids", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "GPL-3.0-or-later" => { with: "#{deprecated_spdx_exception}" }
|
|
end
|
|
RUBY
|
|
fa = formula_auditor("cask", formula_text, core_tap: true, new_formula: true,
|
|
spdx_license_data:, spdx_exception_data:)
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match <<~EOS
|
|
Formula cask contains invalid or deprecated SPDX license exceptions: ["#{deprecated_spdx_exception}"].
|
|
For a list of valid license exceptions check:
|
|
https://spdx.org/licenses/exceptions-index.html
|
|
EOS
|
|
end
|
|
|
|
it "checks online and verifies that a standard license id is in the same exempted license group " \
|
|
"as what is indicated on its GitHub repo", :needs_network do
|
|
fa = formula_auditor "cask", <<~RUBY, spdx_license_data:, online: true, new_formula: true
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "GPL-3.0-or-later"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "checks online and verifies that a standard license array is in the same exempted license group " \
|
|
"as what is indicated on its GitHub repo", :needs_network do
|
|
fa = formula_auditor "cask", <<~RUBY, spdx_license_data:, online: true, new_formula: true
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license any_of: ["GPL-3.0-or-later", "MIT"]
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "checks online and detects that a formula-specified license is not " \
|
|
"the same as what is indicated on its GitHub repository", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "0BSD"
|
|
end
|
|
RUBY
|
|
fa = formula_auditor "cask", formula_text, spdx_license_data:,
|
|
online: true, core_tap: true, new_formula: true
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message])
|
|
.to eq 'Formula license ["0BSD"] does not match GitHub license ["GPL-3.0"].'
|
|
end
|
|
|
|
it "allows a formula-specified license that differs from its GitHub " \
|
|
"repository for formulae on the mismatched license allowlist", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license "0BSD"
|
|
end
|
|
RUBY
|
|
fa = formula_auditor "cask", formula_text, spdx_license_data:,
|
|
online: true, core_tap: true, new_formula: true,
|
|
tap_audit_exceptions: { permitted_formula_license_mismatches: ["cask"] }
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "checks online and detects that an array of license does not contain " \
|
|
"what is indicated on its GitHub repository", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license #{license_any_mismatch}
|
|
end
|
|
RUBY
|
|
fa = formula_auditor "cask", formula_text, spdx_license_data:,
|
|
online: true, core_tap: true, new_formula: true
|
|
|
|
fa.audit_license
|
|
expect(fa.problems.first[:message]).to match "Formula license [\"0BSD\", \"MIT\"] " \
|
|
"does not match GitHub license [\"GPL-3.0\"]."
|
|
end
|
|
|
|
it "checks online and verifies that an array of license contains " \
|
|
"what is indicated on its GitHub repository", :needs_network do
|
|
formula_text = <<~RUBY
|
|
class Cask < Formula
|
|
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
|
head "https://github.com/cask/cask.git"
|
|
license #{license_any}
|
|
end
|
|
RUBY
|
|
fa = formula_auditor "cask", formula_text, spdx_license_data:,
|
|
online: true, core_tap: true, new_formula: true
|
|
|
|
fa.audit_license
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_file" do
|
|
specify "no issue" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_file
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_name" do
|
|
specify "no issue" do
|
|
fa = formula_auditor "foo", <<~RUBY, core_tap: true, strict: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_name
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
specify "uppercase formula name" do
|
|
fa = formula_auditor "Foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/Foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_name
|
|
expect(fa.problems.first[:message]).to match "must not contain uppercase letters"
|
|
end
|
|
end
|
|
|
|
describe "#audit_resource_name_matches_pypi_package_name_in_url" do
|
|
it "reports a problem if the resource name does not match the python sdist name" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "abc123"
|
|
homepage "https://brew.sh"
|
|
|
|
resource "Something" do
|
|
url "https://files.pythonhosted.org/packages/FooSomething-1.0.0.tar.gz"
|
|
sha256 "def456"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems.first[:message])
|
|
.to match("resource name should be `FooSomething` to match the PyPI package name")
|
|
end
|
|
|
|
it "reports a problem if the resource name does not match the python wheel name" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "abc123"
|
|
homepage "https://brew.sh"
|
|
|
|
resource "Something" do
|
|
url "https://files.pythonhosted.org/packages/FooSomething-1.0.0-py3-none-any.whl"
|
|
sha256 "def456"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems.first[:message])
|
|
.to match("resource name should be `FooSomething` to match the PyPI package name")
|
|
end
|
|
end
|
|
|
|
describe "#check_service_command" do
|
|
specify "Not installed" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
service do
|
|
run []
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
expect(fa.check_service_command(fa.formula)).to match nil
|
|
end
|
|
|
|
specify "No service" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
end
|
|
RUBY
|
|
|
|
mkdir_p fa.formula.prefix
|
|
expect(fa.check_service_command(fa.formula)).to match nil
|
|
end
|
|
|
|
specify "No command" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
service do
|
|
run []
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
mkdir_p fa.formula.prefix
|
|
expect(fa.check_service_command(fa.formula)).to match nil
|
|
end
|
|
|
|
specify "Invalid command" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
service do
|
|
run [HOMEBREW_PREFIX/"bin/something"]
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
mkdir_p fa.formula.prefix
|
|
expect(fa.check_service_command(fa.formula)).to match "Service command does not exist"
|
|
end
|
|
end
|
|
|
|
describe "#audit_github_repository" do
|
|
specify "#audit_github_repository when HOMEBREW_NO_GITHUB_API is set" do
|
|
ENV["HOMEBREW_NO_GITHUB_API"] = "1"
|
|
|
|
fa = formula_auditor "foo", <<~RUBY, strict: true, online: true
|
|
class Foo < Formula
|
|
homepage "https://github.com/example/example"
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_github_repository
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_github_repository_archived" do
|
|
specify "#audit_github_repository_archived when HOMEBREW_NO_GITHUB_API is set" do
|
|
fa = formula_auditor "foo", <<~RUBY, strict: true, online: true
|
|
class Foo < Formula
|
|
homepage "https://github.com/example/example"
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_github_repository_archived
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_gitlab_repository" do
|
|
specify "#audit_gitlab_repository for stars, forks and creation date" do
|
|
fa = formula_auditor "foo", <<~RUBY, strict: true, online: true
|
|
class Foo < Formula
|
|
homepage "https://gitlab.com/libtiff/libtiff"
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_gitlab_repository
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_gitlab_repository_archived" do
|
|
specify "#audit gitlab repository for archived status" do
|
|
fa = formula_auditor "foo", <<~RUBY, strict: true, online: true
|
|
class Foo < Formula
|
|
homepage "https://gitlab.com/libtiff/libtiff"
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_gitlab_repository_archived
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_bitbucket_repository" do
|
|
specify "#audit_bitbucket_repository for stars, forks and creation date" do
|
|
fa = formula_auditor "foo", <<~RUBY, strict: true, online: true
|
|
class Foo < Formula
|
|
homepage "https://bitbucket.com/libtiff/libtiff"
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_bitbucket_repository
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_specs" do
|
|
let(:livecheck_throttle) { "livecheck do\n throttle 10\n end" }
|
|
let(:versioned_head_spec_list) { { versioned_head_spec_allowlist: ["foo"] } }
|
|
|
|
it "doesn't allow to miss a checksum" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems.first[:message]).to match "Checksum is missing"
|
|
end
|
|
|
|
it "allows to miss a checksum for git strategy" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo.git", tag: "1.0", revision: "f5e00e485e7aa4c5baa20355b27e3b84a6912790"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "allows to miss a checksum for HEAD" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://brew.sh/foo.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "allows versions with no throttle rate" do
|
|
fa = formula_auditor "bar", <<~RUBY, core_tap: true
|
|
class Bar < Formula
|
|
url "https://brew.sh/foo-1.0.1.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "allows major/minor versions with throttle rate" do
|
|
fa = formula_auditor "foo", <<~RUBY, core_tap: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
#{livecheck_throttle}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "allows patch versions to be multiples of the throttle rate" do
|
|
fa = formula_auditor "foo", <<~RUBY, core_tap: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.10.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
#{livecheck_throttle}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "doesn't allow patch versions that aren't multiples of the throttle rate" do
|
|
fa = formula_auditor "foo", <<~RUBY, core_tap: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.1.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
#{livecheck_throttle}
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems.first[:message]).to match "should only be updated every 10 releases on multiples of 10"
|
|
end
|
|
|
|
it "allows non-versioned formulae to have a `HEAD` spec" do
|
|
fa = formula_auditor "bar", <<~RUBY, core_tap: true, tap_audit_exceptions: versioned_head_spec_list
|
|
class Bar < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://brew.sh/foo.git"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
|
|
it "doesn't allow versioned formulae to have a `HEAD` spec" do
|
|
fa = formula_auditor "bar@1", <<~RUBY, core_tap: true, tap_audit_exceptions: versioned_head_spec_list
|
|
class BarAT1 < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://brew.sh/foo.git"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems.first[:message]).to match "Versioned formulae should not have a `HEAD` spec"
|
|
end
|
|
|
|
it "allows versioned formulae on the allowlist to have a `HEAD` spec" do
|
|
fa = formula_auditor "foo", <<~RUBY, core_tap: true, tap_audit_exceptions: versioned_head_spec_list
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://brew.sh/foo.git"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_deps" do
|
|
describe "a dependency on a macOS-provided keg-only formula" do
|
|
describe "which is allowlisted" do
|
|
subject(:f_a) { fa }
|
|
|
|
let(:fa) do
|
|
formula_auditor "foo", <<~RUBY, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
depends_on "openssl"
|
|
end
|
|
RUBY
|
|
end
|
|
|
|
let(:f_openssl) do
|
|
formula do
|
|
url "https://brew.sh/openssl-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
keg_only :provided_by_macos
|
|
end
|
|
end
|
|
|
|
before do
|
|
allow(fa.formula.deps.first)
|
|
.to receive(:to_formula).and_return(f_openssl)
|
|
fa.audit_deps
|
|
end
|
|
|
|
it(:problems) { expect(f_a.problems).to be_empty }
|
|
end
|
|
|
|
describe "which is not allowlisted", :needs_macos do
|
|
subject(:f_a) { fa }
|
|
|
|
let(:fa) do
|
|
formula_auditor "foo", <<~RUBY, new_formula: true, core_tap: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
depends_on "bc"
|
|
end
|
|
RUBY
|
|
end
|
|
|
|
let(:f_bc) do
|
|
formula do
|
|
url "https://brew.sh/bc-1.0.tgz"
|
|
homepage "https://brew.sh"
|
|
|
|
keg_only :provided_by_macos
|
|
end
|
|
end
|
|
|
|
before do
|
|
allow(fa.formula.deps.first)
|
|
.to receive(:to_formula).and_return(f_bc)
|
|
fa.audit_deps
|
|
end
|
|
|
|
it(:new_formula_problems) do
|
|
expect(f_a.new_formula_problems)
|
|
.to include(a_hash_including(message: a_string_matching(/is provided by macOS/)))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#audit_stable_version" do
|
|
subject do
|
|
fa = described_class.new(Formulary.factory(formula_path), git: true)
|
|
fa.audit_stable_version
|
|
fa.problems.first&.fetch(:message)
|
|
end
|
|
|
|
before do
|
|
origin_formula_path.dirname.mkpath
|
|
origin_formula_path.write <<~RUBY
|
|
class Foo#{foo_version} < Formula
|
|
url "https://brew.sh/foo-1.0.tar.gz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
revision 2
|
|
version_scheme 1
|
|
end
|
|
RUBY
|
|
|
|
origin_tap_path.mkpath
|
|
origin_tap_path.cd do
|
|
system "git", "init"
|
|
system "git", "add", "--all"
|
|
system "git", "commit", "-m", "init"
|
|
end
|
|
|
|
tap_path.mkpath
|
|
tap_path.cd do
|
|
system "git", "clone", origin_tap_path, "."
|
|
end
|
|
end
|
|
|
|
describe "versions" do
|
|
context "when uncommitted should not decrease" do
|
|
before { formula_gsub "foo-1.0.tar.gz", "foo-0.9.tar.gz" }
|
|
|
|
it { is_expected.to match("stable version should not decrease (from 1.0 to 0.9)") }
|
|
end
|
|
|
|
context "when committed can decrease" do
|
|
before do
|
|
formula_gsub_origin_commit "revision 2"
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-0.9.tar.gz"
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
|
|
describe "can decrease with version_scheme increased" do
|
|
before do
|
|
formula_gsub "revision 2"
|
|
formula_gsub "foo-1.0.tar.gz", "foo-0.9.tar.gz"
|
|
formula_gsub "version_scheme 1", "version_scheme 2"
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#audit_revision" do
|
|
subject do
|
|
fa = described_class.new(Formulary.factory(formula_path), git: true)
|
|
fa.audit_revision
|
|
fa.problems.first&.fetch(:message)
|
|
end
|
|
|
|
before do
|
|
origin_formula_path.dirname.mkpath
|
|
origin_formula_path.write <<~RUBY
|
|
class Foo#{foo_version} < Formula
|
|
url "https://brew.sh/foo-1.0.tar.gz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
revision 2
|
|
version_scheme 1
|
|
end
|
|
RUBY
|
|
|
|
origin_tap_path.mkpath
|
|
origin_tap_path.cd do
|
|
system "git", "init"
|
|
system "git", "add", "--all"
|
|
system "git", "commit", "-m", "init"
|
|
end
|
|
|
|
tap_path.mkpath
|
|
tap_path.cd do
|
|
system "git", "clone", origin_tap_path, "."
|
|
end
|
|
end
|
|
|
|
describe "new formulae should not have a revision" do
|
|
it "doesn't allow new formulae to have a revision" do
|
|
fa = formula_auditor "foo", <<~RUBY, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
revision 1
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_revision
|
|
|
|
expect(fa.new_formula_problems).to include(
|
|
a_hash_including(message: a_string_matching(/should not define a revision/)),
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "revisions" do
|
|
describe "should not be removed when first committed above 0" do
|
|
it { is_expected.to be_nil }
|
|
end
|
|
|
|
describe "with the same version, should not decrease" do
|
|
before { formula_gsub_origin_commit "revision 2", "revision 1" }
|
|
|
|
it { is_expected.to match("revision should not decrease (from 2 to 1)") }
|
|
end
|
|
|
|
describe "should not be removed with the same version" do
|
|
before { formula_gsub_origin_commit "revision 2" }
|
|
|
|
it { is_expected.to match("revision should not decrease (from 2 to 0)") }
|
|
end
|
|
|
|
describe "should not decrease with the same, uncommitted version" do
|
|
before { formula_gsub "revision 2", "revision 1" }
|
|
|
|
it { is_expected.to match("revision should not decrease (from 2 to 1)") }
|
|
end
|
|
|
|
describe "should be removed with a newer version" do
|
|
before { formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" }
|
|
|
|
it { is_expected.to match("'revision 2' should be removed") }
|
|
end
|
|
|
|
describe "should be removed with a newer local version" do
|
|
before { formula_gsub "foo-1.0.tar.gz", "foo-1.1.tar.gz" }
|
|
|
|
it { is_expected.to match("'revision 2' should be removed") }
|
|
end
|
|
|
|
describe "should not warn on an newer version revision removal" do
|
|
before do
|
|
formula_gsub_origin_commit "revision 2", ""
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
|
|
describe "should not warn when revision from previous version matches current revision" do
|
|
before do
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub_origin_commit "revision 2", "# no revision"
|
|
formula_gsub_origin_commit "# no revision", "revision 1"
|
|
formula_gsub_origin_commit "revision 1", "revision 2"
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
|
|
describe "should only increment by 1 with an uncommitted version" do
|
|
before do
|
|
formula_gsub "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub "revision 2", "revision 4"
|
|
end
|
|
|
|
it { is_expected.to match("revisions should only increment by 1") }
|
|
end
|
|
|
|
describe "should not warn on past increment by more than 1" do
|
|
before do
|
|
formula_gsub_origin_commit "revision 2", "# no revision"
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub_origin_commit "# no revision", "revision 3"
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#audit_version_scheme" do
|
|
subject do
|
|
fa = described_class.new(Formulary.factory(formula_path), git: true)
|
|
fa.audit_version_scheme
|
|
fa.problems.first&.fetch(:message)
|
|
end
|
|
|
|
before do
|
|
origin_formula_path.dirname.mkpath
|
|
origin_formula_path.write <<~RUBY
|
|
class Foo#{foo_version} < Formula
|
|
url "https://brew.sh/foo-1.0.tar.gz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
revision 2
|
|
version_scheme 1
|
|
end
|
|
RUBY
|
|
|
|
origin_tap_path.mkpath
|
|
origin_tap_path.cd do
|
|
system "git", "init"
|
|
system "git", "add", "--all"
|
|
system "git", "commit", "-m", "init"
|
|
end
|
|
|
|
tap_path.mkpath
|
|
tap_path.cd do
|
|
system "git", "clone", origin_tap_path, "."
|
|
end
|
|
end
|
|
|
|
describe "version_schemes" do
|
|
describe "should not decrease with the same version" do
|
|
before { formula_gsub_origin_commit "version_scheme 1" }
|
|
|
|
it { is_expected.to match("version_scheme should not decrease (from 1 to 0)") }
|
|
end
|
|
|
|
describe "should not decrease with a new version" do
|
|
before do
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub_origin_commit "revision 2", ""
|
|
formula_gsub_origin_commit "version_scheme 1", ""
|
|
end
|
|
|
|
it { is_expected.to match("version_scheme should not decrease (from 1 to 0)") }
|
|
end
|
|
|
|
describe "should only increment by 1" do
|
|
before do
|
|
formula_gsub_origin_commit "version_scheme 1", "# no version_scheme"
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub_origin_commit "revision 2", ""
|
|
formula_gsub_origin_commit "# no version_scheme", "version_scheme 3"
|
|
end
|
|
|
|
it { is_expected.to match("version_schemes should only increment by 1") }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#audit_unconfirmed_checksum_change" do
|
|
subject do
|
|
fa = described_class.new(Formulary.factory(formula_path), git: true)
|
|
fa.audit_unconfirmed_checksum_change
|
|
fa.problems.first&.fetch(:message)
|
|
end
|
|
|
|
before do
|
|
origin_formula_path.dirname.mkpath
|
|
origin_formula_path.write <<~RUBY
|
|
class Foo#{foo_version} < Formula
|
|
url "https://brew.sh/foo-1.0.tar.gz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
revision 2
|
|
version_scheme 1
|
|
end
|
|
RUBY
|
|
|
|
origin_tap_path.mkpath
|
|
origin_tap_path.cd do
|
|
system "git", "init"
|
|
system "git", "add", "--all"
|
|
system "git", "commit", "-m", "init"
|
|
end
|
|
|
|
tap_path.mkpath
|
|
tap_path.cd do
|
|
system "git", "clone", origin_tap_path, "."
|
|
end
|
|
end
|
|
|
|
describe "checksums" do
|
|
describe "should not change with the same version" do
|
|
before do
|
|
formula_gsub(
|
|
'sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"',
|
|
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
|
)
|
|
end
|
|
|
|
it { is_expected.to match("stable sha256 changed without the url/version also changing") }
|
|
end
|
|
|
|
describe "should not change with the same version when not the first commit" do
|
|
before do
|
|
formula_gsub_origin_commit(
|
|
'sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"',
|
|
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
|
)
|
|
formula_gsub_origin_commit "revision 2"
|
|
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub(
|
|
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
|
'sha256 "e048c5e6144f5932d8672c2fade81d9073d5b3ca1517b84df006de3d25414fc1"',
|
|
)
|
|
end
|
|
|
|
it { is_expected.to match("stable sha256 changed without the url/version also changing") }
|
|
end
|
|
|
|
describe "can change with the different version" do
|
|
before do
|
|
formula_gsub_origin_commit(
|
|
'sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"',
|
|
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
|
)
|
|
formula_gsub "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
|
formula_gsub_origin_commit(
|
|
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
|
'sha256 "e048c5e6144f5932d8672c2fade81d9073d5b3ca1517b84df006de3d25414fc1"',
|
|
)
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
|
|
describe "can be removed when switching schemes" do
|
|
before do
|
|
formula_gsub_origin_commit(
|
|
'url "https://brew.sh/foo-1.0.tar.gz"',
|
|
'url "https://foo.com/brew/bar.git", tag: "1.0", revision: "f5e00e485e7aa4c5baa20355b27e3b84a6912790"',
|
|
)
|
|
formula_gsub_origin_commit('sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"',
|
|
"")
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#audit_versioned_keg_only" do
|
|
specify "it warns when a versioned formula is not `keg_only`" do
|
|
fa = formula_auditor "foo@1.1", <<~RUBY, core_tap: true
|
|
class FooAT11 < Formula
|
|
url "https://brew.sh/foo-1.1.tgz"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_versioned_keg_only
|
|
|
|
expect(fa.problems.first[:message])
|
|
.to match("Versioned formulae in homebrew/core should use `keg_only :versioned_formula`")
|
|
end
|
|
|
|
specify "it warns when a versioned formula has an incorrect `keg_only` reason" do
|
|
fa = formula_auditor "foo@1.1", <<~RUBY, core_tap: true
|
|
class FooAT11 < Formula
|
|
url "https://brew.sh/foo-1.1.tgz"
|
|
|
|
keg_only :provided_by_macos
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_versioned_keg_only
|
|
|
|
expect(fa.problems.first[:message])
|
|
.to match("Versioned formulae in homebrew/core should use `keg_only :versioned_formula`")
|
|
end
|
|
|
|
specify "it does not warn when a versioned formula has `keg_only :versioned_formula`" do
|
|
fa = formula_auditor "foo@1.1", <<~RUBY, core_tap: true
|
|
class FooAT11 < Formula
|
|
url "https://brew.sh/foo-1.1.tgz"
|
|
|
|
keg_only :versioned_formula
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_versioned_keg_only
|
|
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_conflicts" do
|
|
before do
|
|
# We don't really test the formula text retrieval here
|
|
allow(File).to receive(:open).and_return("")
|
|
end
|
|
|
|
specify "it warns when conflicting with non-existing formula" do
|
|
foo = formula("foo") do
|
|
url "https://brew.sh/bar-1.0.tgz"
|
|
|
|
conflicts_with "bar"
|
|
end
|
|
|
|
fa = described_class.new foo
|
|
fa.audit_conflicts
|
|
|
|
expect(fa.problems.first[:message])
|
|
.to match("Can't find conflicting formula \"bar\"")
|
|
end
|
|
|
|
specify "it warns when conflicting with itself" do
|
|
foo = formula("foo") do
|
|
url "https://brew.sh/bar-1.0.tgz"
|
|
|
|
conflicts_with "foo"
|
|
end
|
|
stub_formula_loader foo
|
|
|
|
fa = described_class.new foo
|
|
fa.audit_conflicts
|
|
|
|
expect(fa.problems.first[:message])
|
|
.to match("Formula should not conflict with itself")
|
|
end
|
|
|
|
specify "it warns when another formula does not have a symmetric conflict" do
|
|
stub_formula_loader formula("gcc") { url "gcc-1.0" }
|
|
stub_formula_loader formula("glibc") { url "glibc-1.0" }
|
|
|
|
foo = formula("foo") do
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
end
|
|
stub_formula_loader foo
|
|
|
|
bar = formula("bar") do
|
|
url "https://brew.sh/bar-1.0.tgz"
|
|
|
|
conflicts_with "foo"
|
|
end
|
|
|
|
fa = described_class.new bar
|
|
fa.audit_conflicts
|
|
|
|
expect(fa.problems.first[:message])
|
|
.to match("Formula foo should also have a conflict declared with bar")
|
|
end
|
|
end
|
|
end
|