
- If a HEAD branch name isn't specified at all, then the user probably wants to shortcut adding one by being told what the default branch for the repo is. Otherwise they have to click the URL, look at the GitHub UI, then type the branch name into `branch: "foo"` syntax.
1449 lines
46 KiB
Ruby
1449 lines
46 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", branch: "main"
|
|
license "GPL-3.0-or-later"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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", branch: "main"
|
|
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 "requires `branch:` to be specified for Git head URLs" do
|
|
fa = formula_auditor "foo", <<~RUBY, online: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://github.com/Homebrew/homebrew-test-bot.git"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
# This is `.last` because the first problem is the unreachable stable URL.
|
|
expect(fa.problems.last[:message]).to match("Git `head` URL must specify a branch name")
|
|
end
|
|
|
|
it "suggests a detected default branch for Git head URLs" do
|
|
fa = formula_auditor "foo", <<~RUBY, online: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://github.com/Homebrew/homebrew-test-bot.git", branch: "master"
|
|
end
|
|
RUBY
|
|
|
|
message = "Git `head` URL must specify a branch name - try `branch: \"main\"`"
|
|
fa.audit_specs
|
|
# This is `.last` because the first problem is the unreachable stable URL.
|
|
expect(fa.problems.last[:message]).to match(message)
|
|
end
|
|
|
|
it "ignores a pre-existing correct HEAD branch name" do
|
|
fa = formula_auditor "foo", <<~RUBY, online: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://github.com/Homebrew/homebrew-test-bot.git", branch: "main"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).not_to match("Git `head` URL must specify a branch name")
|
|
end
|
|
|
|
it "ignores `branch:` for non-Git head URLs" do
|
|
fa = formula_auditor "foo", <<~RUBY, online: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
head "https://brew.sh/foo.tgz", branch: "develop"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).not_to match("Git `head` URL must specify a branch name")
|
|
end
|
|
|
|
it "ignores `branch:` for `resource` URLs" do
|
|
fa = formula_auditor "foo", <<~RUBY, online: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
|
|
resource "bar" do
|
|
url "https://raw.githubusercontent.com/Homebrew/homebrew-core/HEAD/Formula/bar.rb"
|
|
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_specs
|
|
expect(fa.problems).not_to match("Git `head` URL must specify a branch name")
|
|
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", branch: "develop"
|
|
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", branch: "develop"
|
|
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", branch: "develop"
|
|
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("`revision` 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_scheme` 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", :no_api 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", :no_api 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", :no_api 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
|
|
|
|
describe "#audit_deprecate_disable" do
|
|
specify "it warns when deprecate/disable reason is invalid" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
|
|
deprecate! date: "2021-01-01", because: :foobar
|
|
end
|
|
RUBY
|
|
|
|
mkdir_p fa.formula.prefix
|
|
fa.audit_deprecate_disable
|
|
expect(fa.problems.first[:message])
|
|
.to match("foobar is not a valid deprecate! or disable! reason")
|
|
end
|
|
|
|
specify "it does not warn when deprecate/disable reason is valid" do
|
|
fa = formula_auditor "foo", <<~RUBY
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
|
|
deprecate! date: "2021-01-01", because: :repo_archived
|
|
end
|
|
RUBY
|
|
|
|
mkdir_p fa.formula.prefix
|
|
fa.audit_deprecate_disable
|
|
expect(fa.problems).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#audit_no_autobump" do
|
|
it "warns when autobump exclusion reason is not suitable for new formula" do
|
|
fa = formula_auditor "foo", <<~RUBY, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
|
|
no_autobump! because: :requires_manual_review
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_no_autobump
|
|
expect(fa.new_formula_problems.first[:message])
|
|
.to match("`:requires_manual_review` is a temporary reason intended for existing packages, " \
|
|
"use a different reason instead.")
|
|
end
|
|
|
|
it "does not warn when autobump exclusion reason is allowed" do
|
|
fa = formula_auditor "foo", <<~RUBY, new_formula: true
|
|
class Foo < Formula
|
|
url "https://brew.sh/foo-1.0.tgz"
|
|
|
|
no_autobump! because: "foo bar"
|
|
end
|
|
RUBY
|
|
|
|
fa.audit_no_autobump
|
|
expect(fa.new_formula_problems).to be_empty
|
|
end
|
|
end
|
|
end
|