From 8e4c2550b458b55ff667a8a7726fd402d19e5466 Mon Sep 17 00:00:00 2001 From: Patrick Linnane Date: Sun, 11 Oct 2026 09:59:46 -0700 Subject: [PATCH] Check cask receipt tap when untapping Only select installed casks whose install receipt records the tap being untapped, matching the check already made for formula kegs. Cask tokens are not unique across taps, so a fork of homebrew/cask otherwise claimed casks installed from homebrew/cask. Casks without a receipt still fall back to the tap they were loaded from. Signed-off-by: Patrick Linnane --- Library/Homebrew/cmd/untap.rb | 2 +- Library/Homebrew/test/cmd/untap_spec.rb | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/cmd/untap.rb b/Library/Homebrew/cmd/untap.rb index 04e1f86d863fa..8d485311cb6f5 100644 --- a/Library/Homebrew/cmd/untap.rb +++ b/Library/Homebrew/cmd/untap.rb @@ -136,7 +136,7 @@ def installed_casks_for(tap:) next end - cask if cask.installed? + cask if cask.installed? && cask.tab.tap == tap end end diff --git a/Library/Homebrew/test/cmd/untap_spec.rb b/Library/Homebrew/test/cmd/untap_spec.rb index 030ab9c4e6880..e4beffbbd8d92 100644 --- a/Library/Homebrew/test/cmd/untap_spec.rb +++ b/Library/Homebrew/test/cmd/untap_spec.rb @@ -249,7 +249,7 @@ class InvalidSpec < Formula describe "#installed_casks_for", :cask do shared_examples "finds installed casks in tap", :no_api do - def load_cask(token:, with_cask_file: false, mock_install: false, deprecated: false) + def load_cask(token:, with_cask_file: false, mock_install: false, deprecated: false, tap: subject) cask_source = <<~RUBY cask '#{token}' do version "1.2.3" @@ -269,10 +269,10 @@ def load_cask(token:, with_cask_file: false, mock_install: false, deprecated: fa return if deprecated - cask_loader = Cask::CaskLoader::FromContentLoader.new(cask_source, tap: subject) + cask_loader = Cask::CaskLoader::FromContentLoader.new(cask_source, tap:) cask = cask_loader.load(config: nil) - InstallHelper.install_with_caskfile(cask) if mock_install + InstallHelper.stub_cask_installation(cask) if mock_install cask end @@ -295,6 +295,19 @@ def load_cask(token:, with_cask_file: false, mock_install: false, deprecated: fa it "returns the expected casks" do expect(described_class.new(%w[arg1]).installed_casks_for(tap: subject)).to eq([currently_installed_cask]) end + + it "excludes casks installed from another tap" do + load_cask(token: "other_tap_install", with_cask_file: true, mock_install: true, + tap: subject.core_cask_tap? ? Tap.fetch("homebrew", "bar") : CoreCaskTap.instance) + + expect(described_class.new(%w[arg1]).installed_casks_for(tap: subject)).to eq([currently_installed_cask]) + end + + it "includes installed casks without a receipt" do + (currently_installed_cask.metadata_main_container_path/AbstractTab::FILENAME).unlink + + expect(described_class.new(%w[arg1]).installed_casks_for(tap: subject)).to eq([currently_installed_cask]) + end end context "with core cask tap" do