apainintheneck 819af3cbdd cmd/untap: fix installed formula & cask check
This wasn't working before for a few reasons.

1. It never got past the installed name check because the
installed name sets had short names and the tap names were
long names including the tap namespace too. Now we just trim the
long name before comparing it to the installed name set.

Before:

```
["name"].include?("tap/full/name") # always false
```

After:

```
["name"].include("tap/full/name".split("/").last) # sometimes true
```

2. The names we were trying to load formulae and casks with
were incorrect.

Before:

```
tap = Tap.fetch("homebrew/cask-versions")
token = "homebrew/cask-versions/token"

cask = Cask::CaskLoader.load("#{tap}/#{token}")
```

After:

```
token = "homebrew/cask-versions/token"
cask = CaskCaskLoader.load(token)
```
2024-03-10 22:33:33 -07:00

84 lines
2.7 KiB
Ruby

# typed: strict
# frozen_string_literal: true
require "cli/parser"
module Homebrew
module_function
sig { returns(CLI::Parser) }
def untap_args
Homebrew::CLI::Parser.new do
description <<~EOS
Remove a tapped formula repository.
EOS
switch "-f", "--force",
description: "Untap even if formulae or casks from this tap are currently installed."
named_args :tap, min: 1
end
end
sig { void }
def untap
args = untap_args.parse
args.named.to_installed_taps.each do |tap|
odie "Untapping #{tap} is not allowed" if tap.core_tap? && Homebrew::EnvConfig.no_install_from_api?
if Homebrew::EnvConfig.no_install_from_api? || (!tap.core_tap? && !tap.core_cask_tap?)
installed_formula_names = T.let(nil, T.nilable(T::Set[String]))
installed_tap_formulae = tap.formula_names.filter_map do |formula_name|
# initialise lazily in case there's no formulae in this tap
installed_formula_names ||= Set.new(Formula.installed_formula_names)
next unless installed_formula_names.include?(formula_name.split("/").last)
formula = begin
Formulary.factory(formula_name)
rescue
# Don't blow up because of a single unavailable formula.
next
end
# Can't use Formula#any_version_installed? because it doesn't consider
# taps correctly.
formula if formula.installed_kegs.any? { |keg| keg.tab.tap == tap }
end
installed_cask_tokens = T.let(nil, T.nilable(T::Set[String]))
installed_tap_casks = tap.cask_tokens.filter_map do |cask_token|
# initialise lazily in case there's no casks in this tap
installed_cask_tokens ||= Set.new(Cask::Caskroom.tokens)
next unless installed_cask_tokens.include?(cask_token.split("/").last)
cask = begin
Cask::CaskLoader.load(cask_token)
rescue
# Don't blow up because of a single unavailable cask.
next
end
cask if cask.installed?
end
if installed_tap_formulae.present? || installed_tap_casks.present?
installed_names = (installed_tap_formulae + installed_tap_casks.map(&:token)).join("\n")
if args.force? || Homebrew::EnvConfig.developer?
opoo <<~EOS
Untapping #{tap} even though it contains the following installed formulae or casks:
#{installed_names}
EOS
else
odie <<~EOS
Refusing to untap #{tap} because it contains the following installed formulae or casks:
#{installed_names}
EOS
end
end
end
tap.uninstall manual: true
end
end
end