From e8bfa238773ade98708cadf500a1cb22b24f2b98 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Thu, 10 Jul 2025 08:05:36 +0000 Subject: [PATCH] Support Cask renames when installing/dumping This adds support for Cask old tokens used for renames of Casks. We'll now correctly check these at installation time to avoid repeatedly installing renamed Casks and dump them in the Brewfile. We also use this logic to avoid cleaning up renamed Casks. --- Library/Homebrew/bundle/cask_dumper.rb | 20 ++++++++++++++++++ Library/Homebrew/bundle/cask_installer.rb | 17 +++++++++++++-- Library/Homebrew/bundle/commands/cleanup.rb | 13 ++++++++---- .../Homebrew/test/bundle/cask_dumper_spec.rb | 21 +++++++++++++++++++ .../test/bundle/commands/cleanup_spec.rb | 4 +++- 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/bundle/cask_dumper.rb b/Library/Homebrew/bundle/cask_dumper.rb index b3381d1ae8..ef1059b7e4 100644 --- a/Library/Homebrew/bundle/cask_dumper.rb +++ b/Library/Homebrew/bundle/cask_dumper.rb @@ -8,6 +8,7 @@ module Homebrew @casks = nil @cask_names = nil @cask_hash = nil + @cask_oldnames = nil end def self.cask_names @@ -38,6 +39,25 @@ module Homebrew end.join("\n") end + def self.cask_oldnames + return @cask_oldnames if @cask_oldnames + + @cask_oldnames = {} + casks.each do |c| + oldnames = c.old_tokens + next if oldnames.blank? + + oldnames.each do |oldname| + @cask_oldnames[oldname] = c.full_name + if c.full_name.include? "/" # tap cask + tap_name = c.full_name.rpartition("/").first + @cask_oldnames["#{tap_name}/#{oldname}"] = c.full_name + end + end + end + @cask_oldnames + end + def self.formula_dependencies(cask_list) return [] unless Bundle.cask_installed? return [] if cask_list.blank? diff --git a/Library/Homebrew/bundle/cask_installer.rb b/Library/Homebrew/bundle/cask_installer.rb index 4014666d73..c559fa8523 100644 --- a/Library/Homebrew/bundle/cask_installer.rb +++ b/Library/Homebrew/bundle/cask_installer.rb @@ -87,12 +87,25 @@ module Homebrew !cask_upgradable?(cask) end + def self.cask_in_array?(cask, array) + return true if array.include?(cask) + return true if array.include?(cask.split("/").last) + + require "bundle/cask_dumper" + old_names = Homebrew::Bundle::CaskDumper.cask_oldnames + old_name = old_names[cask] + old_name ||= old_names[cask.split("/").last] + return true if old_name && array.include?(old_name) + + false + end + def self.cask_installed?(cask) - installed_casks.include? cask + cask_in_array?(cask, installed_casks) end def self.cask_upgradable?(cask) - outdated_casks.include? cask + cask_in_array?(cask, outdated_casks) end def self.installed_casks diff --git a/Library/Homebrew/bundle/commands/cleanup.rb b/Library/Homebrew/bundle/commands/cleanup.rb index f72aaee03c..20f99f9208 100644 --- a/Library/Homebrew/bundle/commands/cleanup.rb +++ b/Library/Homebrew/bundle/commands/cleanup.rb @@ -128,9 +128,10 @@ module Homebrew kept_formulae = @dsl.entries.select { |e| e.type == :brew }.map(&:name) kept_formulae += Homebrew::Bundle::CaskDumper.formula_dependencies(kept_casks) kept_formulae.map! do |f| - Homebrew::Bundle::FormulaDumper.formula_aliases[f] || - Homebrew::Bundle::FormulaDumper.formula_oldnames[f] || - f + Homebrew::Bundle::FormulaDumper.formula_aliases.fetch( + f, + Homebrew::Bundle::FormulaDumper.formula_oldnames.fetch(f, f), + ) end kept_formulae + recursive_dependencies(Homebrew::Bundle::FormulaDumper.formulae, kept_formulae) @@ -142,7 +143,11 @@ module Homebrew return @kept_casks if @kept_casks @dsl ||= Brewfile.read(global:, file:) - @kept_casks = @dsl.entries.select { |e| e.type == :cask }.map(&:name) + kept_casks = @dsl.entries.select { |e| e.type == :cask }.flat_map(&:name) + kept_casks.map! do |c| + Homebrew::Bundle::CaskDumper.cask_oldnames.fetch(c, c) + end + @kept_casks = kept_casks end private_class_method def self.recursive_dependencies(current_formulae, formulae_names, top_level: true) diff --git a/Library/Homebrew/test/bundle/cask_dumper_spec.rb b/Library/Homebrew/test/bundle/cask_dumper_spec.rb index 857176b1d5..c9518ee92c 100644 --- a/Library/Homebrew/test/bundle/cask_dumper_spec.rb +++ b/Library/Homebrew/test/bundle/cask_dumper_spec.rb @@ -95,6 +95,27 @@ RSpec.describe Homebrew::Bundle::CaskDumper do end end + describe "#cask_oldnames" do + before do + described_class.reset! + end + + it "returns an empty string when no casks are installed" do + expect(dumper.cask_oldnames).to eql({}) + end + + it "returns a hash with installed casks old names" do + foo = instance_double(Cask::Cask, to_s: "foo", old_tokens: ["oldfoo"], full_name: "qux/quuz/foo") + bar = instance_double(Cask::Cask, to_s: "bar", old_tokens: [], full_name: "bar") + allow(Cask::Caskroom).to receive(:casks).and_return([foo, bar]) + allow(Homebrew::Bundle).to receive(:cask_installed?).and_return(true) + expect(dumper.cask_oldnames).to eql({ + "qux/quuz/oldfoo" => "qux/quuz/foo", + "oldfoo" => "qux/quuz/foo", + }) + end + end + describe "#formula_dependencies" do context "when the given casks don't have formula dependencies" do before do diff --git a/Library/Homebrew/test/bundle/commands/cleanup_spec.rb b/Library/Homebrew/test/bundle/commands/cleanup_spec.rb index 506817cc7c..efecab654f 100644 --- a/Library/Homebrew/test/bundle/commands/cleanup_spec.rb +++ b/Library/Homebrew/test/bundle/commands/cleanup_spec.rb @@ -38,7 +38,9 @@ RSpec.describe Homebrew::Bundle::Commands::Cleanup do end it "computes which casks to uninstall" do - allow(Homebrew::Bundle::CaskDumper).to receive(:casks).and_return(%w[123 456]) + cask_123 = instance_double(Cask::Cask, to_s: "123", old_tokens: []) + cask_456 = instance_double(Cask::Cask, to_s: "456", old_tokens: []) + allow(Homebrew::Bundle::CaskDumper).to receive(:casks).and_return([cask_123, cask_456]) expect(described_class.casks_to_uninstall).to eql(%w[456]) end