From beb97992653a6eb80acbfc7cab9a79be83bae005 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Wed, 13 Dec 2023 13:54:37 +0000 Subject: [PATCH] cmd/untap: fix untapping syntax failure. If an installed cask is invalid on attempting an untap: it will prevent untapping that cask. Fix this in two ways: one more specific to `untap` and one more generally to other commands too: - specific: only read the actual formulae/casks from the tap we're untapping instead of all of those that are installed - general: rescue more exceptions in `Cask::Caskroom.casks` (like we already do for `Formula.installed` --- Library/Homebrew/cask/caskroom.rb | 26 +++++++++++---------- Library/Homebrew/cmd/untap.rb | 38 +++++++++++++++++++++++++++---- Library/Homebrew/formula.rb | 7 ++++++ 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/Library/Homebrew/cask/caskroom.rb b/Library/Homebrew/cask/caskroom.rb index bef02c7461c12..5aac963b10eb8 100644 --- a/Library/Homebrew/cask/caskroom.rb +++ b/Library/Homebrew/cask/caskroom.rb @@ -22,6 +22,12 @@ def self.paths end private_class_method :paths + # Return all tokens for installed casks. + sig { returns(T::Array[String]) } + def self.tokens + paths.map(&:basename).map(&:to_s) + end + sig { returns(T::Boolean) } def self.any_casks_installed? paths.any? @@ -46,18 +52,14 @@ def self.ensure_caskroom_exists sig { params(config: T.nilable(Config)).returns(T::Array[Cask]) } def self.casks(config: nil) - paths.sort.map do |path| - token = path.basename.to_s - - begin - CaskLoader.load(token, config: config) - rescue TapCaskAmbiguityError - tap_path = CaskLoader.tap_paths(token).first - CaskLoader::FromTapPathLoader.new(tap_path).load(config: config) - rescue CaskUnavailableError - # Don't blow up because of a single unavailable cask. - nil - end + tokens.sort.map do |token| + CaskLoader.load(token, config: config) + rescue TapCaskAmbiguityError + tap_path = CaskLoader.tap_paths(token).first + CaskLoader::FromTapPathLoader.new(tap_path).load(config: config) + rescue + # Don't blow up because of a single unavailable cask. + nil end.compact end end diff --git a/Library/Homebrew/cmd/untap.rb b/Library/Homebrew/cmd/untap.rb index bf12b54b8dca1..99af3c2638c39 100644 --- a/Library/Homebrew/cmd/untap.rb +++ b/Library/Homebrew/cmd/untap.rb @@ -1,4 +1,4 @@ -# typed: true +# typed: strict # frozen_string_literal: true require "cli/parser" @@ -19,15 +19,45 @@ def untap_args 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 != "homebrew/cask") - installed_tap_formulae = Formula.installed.select { |formula| formula.tap == tap } - installed_tap_casks = Cask::Caskroom.casks.select { |cask| cask.tap == tap } + 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.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) + + formula = begin + Formulary.factory("#{tap.name}/#{formula_name}") + rescue + # Don't blow up because of a single unavailable formula. + next + end + + formula if formula.any_version_installed? + end.compact + + installed_cask_tokens = T.let(nil, T.nilable(T::Set[String])) + installed_tap_casks = tap.cask_tokens.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) + + cask = begin + Cask::CaskLoader.load("#{tap.name}/#{cask_token}") + rescue + # Don't blow up because of a single unavailable cask. + next + end + + cask if cask.installed? + end.compact if installed_tap_formulae.present? || installed_tap_casks.present? installed_names = (installed_tap_formulae + installed_tap_casks.map(&:token)).join("\n") diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index ddd868f888b8b..8ac85affdd6c1 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1950,6 +1950,13 @@ def self.racks end end + # An array of all currently installed formula names. + # @private + sig { returns(T::Array[String]) } + def self.installed_formula_names + racks.map(&:basename).map(&:to_s) + end + # An array of all installed {Formula} # @private def self.installed