diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index def50503d5..1a0aae3a4d 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -257,6 +257,10 @@ module Homebrew "`HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS` days.", boolean: true, }, + HOMEBREW_NO_INSTALL_UPGRADE: { + description: "If set, `brew install` will not automatically upgrade installed but outdated formulae", + boolean: true, + }, HOMEBREW_PRY: { description: "If set, use Pry for the `brew irb` command.", boolean: true, diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 8b0ab569d0..128b5de2c0 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -344,30 +344,6 @@ class FormulaInstaller Homebrew::Install.perform_build_from_source_checks end - # not in initialize so upgrade can unlink the active keg before calling this - # function but after instantiating this class so that it can avoid having to - # relink the active keg if possible (because it is slow). - if formula.linked_keg.directory? - message = <<~EOS - #{formula.name} #{formula.linked_version} is already installed - EOS - if formula.outdated? && !formula.head? - message += <<~EOS - To upgrade to #{formula.pkg_version}, run: - brew upgrade #{formula.full_name} - EOS - elsif only_deps? - message = nil - else - # some other version is already installed *and* linked - message += <<~EOS - To install #{formula.pkg_version}, first run: - brew unlink #{formula.name} - EOS - end - raise CannotInstallFormulaError, message if message - end - # Warn if a more recent version of this formula is available in the tap. begin if formula.pkg_version < (v = Formulary.factory(formula.full_name, force_bottle: force_bottle?).pkg_version) diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb index 0c531ed1ca..4db75301ee 100644 --- a/Library/Homebrew/install.rb +++ b/Library/Homebrew/install.rb @@ -5,6 +5,7 @@ require "diagnostic" require "fileutils" require "hardware" require "development_tools" +require "upgrade" module Homebrew # Helper module for performing (pre-)install checks. @@ -124,6 +125,8 @@ module Homebrew # dependencies. Therefore before performing other checks we need to be # sure --force flag is passed. if f.outdated? + return true unless Homebrew::EnvConfig.no_install_upgrade? + optlinked_version = Keg.for(f.opt_prefix).version onoe <<~EOS #{f.full_name} #{optlinked_version} is already installed. @@ -266,8 +269,37 @@ module Homebrew quiet: quiet, verbose: verbose, ) + + if f.linked_keg.directory? + if Homebrew::EnvConfig.no_install_upgrade? + message = <<~EOS + #{f.name} #{f.linked_version} is already installed + EOS + message += if f.outdated? && !f.head? + <<~EOS + To upgrade to #{f.pkg_version}, run: + brew upgrade #{f.full_name} + EOS + else + <<~EOS + To install #{f.pkg_version}, first run: + brew unlink #{f.name} + EOS + end + raise CannotInstallFormulaError, message unless only_deps + elsif f.outdated? && !f.head? + puts "#{f.name} #{f.linked_version} is installed but outdated" + kegs = Upgrade.outdated_kegs(f) + linked_kegs = kegs.select(&:linked?) + Upgrade.print_upgrade_message(f, fi.options) + end + end + fi.prelude fi.fetch + + kegs.each(&:unlink) if kegs.present? + fi.install fi.finish rescue FormulaInstallationAlreadyAttemptedError @@ -276,6 +308,13 @@ module Homebrew nil rescue CannotInstallFormulaError => e ofail e.message + ensure + # Re-link kegs if upgrade fails + begin + linked_kegs.each(&:link) if linked_kegs.present? && !f.latest_version_installed? + rescue + nil + end end end end diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index 944be741a5..d13d69b850 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -8233,6 +8233,8 @@ module Homebrew::EnvConfig def self.no_install_cleanup?(); end + def self.no_install_upgrade?(); end + def self.no_installed_dependents_check?(); end def self.no_proxy(); end diff --git a/Library/Homebrew/upgrade.rb b/Library/Homebrew/upgrade.rb index 8c0f60b99c..4db35588eb 100644 --- a/Library/Homebrew/upgrade.rb +++ b/Library/Homebrew/upgrade.rb @@ -64,6 +64,24 @@ module Homebrew end end + def outdated_kegs(formula) + [formula, *formula.old_installed_formulae].map(&:linked_keg) + .select(&:directory?) + .map { |k| Keg.new(k.resolved_path) } + end + + def print_upgrade_message(formula, fi_options) + version_upgrade = if formula.optlinked? + "#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}" + else + "-> #{formula.pkg_version}" + end + oh1 <<~EOS + Upgrading #{Formatter.identifier(formula.full_specified_name)} + #{version_upgrade} #{fi_options.to_a.join(" ")} + EOS + end + def upgrade_formula( formula, flags:, @@ -83,11 +101,8 @@ module Homebrew keg_was_linked = keg.linked? end - formulae_maybe_with_kegs = [formula] + formula.old_installed_formulae - outdated_kegs = formulae_maybe_with_kegs.map(&:linked_keg) - .select(&:directory?) - .map { |k| Keg.new(k.resolved_path) } - linked_kegs = outdated_kegs.select(&:linked?) + kegs = outdated_kegs(formula) + linked_kegs = kegs.select(&:linked?) if formula.opt_prefix.directory? keg = Keg.new(formula.opt_prefix.resolved_path) @@ -118,13 +133,7 @@ module Homebrew }.compact, ) - upgrade_version = if formula.optlinked? - "#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}" - else - "-> #{formula.pkg_version}" - end - oh1 "Upgrading #{Formatter.identifier(formula.full_specified_name)} " \ - "#{upgrade_version} #{fi.options.to_a.join(" ")}" + print_upgrade_message(formula, fi.options) fi.prelude fi.fetch @@ -132,7 +141,7 @@ module Homebrew # first we unlink the currently active keg for this formula otherwise it is # possible for the existing build to interfere with the build we are about to # do! Seriously, it happens! - outdated_kegs.each(&:unlink) + kegs.each(&:unlink) fi.install fi.finish