mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 22:29:27 +04:00
cask/audit: detect incorrect app case
Occasionally PR's or issues are opened in homebrew/cask due to applications not matching the right case name on case-sensitive filesystems. These are relatively rare since most users run with the standard case-insensitive, but it's still a negative user experience. This audit is intended to catch these cases.
This commit is contained in:
@@ -686,6 +686,55 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def audit_artifact_case
|
||||
return if (url = cask.url).nil?
|
||||
return unless online?
|
||||
|
||||
odebug "Auditing artifact case"
|
||||
|
||||
extract_artifacts(include_manual_installers: true) do |artifacts, tmpdir|
|
||||
artifacts.each do |artifact|
|
||||
source = case artifact
|
||||
when Artifact::Pkg, Artifact::Installer
|
||||
artifact.path
|
||||
else
|
||||
artifact.source
|
||||
end
|
||||
|
||||
source = if source.to_s.start_with?("#{cask.appdir}/")
|
||||
Pathname(source.to_s.delete_prefix("#{cask.appdir}/"))
|
||||
elsif source.absolute?
|
||||
source.relative_path_from(cask.staged_path)
|
||||
else
|
||||
source
|
||||
end
|
||||
|
||||
components = source.each_filename.to_a
|
||||
current = tmpdir
|
||||
on_disk = []
|
||||
components.each do |component|
|
||||
break unless current.directory?
|
||||
|
||||
children = current.children.map { |child| child.basename.to_s }
|
||||
match = children.find { |name| name == component } ||
|
||||
children.find { |name| name.casecmp?(component) }
|
||||
break if match.nil?
|
||||
|
||||
on_disk << match
|
||||
current /= match
|
||||
end
|
||||
|
||||
next if on_disk.length != components.length
|
||||
next if on_disk == components
|
||||
|
||||
add_error "Artifact #{source} does not match the case of the extracted " \
|
||||
"#{File.join(on_disk)}; this fails on case-sensitive filesystems.",
|
||||
location: url.location
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def audit_rosetta
|
||||
return if (url = cask.url).nil?
|
||||
|
||||
@@ -1205,6 +1205,93 @@ RSpec.describe Cask::Audit, :cask do
|
||||
end
|
||||
end
|
||||
|
||||
describe "artifact case checks" do
|
||||
let(:online) { true }
|
||||
let(:only) { ["artifact_case"] }
|
||||
let(:tmpdir) { mktmpdir }
|
||||
let(:cask) do
|
||||
Cask::Cask.new("artifact-case") do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "https://brew.sh/artifact-case.zip"
|
||||
name "Artifact Case"
|
||||
homepage "https://brew.sh/"
|
||||
|
||||
app "artifact case.app"
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
allow(audit).to receive(:extract_artifacts).and_yield(cask.artifacts, tmpdir)
|
||||
end
|
||||
|
||||
context "when the case matches" do
|
||||
before { (tmpdir/"artifact case.app").mkpath }
|
||||
|
||||
it { is_expected.to pass }
|
||||
end
|
||||
|
||||
context "when the case does not match" do
|
||||
before { (tmpdir/"Artifact Case.app").mkpath }
|
||||
|
||||
it { is_expected.to error_with(/does not match the case of the extracted/) }
|
||||
end
|
||||
|
||||
context "when both cases are present on disk" do
|
||||
# Both spellings cannot be created on a case-insensitive filesystem.
|
||||
before do
|
||||
allow(tmpdir).to receive(:children)
|
||||
.and_return([tmpdir/"Artifact Case.app", tmpdir/"artifact case.app"])
|
||||
end
|
||||
|
||||
it { is_expected.to pass }
|
||||
end
|
||||
|
||||
context "when the artifact is missing" do
|
||||
it { is_expected.to pass }
|
||||
end
|
||||
|
||||
context "when a binary in the appdir has the wrong case" do
|
||||
let(:cask) do
|
||||
Cask::Cask.new("artifact-case") do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "https://brew.sh/artifact-case.zip"
|
||||
name "Artifact Case"
|
||||
homepage "https://brew.sh/"
|
||||
|
||||
app "Artifact Case.app"
|
||||
binary "#{appdir}/Artifact Case.app/Contents/MacOS/artifact"
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
(tmpdir/"Artifact Case.app/Contents/MacOS").mkpath
|
||||
FileUtils.touch tmpdir/"Artifact Case.app/Contents/MacOS/Artifact"
|
||||
end
|
||||
|
||||
it { is_expected.to error_with(/does not match the case of the extracted/) }
|
||||
end
|
||||
|
||||
context "when a manual installer has the wrong case" do
|
||||
let(:cask) do
|
||||
Cask::Cask.new("artifact-case") do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "https://brew.sh/artifact-case.zip"
|
||||
name "Artifact Case"
|
||||
homepage "https://brew.sh/"
|
||||
|
||||
installer manual: "Artifact Case.app"
|
||||
end
|
||||
end
|
||||
|
||||
before { (tmpdir/"Artifact Case.APP").mkpath }
|
||||
|
||||
it { is_expected.to error_with(/does not match the case of the extracted/) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "minimum OS checks" do
|
||||
let(:online) { true }
|
||||
let(:only) { ["min_os"] }
|
||||
|
||||
Reference in New Issue
Block a user