Merge pull request #23428 from Homebrew/symlink-target-check

Skip cask link when symlink already correct
This commit is contained in:
Mike McQuaid
2026-08-04 15:16:44 +00:00
committed by GitHub
2 changed files with 32 additions and 0 deletions
@@ -79,6 +79,9 @@ module Cask
(target.realpath == source.realpath || target.realpath.to_s.start_with?("#{cask.caskroom_path}/"))
opoo "#{message}; overwriting."
Utils.gain_permissions_remove(target, command:)
elsif target_links_to_source?
ohai "#{self.class.english_name} '#{source.basename}' is already linked to '#{target}'"
return
elsif (formula = conflicting_formula)
opoo "#{message} from formula #{formula}; skipping link."
return
@@ -113,6 +116,14 @@ module Cask
sudo: !target.dirname.writable?
end
sig { returns(T::Boolean) }
def target_links_to_source?
target.symlink? && target.realpath == source.realpath
rescue => e
odebug "Error checking whether #{target} links to #{source}: #{e}"
false
end
# Check if the target file is a symlink that originates from a formula
# with the same name as this cask, indicating a potential conflict
sig { returns(T.nilable(String)) }
@@ -86,6 +86,27 @@ RSpec.describe Cask::Artifact::Binary, :cask do
expect(File.readlink(expected_path)).to eq("/tmp")
end
it "skips linking when the target is already a symlink to the source" do
artifact = artifacts.first
expected_path.make_symlink(artifact.source)
expect do
artifact.install_phase(command: NeverSudoSystemCommand, force: false)
end.to output(/is already linked/).to_stdout
expect(expected_path.readlink).to eq(artifact.source)
end
it "raises a clean error when the target symlink cannot be resolved" do
artifact = artifacts.first
expected_path.make_symlink(artifact.source)
allow(artifact.target).to receive(:realpath).and_raise(Errno::EACCES)
expect do
artifact.install_phase(command: NeverSudoSystemCommand, force: false)
end.to raise_error(Cask::CaskError, /already a Binary/)
end
it "creates parent directory if it doesn't exist" do
FileUtils.rmdir binarydir