diff --git a/Library/Homebrew/api/cask.rb b/Library/Homebrew/api/cask.rb index f3c6a96f81..b65d05cb59 100644 --- a/Library/Homebrew/api/cask.rb +++ b/Library/Homebrew/api/cask.rb @@ -46,8 +46,15 @@ module Homebrew json_casks, updated = Homebrew::API.fetch_json_api_file "cask.jws.json", target: HOMEBREW_CACHE_API/"cask.jws.json" + cache["renames"] = {} cache["casks"] = json_casks.to_h do |json_cask| - [json_cask["token"], json_cask.except("token")] + token = json_cask.delete("token") + + json_cask.fetch("old_tokens", []).each do |old_token| + cache["renames"][old_token] = token + end + + [token, json_cask] end updated @@ -61,7 +68,17 @@ module Homebrew write_names(regenerate: json_updated) end - cache["casks"] + cache.fetch("casks") + end + + sig { returns(T::Hash[String, String]) } + def all_renames + unless cache.key?("renames") + json_updated = download_and_cache_data! + write_names(regenerate: json_updated) + end + + cache.fetch("renames") end sig { params(regenerate: T::Boolean).void } diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index 1de2bf2378..8f5ce040df 100644 --- a/Library/Homebrew/api/formula.rb +++ b/Library/Homebrew/api/formula.rb @@ -80,7 +80,7 @@ module Homebrew cache["aliases"] end - sig { returns(Hash) } + sig { returns(T::Hash[String, String]) } def all_renames unless cache.key?("renames") json_updated = download_and_cache_data! diff --git a/Library/Homebrew/cask.rb b/Library/Homebrew/cask.rb index d3e6d8ebae..d2246dcf9e 100644 --- a/Library/Homebrew/cask.rb +++ b/Library/Homebrew/cask.rb @@ -16,6 +16,7 @@ require "cask/dsl" require "cask/installer" require "cask/macos" require "cask/metadata" +require "cask/migrator" require "cask/pkg" require "cask/quarantine" require "cask/staged" diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index 1fe172a1ac..dc0e9caf7f 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -78,6 +78,17 @@ module Cask end end + # An old name for the cask. + sig { returns(T::Array[String]) } + def old_tokens + @old_tokens ||= if tap + tap.cask_renames + .flat_map { |old_token, new_token| (new_token == token) ? old_token : [] } + else + [] + end + end + def config=(config) @config = config @@ -96,22 +107,18 @@ module Cask define_method(method_name) { |&block| @dsl.send(method_name, &block) } end - sig { returns(T::Array[[String, String]]) } - def timestamped_versions - relative_paths = Pathname.glob(metadata_timestamped_path(version: "*", timestamp: "*")) + sig { params(caskroom_path: Pathname).returns(T::Array[[String, String]]) } + def timestamped_versions(caskroom_path: self.caskroom_path) + relative_paths = Pathname.glob(metadata_timestamped_path( + version: "*", timestamp: "*", + caskroom_path: caskroom_path + )) .map { |p| p.relative_path_from(p.parent.parent) } # Sorbet is unaware that Pathname is sortable: https://github.com/sorbet/sorbet/issues/6844 T.unsafe(relative_paths).sort_by(&:basename) # sort by timestamp .map { |p| p.split.map(&:to_s) } end - def versions - timestamped_versions.map(&:first) - .reverse - .uniq - .reverse - end - def full_name return token if tap.nil? return token if tap.user == "Homebrew" @@ -119,8 +126,9 @@ module Cask "#{tap.name}/#{token}" end + sig { returns(T::Boolean) } def installed? - !versions.empty? + installed_caskfile&.exist? || false end # The caskfile is needed during installation when there are @@ -131,18 +139,41 @@ module Cask sig { returns(T.nilable(Time)) } def install_time - _, time = timestamped_versions.last - return unless time - - Time.strptime(time, Metadata::TIMESTAMP_FORMAT) + # /.metadata///Casks/.{rb,json} -> + time = installed_caskfile&.dirname&.dirname&.basename&.to_s + Time.strptime(time, Metadata::TIMESTAMP_FORMAT) if time end + sig { returns(T.nilable(Pathname)) } def installed_caskfile - installed_version = timestamped_versions.last - caskfile_dir = metadata_main_container_path.join(*installed_version, "Casks") - return caskfile_dir.join("#{token}.json") if caskfile_dir.join("#{token}.json").exist? + installed_caskroom_path = caskroom_path + installed_token = token - caskfile_dir.join("#{token}.rb") + # Check if the cask is installed with an old name. + old_tokens.each do |old_token| + old_caskroom_path = Caskroom.path/old_token + next if !old_caskroom_path.directory? || old_caskroom_path.symlink? + + installed_caskroom_path = old_caskroom_path + installed_token = old_token + break + end + + installed_version = timestamped_versions(caskroom_path: installed_caskroom_path).last + return unless installed_version + + caskfile_dir = metadata_main_container_path(caskroom_path: installed_caskroom_path) + .join(*installed_version, "Casks") + + ["json", "rb"] + .map { |ext| caskfile_dir.join("#{installed_token}.#{ext}") } + .find(&:exist?) + end + + sig { returns(T.nilable(String)) } + def installed_version + # /.metadata///Casks/.{rb,json} -> + installed_caskfile&.dirname&.dirname&.dirname&.basename&.to_s end def config_path @@ -173,6 +204,7 @@ module Cask current_download_sha.blank? || current_download_sha != new_download_sha end + sig { returns(Pathname) } def caskroom_path @caskroom_path ||= Caskroom.path.join(token) end @@ -182,26 +214,23 @@ module Cask greedy_auto_updates: greedy_auto_updates).empty? end + # TODO: Rename to `outdated_version` and only return one version. def outdated_versions(greedy: false, greedy_latest: false, greedy_auto_updates: false) # special case: tap version is not available return [] if version.nil? if version.latest? - return versions if (greedy || greedy_latest) && outdated_download_sha? + return [installed_version] if (greedy || greedy_latest) && outdated_download_sha? return [] elsif auto_updates && !greedy && !greedy_auto_updates return [] end - installed = versions - current = installed.last - # not outdated unless there is a different version on tap - return [] if current == version + return [] if installed_version == version - # collect all installed versions that are different than tap version and return them - installed.reject { |v| v == version } + [installed_version] end def outdated_info(greedy, verbose, json, greedy_latest, greedy_auto_updates) @@ -279,6 +308,7 @@ module Cask { "token" => token, "full_token" => full_name, + "old_tokens" => old_tokens, "tap" => tap&.name, "name" => name, "desc" => desc, @@ -287,7 +317,7 @@ module Cask "url_specs" => url_specs, "appcast" => appcast, "version" => version, - "installed" => versions.last, + "installed" => installed_version, "outdated" => outdated?, "sha256" => sha256, "artifacts" => artifacts_list, @@ -349,7 +379,7 @@ module Cask def api_to_local_hash(hash) hash["token"] = token - hash["installed"] = versions.last + hash["installed"] = installed_version hash["outdated"] = outdated? hash end diff --git a/Library/Homebrew/cask/cask_loader.rb b/Library/Homebrew/cask/cask_loader.rb index 0f3825ad7c..751e1d3009 100644 --- a/Library/Homebrew/cask/cask_loader.rb +++ b/Library/Homebrew/cask/cask_loader.rb @@ -401,11 +401,11 @@ module Cask self.for(ref, need_path: true).path end - def self.load(ref, config: nil) - self.for(ref).load(config: config) + def self.load(ref, config: nil, warn: true) + self.for(ref, warn: warn).load(config: config) end - def self.for(ref, need_path: false) + def self.for(ref, need_path: false, warn: true) [ FromInstanceLoader, FromContentLoader, @@ -422,7 +422,7 @@ module Cask end end - case (possible_tap_casks = tap_paths(ref)).count + case (possible_tap_casks = tap_paths(ref, warn: warn)).count when 1 return FromTapPathLoader.new(possible_tap_casks.first) when 2..Float::INFINITY @@ -441,9 +441,13 @@ module Cask Tap.default_cask_tap.cask_dir/"#{token.to_s.downcase}.rb" end - def self.tap_paths(token) + def self.tap_paths(token, warn: true) + token = token.to_s.downcase + Tap.map do |tap| - find_cask_in_tap(token.to_s.downcase, tap) + new_token = tap.cask_renames[token] + opoo "Cask #{token} was renamed to #{new_token}." if new_token && warn + find_cask_in_tap(new_token || token, tap) end.select(&:exist?) end diff --git a/Library/Homebrew/cask/caskroom.rb b/Library/Homebrew/cask/caskroom.rb index a478f3c3b6..bef02c7461 100644 --- a/Library/Homebrew/cask/caskroom.rb +++ b/Library/Homebrew/cask/caskroom.rb @@ -13,11 +13,18 @@ module Cask @path ||= HOMEBREW_PREFIX/"Caskroom" end + # Return all paths for installed casks. + sig { returns(T::Array[Pathname]) } + def self.paths + return [] unless path.exist? + + path.children.select { |p| p.directory? && !p.symlink? } + end + private_class_method :paths + sig { returns(T::Boolean) } def self.any_casks_installed? - return false unless path.exist? - - path.children.select(&:directory?).any? + paths.any? end sig { void } @@ -39,9 +46,7 @@ module Cask sig { params(config: T.nilable(Config)).returns(T::Array[Cask]) } def self.casks(config: nil) - return [] unless path.exist? - - path.children.select(&:directory?).sort.map do |path| + paths.sort.map do |path| token = path.basename.to_s begin diff --git a/Library/Homebrew/cask/info.rb b/Library/Homebrew/cask/info.rb index 0420a20a0b..3463f6ced8 100644 --- a/Library/Homebrew/cask/info.rb +++ b/Library/Homebrew/cask/info.rb @@ -39,17 +39,14 @@ module Cask def self.installation_info(cask) return "Not installed\n" unless cask.installed? - install_info = +"" - cask.versions.each do |version| - versioned_staged_path = cask.caskroom_path.join(version) - path_details = if versioned_staged_path.exist? - versioned_staged_path.abv - else - Formatter.error("does not exist") - end - install_info << "#{versioned_staged_path} (#{path_details})\n" + versioned_staged_path = cask.caskroom_path.join(cask.installed_version) + path_details = if versioned_staged_path.exist? + versioned_staged_path.abv + else + Formatter.error("does not exist") end - install_info.freeze + + "#{versioned_staged_path} (#{path_details})\n" end def self.name_info(cask) diff --git a/Library/Homebrew/cask/installer.rb b/Library/Homebrew/cask/installer.rb index 16c3d5791d..116513c630 100644 --- a/Library/Homebrew/cask/installer.rb +++ b/Library/Homebrew/cask/installer.rb @@ -7,6 +7,7 @@ require "utils/topological_hash" require "cask/config" require "cask/download" +require "cask/migrator" require "cask/quarantine" require "cgi" @@ -87,6 +88,8 @@ module Cask start_time = Time.now odebug "Cask::Installer#install" + Migrator.migrate_if_needed(@cask) + old_config = @cask.config if @cask.installed? && !force? && !reinstall? && !upgrade? return if quiet? @@ -227,8 +230,9 @@ on_request: true) next if artifact.is_a?(Artifact::Binary) && !binaries? - artifact.install_phase(command: @command, verbose: verbose?, adopt: adopt?, force: force?, - predecessor: predecessor) + artifact.install_phase( + command: @command, verbose: verbose?, adopt: adopt?, force: force?, predecessor: predecessor, + ) already_installed_artifacts.unshift(artifact) end @@ -382,7 +386,6 @@ on_request: true) end def save_config_file - metadata_subdir @cask.config_path.atomic_write(@cask.config.to_json) end @@ -408,7 +411,8 @@ on_request: true) end def remove_download_sha - FileUtils.rm_f @cask.download_sha_path if @cask.download_sha_path.exist? + FileUtils.rm_f @cask.download_sha_path + @cask.download_sha_path.parent.rmdir_if_possible end def start_upgrade(successor:) @@ -424,8 +428,8 @@ on_request: true) def restore_backup return if !backup_path.directory? || !backup_metadata_path.directory? - Pathname.new(@cask.staged_path).rmtree if @cask.staged_path.exist? - Pathname.new(@cask.metadata_versioned_path).rmtree if @cask.metadata_versioned_path.exist? + @cask.staged_path.rmtree if @cask.staged_path.exist? + @cask.metadata_versioned_path.rmtree if @cask.metadata_versioned_path.exist? backup_path.rename @cask.staged_path backup_metadata_path.rename @cask.metadata_versioned_path @@ -539,6 +543,12 @@ on_request: true) # toplevel staged distribution @cask.caskroom_path.rmdir_if_possible unless upgrade? + + # Remove symlinks for renamed casks if they are now broken. + @cask.old_tokens.each do |old_token| + old_caskroom_path = Caskroom.path/old_token + FileUtils.rm old_caskroom_path if old_caskroom_path.symlink? && !old_caskroom_path.exist? + end end def purge_caskroom_path @@ -550,9 +560,11 @@ on_request: true) # load the same cask file that was used for installation, if possible def load_installed_caskfile! + Migrator.migrate_if_needed(@cask) + installed_caskfile = @cask.installed_caskfile - if installed_caskfile.exist? + if installed_caskfile&.exist? begin @cask = CaskLoader.load(installed_caskfile) return diff --git a/Library/Homebrew/cask/list.rb b/Library/Homebrew/cask/list.rb index 287730d085..aa3a84867d 100644 --- a/Library/Homebrew/cask/list.rb +++ b/Library/Homebrew/cask/list.rb @@ -43,7 +43,7 @@ module Cask end def self.format_versioned(cask) - cask.to_s.concat(cask.versions.map(&:to_s).join(" ").prepend(" ")) + "#{cask}#{cask.installed_version&.prepend(" ")}" end end end diff --git a/Library/Homebrew/cask/metadata.rb b/Library/Homebrew/cask/metadata.rb index 123dc6e0b5..4282dd6865 100644 --- a/Library/Homebrew/cask/metadata.rb +++ b/Library/Homebrew/cask/metadata.rb @@ -9,26 +9,27 @@ module Cask METADATA_SUBDIR = ".metadata" TIMESTAMP_FORMAT = "%Y%m%d%H%M%S.%L" - def metadata_main_container_path - @metadata_main_container_path ||= caskroom_path.join(METADATA_SUBDIR) + def metadata_main_container_path(caskroom_path: self.caskroom_path) + caskroom_path.join(METADATA_SUBDIR) end - def metadata_versioned_path(version: self.version) + def metadata_versioned_path(version: self.version, caskroom_path: self.caskroom_path) cask_version = (version || :unknown).to_s raise CaskError, "Cannot create metadata path with empty version." if cask_version.empty? - metadata_main_container_path.join(cask_version) + metadata_main_container_path(caskroom_path: caskroom_path).join(cask_version) end - def metadata_timestamped_path(version: self.version, timestamp: :latest, create: false) + def metadata_timestamped_path(version: self.version, timestamp: :latest, create: false, + caskroom_path: self.caskroom_path) raise CaskError, "Cannot create metadata path when timestamp is :latest." if create && timestamp == :latest path = if timestamp == :latest - Pathname.glob(metadata_versioned_path(version: version).join("*")).max + Pathname.glob(metadata_versioned_path(version: version, caskroom_path: caskroom_path).join("*")).max else timestamp = new_timestamp if timestamp == :now - metadata_versioned_path(version: version).join(timestamp) + metadata_versioned_path(version: version, caskroom_path: caskroom_path).join(timestamp) end if create && !path.directory? @@ -39,11 +40,13 @@ module Cask path end - def metadata_subdir(leaf, version: self.version, timestamp: :latest, create: false) + def metadata_subdir(leaf, version: self.version, timestamp: :latest, create: false, + caskroom_path: self.caskroom_path) raise CaskError, "Cannot create metadata subdir when timestamp is :latest." if create && timestamp == :latest raise CaskError, "Cannot create metadata subdir for empty leaf." if !leaf.respond_to?(:empty?) || leaf.empty? - parent = metadata_timestamped_path(version: version, timestamp: timestamp, create: create) + parent = metadata_timestamped_path(version: version, timestamp: timestamp, create: create, + caskroom_path: caskroom_path) return if parent.nil? diff --git a/Library/Homebrew/cask/migrator.rb b/Library/Homebrew/cask/migrator.rb new file mode 100644 index 0000000000..d480b18621 --- /dev/null +++ b/Library/Homebrew/cask/migrator.rb @@ -0,0 +1,86 @@ +# typed: true +# frozen_string_literal: true + +require "cask/cask_loader" +require "utils/inreplace" + +module Cask + class Migrator + extend ::Utils::Inreplace + + attr_reader :old_cask, :new_cask + + sig { params(old_cask: Cask, new_cask: Cask).void } + def initialize(old_cask, new_cask) + raise CaskNotInstalledError, new_cask unless new_cask.installed? + + @old_cask = old_cask + @new_cask = new_cask + end + + sig { params(new_cask: Cask, dry_run: T::Boolean).void } + def self.migrate_if_needed(new_cask, dry_run: false) + old_tokens = new_cask.old_tokens + return if old_tokens.empty? + + return unless (installed_caskfile = new_cask.installed_caskfile) + + installed_token = installed_caskfile.relative_path_from(Caskroom.path).basename.to_s + return if new_cask.token == installed_token + + old_cask = CaskLoader.load(installed_caskfile) + migrator = new(old_cask, new_cask) + migrator.migrate(dry_run: dry_run) + end + + sig { params(dry_run: T::Boolean).void } + def migrate(dry_run: false) + old_token = old_cask.token + new_token = new_cask.token + + old_caskroom_path = old_cask.caskroom_path + new_caskroom_path = new_cask.caskroom_path + + old_installed_caskfile = old_cask.installed_caskfile.relative_path_from(old_caskroom_path) + new_installed_caskfile = old_installed_caskfile.dirname/old_installed_caskfile.basename.sub( + old_token, + new_token, + ) + + if dry_run + oh1 "Would migrate cask #{Formatter.identifier(old_token)} to #{Formatter.identifier(new_token)}" + + puts "cp -r #{old_caskroom_path} #{new_caskroom_path}" + puts "mv #{new_caskroom_path}/#{old_installed_caskfile} #{new_caskroom_path}/#{new_installed_caskfile}" + puts "rm -r #{old_caskroom_path}" + puts "ln -s #{new_caskroom_path.basename} #{old_caskroom_path}" + else + oh1 "Migrating cask #{Formatter.identifier(old_token)} to #{Formatter.identifier(new_token)}" + + begin + FileUtils.cp_r old_caskroom_path, new_caskroom_path + FileUtils.mv new_caskroom_path/old_installed_caskfile, new_caskroom_path/new_installed_caskfile + self.class.replace_caskfile_token(new_caskroom_path/new_installed_caskfile, old_token, new_token) + rescue => e + FileUtils.rm_rf new_caskroom_path + raise e + end + + FileUtils.rm_r old_caskroom_path + FileUtils.ln_s new_caskroom_path.basename, old_caskroom_path + end + end + + sig { params(path: Pathname, old_token: String, new_token: String).void } + def self.replace_caskfile_token(path, old_token, new_token) + case path.extname + when ".rb" + inreplace path, /\A\s*cask\s+"#{Regexp.escape(old_token)}"/, "cask #{new_token.inspect}" + when ".json" + json = JSON.parse(path.read) + json["token"] = new_token + path.atomic_write json.to_json + end + end + end +end diff --git a/Library/Homebrew/cask/uninstall.rb b/Library/Homebrew/cask/uninstall.rb index 9f28b3f14c..fb38219e2c 100644 --- a/Library/Homebrew/cask/uninstall.rb +++ b/Library/Homebrew/cask/uninstall.rb @@ -13,13 +13,6 @@ module Cask raise CaskNotInstalledError, cask if !cask.installed? && !force Installer.new(cask, binaries: binaries, force: force, verbose: verbose).uninstall - - next if (versions = cask.versions).empty? - - puts <<~EOS - #{cask} #{versions.to_sentence} #{(versions.count == 1) ? "is" : "are"} still installed. - Remove #{(versions.count == 1) ? "it" : "them all"} with `brew uninstall --cask --force #{cask}`. - EOS end end end diff --git a/Library/Homebrew/cask/upgrade.rb b/Library/Homebrew/cask/upgrade.rb index 0e5e1870c1..a501908413 100644 --- a/Library/Homebrew/cask/upgrade.rb +++ b/Library/Homebrew/cask/upgrade.rb @@ -95,8 +95,7 @@ module Cask caught_exceptions = [] upgradable_casks = outdated_casks.map do |c| - if !c.installed_caskfile.exist? && c.tap.to_s == "homebrew/cask" && - Homebrew::API::Cask.all_casks.key?(c.token) + if !c.installed? && c.tap.to_s == "homebrew/cask" && Homebrew::API::Cask.all_casks.key?(c.token) odie <<~EOS The cask '#{c.token}' was affected by a bug and cannot be upgraded as-is. To fix this, run: brew reinstall --cask --force #{c.token} @@ -192,7 +191,7 @@ module Cask new_cask_installer.install_artifacts(predecessor: old_cask) new_artifacts_installed = true - # If successful, wipe the old cask from staging + # If successful, wipe the old cask from staging. old_cask_installer.finalize_upgrade rescue => e new_cask_installer.uninstall_artifacts(successor: old_cask) if new_artifacts_installed diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index 80a79f7abf..f0f16d08b9 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -155,7 +155,7 @@ module Homebrew return false if cask.blank? return true unless basename.to_s.match?(/\A#{Regexp.escape(name)}--#{Regexp.escape(cask.version)}\b/) - return true if scrub && cask.versions.exclude?(cask.version) + return true if scrub && cask.installed_version != cask.version if cask.version.latest? cleanup_threshold = (DateTime.now - CLEANUP_DEFAULT_DAYS).to_time diff --git a/Library/Homebrew/cli/named_args.rb b/Library/Homebrew/cli/named_args.rb index 1e6a8167cb..078e9cfd80 100644 --- a/Library/Homebrew/cli/named_args.rb +++ b/Library/Homebrew/cli/named_args.rb @@ -47,12 +47,14 @@ module Homebrew ignore_unavailable: T.nilable(T::Boolean), method: T.nilable(Symbol), uniq: T::Boolean, + warn: T::Boolean, ).returns(T::Array[T.any(Formula, Keg, Cask::Cask)]) } - def to_formulae_and_casks(only: parent&.only_formula_or_cask, ignore_unavailable: nil, method: nil, uniq: true) + def to_formulae_and_casks(only: parent&.only_formula_or_cask, ignore_unavailable: nil, method: nil, uniq: true, + warn: true) @to_formulae_and_casks ||= {} @to_formulae_and_casks[only] ||= downcased_unique_named.flat_map do |name| - load_formula_or_cask(name, only: only, method: method) + load_formula_or_cask(name, only: only, method: method, warn: warn) rescue FormulaUnreadableError, FormulaClassUnavailableError, TapFormulaUnreadableError, TapFormulaClassUnavailableError, Cask::CaskUnreadableError @@ -86,14 +88,14 @@ module Homebrew end.uniq.freeze end - def load_formula_or_cask(name, only: nil, method: nil) + def load_formula_or_cask(name, only: nil, method: nil, warn: true) unreadable_error = nil if only != :cask begin formula = case method when nil, :factory - Formulary.factory(name, *spec, force_bottle: @force_bottle, flags: @flags) + Formulary.factory(name, *spec, force_bottle: @force_bottle, flags: @flags, warn: warn) when :resolve resolve_formula(name) when :latest_kegs @@ -124,7 +126,7 @@ module Homebrew begin config = Cask::Config.from_args(@parent) if @cask_options - cask = Cask::CaskLoader.load(name, config: config) + cask = Cask::CaskLoader.load(name, config: config, warn: warn) if unreadable_error.present? onoe <<~EOS @@ -145,7 +147,7 @@ module Homebrew # If we're trying to get a keg-like Cask, do our best to handle it # not being readable and return something that can be used. if want_keg_like_cask - cask_version = Cask::Cask.new(name, config: config).versions.first + cask_version = Cask::Cask.new(name, config: config).installed_version cask = Cask::Cask.new(name, config: config) do version cask_version if cask_version end diff --git a/Library/Homebrew/cmd/migrate.rb b/Library/Homebrew/cmd/migrate.rb index f753bc81b1..0552ea5e0b 100644 --- a/Library/Homebrew/cmd/migrate.rb +++ b/Library/Homebrew/cmd/migrate.rb @@ -3,12 +3,11 @@ require "migrator" require "cli/parser" +require "cask/migrator" module Homebrew - module_function - sig { returns(CLI::Parser) } - def migrate_args + def self.migrate_args Homebrew::CLI::Parser.new do description <<~EOS Migrate renamed packages to new names, where are old names of @@ -19,17 +18,27 @@ module Homebrew "the same taps and migrate them anyway." switch "-n", "--dry-run", description: "Show what would be migrated, but do not actually migrate anything." + switch "--formula", "--formulae", + description: "Only migrate formulae." + switch "--cask", "--casks", + description: "Only migrate casks." - named_args :installed_formula, min: 1 + conflicts "--formula", "--cask" + + named_args [:installed_formula, :installed_cask], min: 1 end end - def migrate + def self.migrate args = migrate_args.parse - args.named.to_kegs.each do |keg| - f = Formulary.from_keg(keg) - Migrator.migrate_if_needed(f, force: args.force?, dry_run: args.dry_run?) + args.named.to_formulae_and_casks(warn: false).each do |formula_or_cask| + case formula_or_cask + when Formula + Migrator.migrate_if_needed(formula_or_cask, force: args.force?, dry_run: args.dry_run?) + when Cask::Cask + Cask::Migrator.migrate_if_needed(formula_or_cask, dry_run: args.dry_run?) + end end end end diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index ee5be24c5c..ff5ab155bf 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -3,6 +3,7 @@ require "migrator" require "formulary" +require "cask/cask_loader" require "descriptions" require "cleanup" require "description_cache_store" @@ -246,6 +247,7 @@ module Homebrew hub.dump(auto_update: args.auto_update?) unless args.quiet? hub.reporters.each(&:migrate_tap_migration) + hub.reporters.each(&:migrate_cask_rename) hub.reporters.each { |r| r.migrate_formula_rename(force: args.force?, verbose: args.verbose?) } CacheStoreDatabase.use(:descriptions) do |db| @@ -390,6 +392,14 @@ class Reporter when "M" # Report updated casks @report[:MC] << tap.formula_file_to_name(src) + when /^R\d{0,3}/ + src_full_name = tap.formula_file_to_name(src) + dst_full_name = tap.formula_file_to_name(dst) + # Don't report formulae that are moved within a tap but not renamed + next if src_full_name == dst_full_name + + @report[:DC] << src_full_name + @report[:AC] << dst_full_name end end @@ -416,6 +426,41 @@ class Reporter end end + renamed_casks = Set.new + @report[:DC].each do |old_full_name| + old_name = old_full_name.split("/").last + new_name = tap.cask_renames[old_name] + next unless new_name + + new_full_name = if tap.name == "homebrew/cask" + new_name + else + "#{tap}/#{new_name}" + end + + renamed_casks << [old_full_name, new_full_name] if @report[:AC].include?(new_full_name) + end + + @report[:AC].each do |new_full_name| + new_name = new_full_name.split("/").last + old_name = tap.cask_renames.key(new_name) + next unless old_name + + old_full_name = if tap.name == "homebrew/cask" + old_name + else + "#{tap}/#{old_name}" + end + + renamed_casks << [old_full_name, new_full_name] + end + + if renamed_casks.any? + @report[:AC] -= renamed_casks.map(&:last) + @report[:DC] -= renamed_casks.map(&:first) + @report[:RC] = renamed_casks.to_a + end + renamed_formulae = Set.new @report[:D].each do |old_full_name| old_name = old_full_name.split("/").last @@ -445,7 +490,7 @@ class Reporter renamed_formulae << [old_full_name, new_full_name] end - if renamed_formulae.present? + if renamed_formulae.any? @report[:A] -= renamed_formulae.map(&:last) @report[:D] -= renamed_formulae.map(&:first) @report[:R] = renamed_formulae.to_a @@ -541,6 +586,12 @@ class Reporter end end + def migrate_cask_rename + Cask::Caskroom.casks.each do |cask| + Cask::Migrator.migrate_if_needed(cask) + end + end + def migrate_formula_rename(force:, verbose:) Formula.installed.each do |formula| next unless Migrator.needs_migration?(formula) @@ -631,6 +682,7 @@ class ReporterHub dump_new_formula_report dump_new_cask_report dump_renamed_formula_report if report_all + dump_renamed_cask_report if report_all dump_deleted_formula_report(report_all) dump_deleted_cask_report(report_all) @@ -723,6 +775,16 @@ class ReporterHub output_dump_formula_or_cask_report "Renamed Formulae", formulae end + def dump_renamed_cask_report + casks = select_formula_or_cask(:RC).sort.map do |name, new_name| + name = pretty_installed(name) if installed?(name) + new_name = pretty_installed(new_name) if installed?(new_name) + "#{name} -> #{new_name}" + end + + output_dump_formula_or_cask_report "Renamed Casks", casks + end + def dump_deleted_formula_report(report_all) formulae = select_formula_or_cask(:D).sort.map do |name| if installed?(name) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index f61f106a62..ff080d2499 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -502,7 +502,8 @@ class Formula # Old names for the formula. def oldnames @oldnames ||= if tap - tap.formula_renames.select { |_, oldname| oldname == name }.keys + tap.formula_renames + .flat_map { |old_name, new_name| (new_name == name) ? old_name : [] } else [] end diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index d3514a050c..8f8fb9d54a 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -523,8 +523,7 @@ module Formulary # Loads tapped formulae. class TapLoader < FormulaLoader - def initialize(tapped_name, from: nil) - warn = [:keg, :rack].exclude?(from) + def initialize(tapped_name, from: nil, warn: true) name, path, tap = formula_name_path(tapped_name, warn: warn) super name, path, tap: tap end @@ -555,7 +554,7 @@ module Formulary new_name = new_tap.core_tap? ? name : new_tapped_name end - opoo "Use #{new_name} instead of deprecated #{old_name}" if warn && old_name && new_name + opoo "Formula #{old_name} was renamed to #{new_name}." if warn && old_name && new_name end [name, path, tap] @@ -649,7 +648,7 @@ module Formulary # * a formula URL # * a local bottle reference def self.factory( - ref, spec = :stable, alias_path: nil, from: nil, + ref, spec = :stable, alias_path: nil, from: nil, warn: true, force_bottle: false, flags: [], ignore_errors: false ) raise ArgumentError, "Formulae must have a ref!" unless ref @@ -660,7 +659,7 @@ module Formulary return cache[:formulary_factory][cache_key] end - formula = loader_for(ref, from: from).get_formula(spec, alias_path: alias_path, + formula = loader_for(ref, from: from, warn: warn).get_formula(spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags, ignore_errors: ignore_errors) if factory_cached? @@ -683,7 +682,7 @@ module Formulary if keg from_keg(keg, spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags) else - factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack, + factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack, warn: false, force_bottle: force_bottle, flags: flags) end end @@ -704,15 +703,15 @@ module Formulary spec ||= tab.spec f = if tap.nil? - factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg, + factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg, warn: false, force_bottle: force_bottle, flags: flags) else begin - factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg, + factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg, warn: false, force_bottle: force_bottle, flags: flags) rescue FormulaUnavailableError # formula may be migrated to different tap. Try to search in core and all taps. - factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg, + factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg, warn: false, force_bottle: force_bottle, flags: flags) end end @@ -757,7 +756,7 @@ module Formulary loader_for(ref).path end - def self.loader_for(ref, from: nil) + def self.loader_for(ref, from: nil, warn: true) case ref when HOMEBREW_BOTTLES_EXTNAME_REGEX return BottleLoader.new(ref) @@ -770,7 +769,7 @@ module Formulary return AliasAPILoader.new(name) if Homebrew::API::Formula.all_aliases.key?(name) end - return TapLoader.new(ref, from: from) + return TapLoader.new(ref, from: from, warn: warn) end pathname_ref = Pathname.new(ref) @@ -800,7 +799,9 @@ module Formulary return FormulaLoader.new(name, path) end - return TapLoader.new("#{CoreTap.instance}/#{ref}", from: from) if CoreTap.instance.formula_renames.key?(ref) + if CoreTap.instance.formula_renames.key?(ref) + return TapLoader.new("#{CoreTap.instance}/#{ref}", from: from, warn: warn) + end possible_taps = Tap.select { |tap| tap.formula_renames.key?(ref) } @@ -809,7 +810,7 @@ module Formulary raise TapFormulaWithOldnameAmbiguityError.new(ref, possible_tap_newname_formulae) end - return TapLoader.new("#{possible_taps.first}/#{ref}", from: from) unless possible_taps.empty? + return TapLoader.new("#{possible_taps.first}/#{ref}", from: from, warn: warn) unless possible_taps.empty? possible_keg_formula = Pathname.new("#{HOMEBREW_PREFIX}/opt/#{ref}/.brew/#{ref}.rb") return FormulaLoader.new(ref, possible_keg_formula) if possible_keg_formula.file? diff --git a/Library/Homebrew/migrator.rb b/Library/Homebrew/migrator.rb index 54f0d4d0ac..507ad794c4 100644 --- a/Library/Homebrew/migrator.rb +++ b/Library/Homebrew/migrator.rb @@ -106,15 +106,14 @@ class Migrator def self.migrate_if_needed(formula, force:, dry_run: false) oldnames = Migrator.oldnames_needing_migration(formula) - return if oldnames.empty? begin - if dry_run - ohai "Would migrate #{oldnames.to_sentence} to #{formula.name}" - return - end - oldnames.each do |oldname| + if dry_run + oh1 "Would migrate formula #{Formatter.identifier(oldname)} to #{Formatter.identifier(formula.name)}" + next + end + migrator = Migrator.new(formula, oldname, force: force) migrator.migrate end @@ -205,7 +204,7 @@ class Migrator end def migrate - oh1 "Processing #{Formatter.identifier(oldname)} formula rename to #{Formatter.identifier(newname)}" + oh1 "Migrating formula #{Formatter.identifier(oldname)} to #{Formatter.identifier(newname)}" lock unlink_oldname unlink_newname if new_cellar.exist? diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index 1e79ac8890..1b49d72bd2 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -18,6 +18,7 @@ class Tap TAP_DIRECTORY = (HOMEBREW_LIBRARY/"Taps").freeze + HOMEBREW_TAP_CASK_RENAMES_FILE = "cask_renames.json" HOMEBREW_TAP_FORMULA_RENAMES_FILE = "formula_renames.json" HOMEBREW_TAP_MIGRATIONS_FILE = "tap_migrations.json" HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR = "audit_exceptions" @@ -26,6 +27,7 @@ class Tap HOMEBREW_TAP_JSON_FILES = %W[ #{HOMEBREW_TAP_FORMULA_RENAMES_FILE} + #{HOMEBREW_TAP_CASK_RENAMES_FILE} #{HOMEBREW_TAP_MIGRATIONS_FILE} #{HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR}/*.json #{HOMEBREW_TAP_STYLE_EXCEPTIONS_DIR}/*.json @@ -687,8 +689,20 @@ class Tap hash end + # Hash with tap cask renames. + sig { returns(T::Hash[String, String]) } + def cask_renames + @cask_renames ||= if name == "homebrew/cask" && !Homebrew::EnvConfig.no_install_from_api? + Homebrew::API::Cask.all_renames + elsif (rename_file = path/HOMEBREW_TAP_CASK_RENAMES_FILE).file? + JSON.parse(rename_file.read) + else + {} + end + end + # Hash with tap formula renames. - sig { returns(Hash) } + sig { returns(T::Hash[String, String]) } def formula_renames @formula_renames ||= if (rename_file = path/HOMEBREW_TAP_FORMULA_RENAMES_FILE).file? JSON.parse(rename_file.read) @@ -931,7 +945,7 @@ class CoreTap < Tap end # @private - sig { returns(Hash) } + sig { returns(T::Hash[String, String]) } def formula_renames @formula_renames ||= if Homebrew::EnvConfig.no_install_from_api? self.class.ensure_installed! diff --git a/Library/Homebrew/test/cask/cask_spec.rb b/Library/Homebrew/test/cask/cask_spec.rb index 4e465b1156..7cc49f37a9 100644 --- a/Library/Homebrew/test/cask/cask_spec.rb +++ b/Library/Homebrew/test/cask/cask_spec.rb @@ -4,7 +4,7 @@ describe Cask::Cask, :cask do let(:cask) { described_class.new("versioned-cask") } context "when multiple versions are installed" do - describe "#versions" do + describe "#installed_version" do context "when there are duplicate versions" do it "uses the last unique version" do allow(cask).to receive(:timestamped_versions).and_return([ @@ -13,11 +13,11 @@ describe Cask::Cask, :cask do ["1.2.2", "1001"], ]) + # Installed caskfile must exist to count as installed. + allow_any_instance_of(Pathname).to receive(:exist?).and_return(true) + expect(cask).to receive(:timestamped_versions) - expect(cask.versions).to eq([ - "1.2.3", - "1.2.2", - ]) + expect(cask.installed_version).to eq("1.2.2") end end end @@ -104,10 +104,10 @@ describe Cask::Cask, :cask do let(:cask) { described_class.new("basic-cask") } shared_examples "versioned casks" do |tap_version, expectations| - expectations.each do |installed_versions, expected_output| - context "when versions #{installed_versions.inspect} are installed and the tap version is #{tap_version}" do + expectations.each do |installed_version, expected_output| + context "when version #{installed_version.inspect} is installed and the tap version is #{tap_version}" do it { - allow(cask).to receive(:versions).and_return(installed_versions) + allow(cask).to receive(:installed_version).and_return(installed_version) allow(cask).to receive(:version).and_return(Cask::DSL::Version.new(tap_version)) expect(cask).to receive(:outdated_versions).and_call_original expect(subject).to eq expected_output @@ -118,16 +118,13 @@ describe Cask::Cask, :cask do describe "installed version is equal to tap version => not outdated" do include_examples "versioned casks", "1.2.3", - ["1.2.3"] => [], - ["1.2.4", "1.2.3"] => [] + "1.2.3" => [] end describe "installed version is different than tap version => outdated" do include_examples "versioned casks", "1.2.4", - ["1.2.3"] => ["1.2.3"], - ["1.2.4", "1.2.3"] => ["1.2.3"], - ["1.2.2", "1.2.3"] => ["1.2.2", "1.2.3"], - ["1.2.2", "1.2.4", "1.2.3"] => ["1.2.2", "1.2.3"] + "1.2.3" => ["1.2.3"], + "1.2.4" => [] end end @@ -142,7 +139,7 @@ describe Cask::Cask, :cask do subject { cask.outdated_versions(greedy: greedy) } it { - allow(cask).to receive(:versions).and_return(installed_version) + allow(cask).to receive(:installed_version).and_return(installed_version) allow(cask).to receive(:version).and_return(Cask::DSL::Version.new(tap_version)) allow(cask).to receive(:outdated_download_sha?).and_return(outdated_sha) expect(cask).to receive(:outdated_versions).and_call_original @@ -154,29 +151,29 @@ describe Cask::Cask, :cask do describe ":latest version installed, :latest version in tap" do include_examples ":latest cask", false, false, "latest", - ["latest"] => [] + "latest" => [] include_examples ":latest cask", true, false, "latest", - ["latest"] => [] + "latest" => [] include_examples ":latest cask", true, true, "latest", - ["latest"] => ["latest"] + "latest" => ["latest"] end describe "numbered version installed, :latest version in tap" do include_examples ":latest cask", false, false, "latest", - ["1.2.3"] => [] + "1.2.3" => [] include_examples ":latest cask", true, false, "latest", - ["1.2.3"] => [] + "1.2.3" => [] include_examples ":latest cask", true, true, "latest", - ["1.2.3"] => ["1.2.3"] + "1.2.3" => ["1.2.3"] end describe "latest version installed, numbered version in tap" do include_examples ":latest cask", false, false, "1.2.3", - ["latest"] => ["latest"] + "latest" => ["latest"] include_examples ":latest cask", true, false, "1.2.3", - ["latest"] => ["latest"] + "latest" => ["latest"] include_examples ":latest cask", true, true, "1.2.3", - ["latest"] => ["latest"] + "latest" => ["latest"] end end end diff --git a/Library/Homebrew/test/cask/installer_spec.rb b/Library/Homebrew/test/cask/installer_spec.rb index 1bfe81b09b..983c4de983 100644 --- a/Library/Homebrew/test/cask/installer_spec.rb +++ b/Library/Homebrew/test/cask/installer_spec.rb @@ -313,7 +313,7 @@ describe Cask::Installer, :cask do caffeine = Cask::CaskLoader.load(path) expect(caffeine).to receive(:loaded_from_api?).twice.and_return(true) expect(caffeine).to receive(:caskfile_only?).twice.and_return(true) - expect(caffeine).to receive(:installed_caskfile).once.and_return(invalid_path) + expect(caffeine).to receive(:installed_caskfile).twice.and_return(invalid_path) described_class.new(caffeine).install expect(Cask::CaskLoader.load(path)).to be_installed diff --git a/Library/Homebrew/test/cask/uninstall_spec.rb b/Library/Homebrew/test/cask/uninstall_spec.rb index d0df5c0e7e..61d0553e3d 100644 --- a/Library/Homebrew/test/cask/uninstall_spec.rb +++ b/Library/Homebrew/test/cask/uninstall_spec.rb @@ -112,14 +112,6 @@ describe Cask::Uninstall, :cask do expect(caskroom_path.join(first_installed_version)).not_to exist expect(caskroom_path).not_to exist end - - it "displays a message when versions remain installed" do - expect do - expect do - described_class.uninstall_casks(Cask::Cask.new("versioned-cask")) - end.not_to output.to_stderr - end.to output(/#{token} #{first_installed_version} is still installed./).to_stdout - end end context "when Casks in Taps have been renamed or removed" do diff --git a/Library/Homebrew/test/cask/upgrade_spec.rb b/Library/Homebrew/test/cask/upgrade_spec.rb index d9c9471256..d29482eb73 100644 --- a/Library/Homebrew/test/cask/upgrade_spec.rb +++ b/Library/Homebrew/test/cask/upgrade_spec.rb @@ -3,8 +3,12 @@ require "cask/upgrade" describe Cask::Upgrade, :cask do - let(:version_latest_path_second) { version_latest.config.appdir.join("Caffeine Pro.app") } - let(:version_latest_path_first) { version_latest.config.appdir.join("Caffeine Mini.app") } + let(:version_latest_paths) do + [ + version_latest.config.appdir.join("Caffeine Mini.app"), + version_latest.config.appdir.join("Caffeine Pro.app"), + ] + end let(:version_latest) { Cask::CaskLoader.load("version-latest") } let(:auto_updates_path) { auto_updates.config.appdir.join("MyFancyApp.app") } let(:auto_updates) { Cask::CaskLoader.load("auto-updates") } @@ -38,81 +42,81 @@ describe Cask::Upgrade, :cask do it "updates all the installed Casks when no token is provided" do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" described_class.upgrade_casks(args: args) expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.3" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.61") + expect(local_transmission.installed_version).to eq "2.61" expect(renamed_app).to be_installed expect(renamed_app_old_path).not_to be_a_directory expect(renamed_app_new_path).to be_a_directory - expect(renamed_app.versions).to include("2.0.0") + expect(renamed_app.installed_version).to eq "2.0.0" end it "updates only the Casks specified in the command line" do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" described_class.upgrade_casks(local_caffeine, args: args) expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.3" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" end it 'updates "auto_updates" and "latest" Casks when their tokens are provided in the command line' do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") + expect(auto_updates.installed_version).to eq "2.57" described_class.upgrade_casks(local_caffeine, auto_updates, args: args) expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.3" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.61") + expect(auto_updates.installed_version).to eq "2.61" end end @@ -120,24 +124,24 @@ describe Cask::Upgrade, :cask do it 'includes the Casks with "auto_updates true" or "version latest"' do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") + expect(auto_updates.installed_version).to eq "2.57" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" expect(version_latest).to be_installed - expect(version_latest_path_first).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" # Change download sha so that :latest cask decides to update itself version_latest.download_sha_path.write("fake download sha") expect(version_latest.outdated_download_sha?).to be(true) @@ -146,50 +150,49 @@ describe Cask::Upgrade, :cask do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.3" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.61") + expect(auto_updates.installed_version).to eq "2.61" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.61") + expect(local_transmission.installed_version).to eq "2.61" expect(renamed_app).to be_installed expect(renamed_app_old_path).not_to be_a_directory expect(renamed_app_new_path).to be_a_directory - expect(renamed_app.versions).to include("2.0.0") + expect(renamed_app.installed_version).to eq "2.0.0" expect(version_latest).to be_installed - expect(version_latest_path_second).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" expect(version_latest.outdated_download_sha?).to be(false) end it 'does not include the Casks with "auto_updates true" or "version latest" when the version did not change' do expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") + expect(auto_updates.installed_version).to eq "2.57" described_class.upgrade_casks(auto_updates, greedy: true, args: args) expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.61") + expect(auto_updates.installed_version).to eq "2.61" described_class.upgrade_casks(auto_updates, greedy: true, args: args) expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.61") + expect(auto_updates.installed_version).to eq "2.61" end it 'does not include the Casks with "version latest" when the version did not change' do expect(version_latest).to be_installed - expect(version_latest_path_first).to be_a_directory - expect(version_latest_path_second).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" # Change download sha so that :latest cask decides to update itself version_latest.download_sha_path.write("fake download sha") expect(version_latest.outdated_download_sha?).to be(true) @@ -197,17 +200,15 @@ describe Cask::Upgrade, :cask do described_class.upgrade_casks(version_latest, greedy: true, args: args) expect(version_latest).to be_installed - expect(version_latest_path_first).to be_a_directory - expect(version_latest_path_second).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" expect(version_latest.outdated_download_sha?).to be(false) described_class.upgrade_casks(version_latest, greedy: true, args: args) expect(version_latest).to be_installed - expect(version_latest_path_first).to be_a_directory - expect(version_latest_path_second).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" expect(version_latest.outdated_download_sha?).to be(false) end end @@ -236,34 +237,31 @@ describe Cask::Upgrade, :cask do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" described_class.upgrade_casks(dry_run: true, args: args) expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") - expect(local_caffeine.versions).not_to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") - expect(local_transmission.versions).not_to include("2.61") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") - expect(renamed_app.versions).not_to include("2.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" end it "would update only the Casks specified in the command line" do @@ -271,23 +269,21 @@ describe Cask::Upgrade, :cask do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" described_class.upgrade_casks(local_caffeine, dry_run: true, args: args) expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") - expect(local_caffeine.versions).not_to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") - expect(local_transmission.versions).not_to include("2.61") + expect(local_transmission.installed_version).to eq "2.60" end it 'would update "auto_updates" and "latest" Casks when their tokens are provided in the command line' do @@ -295,34 +291,31 @@ describe Cask::Upgrade, :cask do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") + expect(auto_updates.installed_version).to eq "2.57" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" described_class.upgrade_casks(local_caffeine, auto_updates, dry_run: true, args: args) expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") - expect(local_caffeine.versions).not_to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") - expect(auto_updates.versions).not_to include("2.61") + expect(auto_updates.installed_version).to eq "2.57" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") - expect(renamed_app.versions).not_to include("2.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" end end @@ -332,20 +325,20 @@ describe Cask::Upgrade, :cask do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") + expect(auto_updates.installed_version).to eq "2.57" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" expect(version_latest).to be_installed # Change download sha so that :latest cask decides to update itself @@ -356,24 +349,20 @@ describe Cask::Upgrade, :cask do expect(local_caffeine).to be_installed expect(local_caffeine_path).to be_a_directory - expect(local_caffeine.versions).to include("1.2.2") - expect(local_caffeine.versions).not_to include("1.2.3") + expect(local_caffeine.installed_version).to eq "1.2.2" expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") - expect(auto_updates.versions).not_to include("2.61") + expect(auto_updates.installed_version).to eq "2.57" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") - expect(local_transmission.versions).not_to include("2.61") + expect(local_transmission.installed_version).to eq "2.60" expect(renamed_app).to be_installed expect(renamed_app_old_path).to be_a_directory expect(renamed_app_new_path).not_to be_a_directory - expect(renamed_app.versions).to include("1.0.0") - expect(renamed_app.versions).not_to include("2.0.0") + expect(renamed_app.installed_version).to eq "1.0.0" expect(version_latest).to be_installed expect(version_latest.outdated_download_sha?).to be(true) @@ -384,23 +373,21 @@ describe Cask::Upgrade, :cask do expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") + expect(auto_updates.installed_version).to eq "2.57" described_class.upgrade_casks(auto_updates, dry_run: true, greedy: true, args: args) expect(auto_updates).to be_installed expect(auto_updates_path).to be_a_directory - expect(auto_updates.versions).to include("2.57") - expect(auto_updates.versions).not_to include("2.61") + expect(auto_updates.installed_version).to eq "2.57" end it 'would update outdated Casks with "version latest"' do expect(described_class).not_to receive(:upgrade_cask) expect(version_latest).to be_installed - expect(version_latest_path_first).to be_a_directory - expect(version_latest_path_second).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" # Change download sha so that :latest cask decides to update itself version_latest.download_sha_path.write("fake download sha") expect(version_latest.outdated_download_sha?).to be(true) @@ -408,9 +395,8 @@ describe Cask::Upgrade, :cask do described_class.upgrade_casks(version_latest, dry_run: true, greedy: true, args: args) expect(version_latest).to be_installed - expect(version_latest_path_first).to be_a_directory - expect(version_latest_path_second).to be_a_directory - expect(version_latest.versions).to include("latest") + expect(version_latest_paths).to all be_a_directory + expect(version_latest.installed_version).to eq "latest" expect(version_latest.outdated_download_sha?).to be(true) end end @@ -440,7 +426,7 @@ describe Cask::Upgrade, :cask do expect(will_fail_if_upgraded).to be_installed expect(will_fail_if_upgraded_path).to be_a_file - expect(will_fail_if_upgraded.versions).to include("1.2.2") + expect(will_fail_if_upgraded.installed_version).to eq "1.2.2" expect do described_class.upgrade_casks(will_fail_if_upgraded, args: args) @@ -448,7 +434,7 @@ describe Cask::Upgrade, :cask do expect(will_fail_if_upgraded).to be_installed expect(will_fail_if_upgraded_path).to be_a_file - expect(will_fail_if_upgraded.versions).to include("1.2.2") + expect(will_fail_if_upgraded.installed_version).to eq "1.2.2" expect(will_fail_if_upgraded.staged_path).not_to exist end @@ -458,7 +444,7 @@ describe Cask::Upgrade, :cask do expect(bad_checksum).to be_installed expect(bad_checksum_path).to be_a_directory - expect(bad_checksum.versions).to include("1.2.2") + expect(bad_checksum.installed_version).to eq "1.2.2" expect do described_class.upgrade_casks(bad_checksum, args: args) @@ -466,7 +452,7 @@ describe Cask::Upgrade, :cask do expect(bad_checksum).to be_installed expect(bad_checksum_path).to be_a_directory - expect(bad_checksum.versions).to include("1.2.2") + expect(bad_checksum.installed_version).to eq "1.2.2" expect(bad_checksum.staged_path).not_to exist end end @@ -495,15 +481,15 @@ describe Cask::Upgrade, :cask do expect(bad_checksum).to be_installed expect(bad_checksum_path).to be_a_directory - expect(bad_checksum.versions).to include("1.2.2") + expect(bad_checksum.installed_version).to eq "1.2.2" expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.60") + expect(local_transmission.installed_version).to eq "2.60" expect(bad_checksum_2).to be_installed expect(bad_checksum_2_path).to be_a_file - expect(bad_checksum_2.versions).to include("1.2.2") + expect(bad_checksum_2.installed_version).to eq "1.2.2" expect do described_class.upgrade_casks(args: args) @@ -511,16 +497,16 @@ describe Cask::Upgrade, :cask do expect(bad_checksum).to be_installed expect(bad_checksum_path).to be_a_directory - expect(bad_checksum.versions).to include("1.2.2") + expect(bad_checksum.installed_version).to eq "1.2.2" expect(bad_checksum.staged_path).not_to exist expect(local_transmission).to be_installed expect(local_transmission_path).to be_a_directory - expect(local_transmission.versions).to include("2.61") + expect(local_transmission.installed_version).to eq "2.61" expect(bad_checksum_2).to be_installed expect(bad_checksum_2_path).to be_a_file - expect(bad_checksum_2.versions).to include("1.2.2") + expect(bad_checksum_2.installed_version).to eq "1.2.2" expect(bad_checksum_2.staged_path).not_to exist end end diff --git a/Library/Homebrew/test/cli/named_args_spec.rb b/Library/Homebrew/test/cli/named_args_spec.rb index bf1b2729b6..7ed7836ae6 100644 --- a/Library/Homebrew/test/cli/named_args_spec.rb +++ b/Library/Homebrew/test/cli/named_args_spec.rb @@ -4,17 +4,17 @@ require "cli/named_args" def setup_unredable_formula(name) error = FormulaUnreadableError.new(name, RuntimeError.new("testing")) - allow(Formulary).to receive(:factory).with(name, force_bottle: false, flags: []).and_raise(error) + allow(Formulary).to receive(:factory).with(name, force_bottle: false, flags: [], warn: true).and_raise(error) end def setup_unredable_cask(name) error = Cask::CaskUnreadableError.new(name, "testing") allow(Cask::CaskLoader).to receive(:load).with(name).and_raise(error) - allow(Cask::CaskLoader).to receive(:load).with(name, config: nil).and_raise(error) + allow(Cask::CaskLoader).to receive(:load).with(name, config: nil, warn: true).and_raise(error) config = instance_double(Cask::Config) allow(Cask::Config).to receive(:from_args).and_return(config) - allow(Cask::CaskLoader).to receive(:load).with(name, config: config).and_raise(error) + allow(Cask::CaskLoader).to receive(:load).with(name, config: config, warn: true).and_raise(error) end describe Homebrew::CLI::NamedArgs do diff --git a/Library/Homebrew/test/cmd/migrate_spec.rb b/Library/Homebrew/test/cmd/migrate_spec.rb index 8b0b6610a6..d516ebf2f4 100644 --- a/Library/Homebrew/test/cmd/migrate_spec.rb +++ b/Library/Homebrew/test/cmd/migrate_spec.rb @@ -11,7 +11,7 @@ describe "brew migrate" do install_and_rename_coretap_formula "testball1", "testball2" expect { brew "migrate", "testball1" } - .to output(/Processing testball1 formula rename to testball2/).to_stdout + .to output(/Migrating formula testball1 to testball2/).to_stdout .and not_to_output.to_stderr .and be_a_success end diff --git a/Library/Homebrew/test/support/fixtures/cask/everything.json b/Library/Homebrew/test/support/fixtures/cask/everything.json index 8d3b86f5a6..32eae9e9bd 100644 --- a/Library/Homebrew/test/support/fixtures/cask/everything.json +++ b/Library/Homebrew/test/support/fixtures/cask/everything.json @@ -1,6 +1,9 @@ { "token": "everything", "full_token": "everything", + "old_tokens": [ + + ], "tap": "homebrew/cask", "name": [ "Everything" diff --git a/Library/Homebrew/test/support/helper/cask.rb b/Library/Homebrew/test/support/helper/cask.rb index 940e80a4f9..8f14f52649 100644 --- a/Library/Homebrew/test/support/helper/cask.rb +++ b/Library/Homebrew/test/support/helper/cask.rb @@ -9,7 +9,7 @@ module Test allow(::Cask::CaskLoader).to receive(:for).and_call_original if call_original loader = ::Cask::CaskLoader::FromInstanceLoader.new cask - allow(::Cask::CaskLoader).to receive(:for).with(ref).and_return(loader) + allow(::Cask::CaskLoader).to receive(:for).with(ref, warn: true).and_return(loader) end end end diff --git a/Library/Homebrew/test/support/helper/formula.rb b/Library/Homebrew/test/support/helper/formula.rb index caf4a1c73a..347bf511d7 100644 --- a/Library/Homebrew/test/support/helper/formula.rb +++ b/Library/Homebrew/test/support/helper/formula.rb @@ -15,8 +15,8 @@ module Test allow(Formulary).to receive(:loader_for).and_call_original if call_original loader = double(get_formula: formula) - allow(Formulary).to receive(:loader_for).with(ref, from: :keg).and_return(loader) - allow(Formulary).to receive(:loader_for).with(ref, from: nil).and_return(loader) + allow(Formulary).to receive(:loader_for).with(ref, from: :keg, warn: false).and_return(loader) + allow(Formulary).to receive(:loader_for).with(ref, from: nil, warn: true).and_return(loader) end end end