mirror of
https://github.com/Homebrew/brew.git
synced 2026-10-28 09:34:42 +08:00
- New casks often omit `zap`, and the `# No zap stanza required` comment that marks a deliberate omission can't be audited. Add `zap :not_required` so a cask can declare it instead. - Serialise it as an empty `zap` stanza so that older versions of Homebrew can still load these casks from the API. - Audit new casks in official taps for a `zap` stanza on the OS and architecture being audited. CI audits each one, so this catches a cask that supports Linux but only has a `zap` in `on_macos`. - Casks have no stanza values for platforms they don't support, so have `brew audit` audit macOS-only casks under macOS and casks with `depends_on arch` on an architecture they support, as it already does for Linux-only casks. Otherwise `--os=all` or `--arch=all` fails a macOS-only cask with its `zap` in `on_macos`, or an ARM-only one with it in `on_arm`, and other audits fail the same way, e.g. on a `url` in `on_arm`. - Only suggest `brew generate-zap` when auditing macOS, as it only scans macOS paths, and have it warn that the app must have been launched before it prints `zap :not_required`. - Fix `Cask/UninstallMethodsOrder`, which crashed on the non-hash argument of `zap :not_required`.
232 lines
8.1 KiB
Ruby
232 lines
8.1 KiB
Ruby
# typed: true
|
|
# frozen_string_literal: true
|
|
|
|
require "dev-cmd/audit"
|
|
require "cmd/shared_examples/args_parse"
|
|
|
|
RSpec.describe Homebrew::DevCmd::Audit do
|
|
it_behaves_like "parseable arguments"
|
|
|
|
it "audits a Formula and Cask", :cask, :integration_test do
|
|
setup_test_formula "testball"
|
|
|
|
expect { brew "audit", "--skip-style", "--only=description", "testball", "local-caffeine" }
|
|
.to be_a_success
|
|
end
|
|
|
|
describe "#run" do
|
|
subject(:audit) { described_class.new(["--tap=homebrew/test"]) }
|
|
|
|
let(:tap_path) { mktmpdir }
|
|
let(:macos_only_cask_file) { tap_path/"Casks/macos-only-example.rb" }
|
|
let(:linux_cask_file) { tap_path/"Casks/linux-example.rb" }
|
|
let(:linux_only_cask_file) { tap_path/"Casks/linux-only-example.rb" }
|
|
let(:tap) do
|
|
instance_double(Tap, formula_files: [], cask_files: [macos_only_cask_file, linux_cask_file,
|
|
linux_only_cask_file])
|
|
end
|
|
|
|
before do
|
|
macos_only_cask_file.dirname.mkpath
|
|
macos_only_cask_file.write <<~RUBY
|
|
cask "macos-only-example" do
|
|
version "1.0"
|
|
sha256 arm: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
intel: "1111111111111111111111111111111111111111111111111111111111111111"
|
|
url "https://example.invalid/x-\#{version}.pkg"
|
|
name "Example"
|
|
desc "macOS-only cask"
|
|
homepage "https://example.invalid/"
|
|
depends_on macos: :ventura
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
linux_cask_file.write <<~RUBY
|
|
cask "linux-example" do
|
|
version "1.0"
|
|
sha256 arm: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
intel: "1111111111111111111111111111111111111111111111111111111111111111"
|
|
url "https://example.invalid/x-\#{version}.tar.gz"
|
|
name "Example"
|
|
desc "Linux-supported cask"
|
|
homepage "https://example.invalid/"
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
linux_only_cask_file.write <<~RUBY
|
|
cask "linux-only-example" do
|
|
version "1.0"
|
|
sha256 arm64_linux: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
x86_64_linux: "1111111111111111111111111111111111111111111111111111111111111111"
|
|
url "https://example.invalid/x-\#{version}.tar.gz"
|
|
name "Example"
|
|
desc "Linux-only cask"
|
|
homepage "https://example.invalid/"
|
|
depends_on :linux
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
|
|
allow(Utils::GemSetup).to receive(:install_bundler_gems!)
|
|
ENV.activate_extensions!
|
|
allow(ENV).to receive(:setup_build_environment)
|
|
allow(Tap).to receive(:fetch).and_call_original
|
|
allow(Tap).to receive(:fetch).with("homebrew/test").and_return(tap)
|
|
allow(Tap).to receive(:installed).and_return([])
|
|
end
|
|
|
|
it "audits Linux-supporting casks and skips macOS-only ones on Linux" do
|
|
problems = a_string_matching(
|
|
/\A(?=.*linux-example)(?=.*a sha256 stanza is required)(?!.*macos-only-example).*\z/m,
|
|
)
|
|
|
|
Homebrew::SimulateSystem.with(os: :linux) do
|
|
expect { audit.run }.to output(problems).to_stdout
|
|
.and output(/1 problem in 1 cask detected/).to_stderr
|
|
end
|
|
end
|
|
|
|
it "audits Linux-only casks under Linux when running on macOS" do
|
|
Homebrew::SimulateSystem.with(os: :macos) do
|
|
expect { audit.run }.not_to output.to_stdout
|
|
end
|
|
end
|
|
|
|
it "audits macOS-only casks under macOS when auditing all operating systems" do
|
|
macos_url_cask_file = tap_path/"Casks/macos-url-example.rb"
|
|
macos_url_cask_file.write <<~RUBY
|
|
cask "macos-url-example" do
|
|
version "1.0"
|
|
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
on_macos do
|
|
url "https://example.invalid/x-\#{version}.pkg"
|
|
end
|
|
|
|
name "Example"
|
|
desc "macOS-only cask"
|
|
homepage "https://example.invalid/"
|
|
|
|
depends_on macos: :ventura
|
|
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
allow(tap).to receive(:cask_files).and_return([macos_url_cask_file])
|
|
|
|
expect { described_class.new(["--tap=homebrew/test", "--os=all", "--arch=arm"]).run }
|
|
.not_to output.to_stdout
|
|
end
|
|
|
|
it "audits ARM-only casks under ARM when auditing Intel" do
|
|
arm_url_cask_file = tap_path/"Casks/arm-url-example.rb"
|
|
arm_url_cask_file.write <<~RUBY
|
|
cask "arm-url-example" do
|
|
version "1.0"
|
|
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
on_arm do
|
|
url "https://example.invalid/x-\#{version}.tar.gz"
|
|
end
|
|
|
|
name "Example"
|
|
desc "ARM-only cask"
|
|
homepage "https://example.invalid/"
|
|
|
|
depends_on arch: :arm64
|
|
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
allow(tap).to receive(:cask_files).and_return([arm_url_cask_file])
|
|
|
|
Homebrew::SimulateSystem.with(os: :macos, arch: :intel) do
|
|
expect { audit.run }.not_to output.to_stdout
|
|
end
|
|
end
|
|
|
|
it "audits casks on the architecture each operating system supports" do
|
|
os_arch_cask_file = tap_path/"Casks/os-arch-example.rb"
|
|
os_arch_cask_file.write <<~RUBY
|
|
cask "os-arch-example" do
|
|
version "1.0"
|
|
sha256 arm: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
x86_64_linux: "1111111111111111111111111111111111111111111111111111111111111111"
|
|
|
|
url "https://example.invalid/x-\#{version}.tar.gz"
|
|
name "Example"
|
|
desc "macOS ARM and Linux Intel cask"
|
|
homepage "https://example.invalid/"
|
|
|
|
on_macos do
|
|
depends_on arch: :arm64
|
|
end
|
|
|
|
on_linux do
|
|
depends_on arch: :x86_64
|
|
end
|
|
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
allow(tap).to receive(:cask_files).and_return([os_arch_cask_file])
|
|
|
|
expect { described_class.new(["--tap=homebrew/test", "--os=all", "--arch=intel"]).run }
|
|
.not_to output.to_stdout
|
|
end
|
|
|
|
it "audits Linux Intel for a cask that only requires macOS on ARM" do
|
|
arm_macos_cask_file = tap_path/"Casks/arm-macos-example.rb"
|
|
arm_macos_cask_file.write <<~RUBY
|
|
cask "arm-macos-example" do
|
|
version "1.0"
|
|
sha256 arm: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
intel: "1111111111111111111111111111111111111111111111111111111111111111",
|
|
arm64_linux: "0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
url "https://example.invalid/x-\#{version}.tar.gz"
|
|
name "Example"
|
|
desc "Cask without a Linux Intel checksum"
|
|
homepage "https://example.invalid/"
|
|
|
|
on_arm do
|
|
depends_on macos: :big_sur
|
|
end
|
|
|
|
binary "x"
|
|
end
|
|
RUBY
|
|
allow(tap).to receive(:cask_files).and_return([arm_macos_cask_file])
|
|
|
|
expect { described_class.new(["--tap=homebrew/test", "--os=all", "--arch=all"]).run }
|
|
.to output(/a sha256 stanza is required/).to_stdout
|
|
end
|
|
|
|
it "enables API access when auditing external formulae after it was automatically disabled" do
|
|
formula_file = tap_path/"Formula/example.rb"
|
|
formula_file.dirname.mkpath
|
|
formula_file.write <<~RUBY
|
|
class Example < Formula
|
|
desc "Example"
|
|
homepage "https://example.com"
|
|
url "https://example.com/example-1.0.tar.gz"
|
|
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
depends_on "dependency"
|
|
end
|
|
RUBY
|
|
allow(tap).to receive_messages(formula_files: [formula_file], cask_files: [])
|
|
formula_auditor = instance_double(Homebrew::FormulaAuditor, audit: nil, problems: [], new_formula_problems: [])
|
|
|
|
with_env(HOMEBREW_NO_INSTALL_FROM_API: "1", HOMEBREW_AUTOMATICALLY_SET_NO_INSTALL_FROM_API: "1") do
|
|
expect(Homebrew::FormulaAuditor).to receive(:new) do
|
|
expect(Homebrew::EnvConfig.no_install_from_api?).to be(false)
|
|
formula_auditor
|
|
end
|
|
|
|
audit.run
|
|
end
|
|
end
|
|
end
|
|
end
|