Merge pull request #19676 from Homebrew/brew_bundle_upgrade_formula
Add `brew bundle --upgrade-formulae`
This commit is contained in:
		
						commit
						b44dfb2999
					
				@ -1,4 +1,4 @@
 | 
			
		||||
# typed: true # rubocop:todo Sorbet/StrictSigil
 | 
			
		||||
# typed: strict
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "English"
 | 
			
		||||
@ -6,11 +6,22 @@ require "English"
 | 
			
		||||
module Homebrew
 | 
			
		||||
  module Bundle
 | 
			
		||||
    class << self
 | 
			
		||||
      sig { params(args_upgrade_formula: T.nilable(String)).void }
 | 
			
		||||
      def upgrade_formulae=(args_upgrade_formula)
 | 
			
		||||
        @upgrade_formulae = args_upgrade_formula.to_s.split(",")
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T::Array[String]) }
 | 
			
		||||
      def upgrade_formulae
 | 
			
		||||
        @upgrade_formulae || []
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { params(cmd: T.any(String, Pathname), args: T.anything, verbose: T::Boolean).returns(T::Boolean) }
 | 
			
		||||
      def system(cmd, *args, verbose: false)
 | 
			
		||||
        return super cmd, *args if verbose
 | 
			
		||||
 | 
			
		||||
        logs = []
 | 
			
		||||
        success = T.let(nil, T.nilable(T::Boolean))
 | 
			
		||||
        success = T.let(false, T::Boolean)
 | 
			
		||||
        IO.popen([cmd, *args], err: [:child, :out]) do |pipe|
 | 
			
		||||
          while (buf = pipe.gets)
 | 
			
		||||
            logs << buf
 | 
			
		||||
@ -23,18 +34,22 @@ module Homebrew
 | 
			
		||||
        success
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { params(args: T.anything, verbose: T::Boolean).returns(T::Boolean) }
 | 
			
		||||
      def brew(*args, verbose: false)
 | 
			
		||||
        system(HOMEBREW_BREW_FILE, *args, verbose:)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T::Boolean) }
 | 
			
		||||
      def mas_installed?
 | 
			
		||||
        @mas_installed ||= which_formula("mas")
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T::Boolean) }
 | 
			
		||||
      def vscode_installed?
 | 
			
		||||
        @vscode_installed ||= which_vscode.present?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T.nilable(Pathname)) }
 | 
			
		||||
      def which_vscode
 | 
			
		||||
        @which_vscode ||= which("code", ORIGINAL_PATHS)
 | 
			
		||||
        @which_vscode ||= which("codium", ORIGINAL_PATHS)
 | 
			
		||||
@ -42,22 +57,26 @@ module Homebrew
 | 
			
		||||
        @which_vscode ||= which("code-insiders", ORIGINAL_PATHS)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T::Boolean) }
 | 
			
		||||
      def whalebrew_installed?
 | 
			
		||||
        @whalebrew_installed ||= which_formula("whalebrew")
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T::Boolean) }
 | 
			
		||||
      def cask_installed?
 | 
			
		||||
        @cask_installed ||= File.directory?("#{HOMEBREW_PREFIX}/Caskroom") &&
 | 
			
		||||
                            (File.directory?("#{HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-cask") ||
 | 
			
		||||
                             !Homebrew::EnvConfig.no_install_from_api?)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { params(name: String).returns(T::Boolean) }
 | 
			
		||||
      def which_formula(name)
 | 
			
		||||
        formula = Formulary.factory(name)
 | 
			
		||||
        ENV["PATH"] = "#{formula.opt_bin}:#{ENV.fetch("PATH", nil)}" if formula.any_version_installed?
 | 
			
		||||
        which(name).present?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { params(block: T.proc.returns(T.anything)).returns(T.untyped) }
 | 
			
		||||
      def exchange_uid_if_needed!(&block)
 | 
			
		||||
        euid = Process.euid
 | 
			
		||||
        uid = Process.uid
 | 
			
		||||
@ -83,6 +102,7 @@ module Homebrew
 | 
			
		||||
        return_value
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sig { returns(T::Hash[String, String]) }
 | 
			
		||||
      def formula_versions_from_env
 | 
			
		||||
        @formula_versions_from_env ||= begin
 | 
			
		||||
          formula_versions = {}
 | 
			
		||||
@ -106,11 +126,13 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      sig { void }
 | 
			
		||||
      def reset!
 | 
			
		||||
        @mas_installed = nil
 | 
			
		||||
        @vscode_installed = nil
 | 
			
		||||
        @whalebrew_installed = nil
 | 
			
		||||
        @cask_installed = nil
 | 
			
		||||
        @formula_versions_from_env = nil
 | 
			
		||||
        @mas_installed = T.let(nil, T.nilable(T::Boolean))
 | 
			
		||||
        @vscode_installed = T.let(nil, T.nilable(T::Boolean))
 | 
			
		||||
        @which_vscode = T.let(nil, T.nilable(String))
 | 
			
		||||
        @whalebrew_installed = T.let(nil, T.nilable(T::Boolean))
 | 
			
		||||
        @cask_installed = T.let(nil, T.nilable(T::Boolean))
 | 
			
		||||
        @formula_versions_from_env = T.let(nil, T.nilable(T::Hash[String, String]))
 | 
			
		||||
        @upgrade_formulae = T.let(nil, T.nilable(T::Array[String]))
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ module Homebrew
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def preinstall(no_upgrade: false, verbose: false)
 | 
			
		||||
        if installed? && (no_upgrade || !upgradable?)
 | 
			
		||||
        if installed? && (self.class.no_upgrade_with_args?(no_upgrade, @name) || !upgradable?)
 | 
			
		||||
          puts "Skipping install of #{@name} formula. It is already installed." if verbose
 | 
			
		||||
          @changed = nil
 | 
			
		||||
          return false
 | 
			
		||||
@ -166,11 +166,15 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      def self.formula_installed_and_up_to_date?(formula, no_upgrade: false)
 | 
			
		||||
        return false unless formula_installed?(formula)
 | 
			
		||||
        return true if no_upgrade
 | 
			
		||||
        return true if no_upgrade_with_args?(no_upgrade, formula)
 | 
			
		||||
 | 
			
		||||
        !formula_upgradable?(formula)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def self.no_upgrade_with_args?(no_upgrade, formula_name)
 | 
			
		||||
        no_upgrade && Bundle.upgrade_formulae.exclude?(formula_name)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def self.formula_in_array?(formula, array)
 | 
			
		||||
        return true if array.include?(formula)
 | 
			
		||||
        return true if array.include?(formula.split("/").last)
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ module Homebrew
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def failure_reason(name, no_upgrade:)
 | 
			
		||||
          reason = if no_upgrade
 | 
			
		||||
          reason = if no_upgrade && Bundle.upgrade_formulae.exclude?(name)
 | 
			
		||||
            "needs to be installed."
 | 
			
		||||
          else
 | 
			
		||||
            "needs to be installed or updated."
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
            Bundle.exchange_uid_if_needed! do
 | 
			
		||||
              vscode_extensions.each do |extension|
 | 
			
		||||
                Kernel.system(Bundle.which_vscode, "--uninstall-extension", extension)
 | 
			
		||||
                Kernel.system(T.must(Bundle.which_vscode).to_s, "--uninstall-extension", extension)
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ module Homebrew
 | 
			
		||||
        puts "Installing #{name} VSCode extension. It is not currently installed." if verbose
 | 
			
		||||
 | 
			
		||||
        return false unless Bundle.exchange_uid_if_needed! do
 | 
			
		||||
          Bundle.system(Bundle.which_vscode, "--install-extension", name, verbose:)
 | 
			
		||||
          Bundle.system(T.must(Bundle.which_vscode), "--install-extension", name, verbose:)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        installed_extensions << name
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,10 @@ module Homebrew
 | 
			
		||||
                            "This is enabled by default if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set."
 | 
			
		||||
        switch "--upgrade",
 | 
			
		||||
               description: "`install` runs `brew upgrade` on outdated dependencies, " \
 | 
			
		||||
                            "even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set. "
 | 
			
		||||
                            "even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set."
 | 
			
		||||
        flag "--upgrade-formulae=", "--upgrade-formula=",
 | 
			
		||||
             description: "`install` runs `brew upgrade` on any of these comma-separated formulae, " \
 | 
			
		||||
                          "even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set."
 | 
			
		||||
        switch "--install",
 | 
			
		||||
               description: "Run `install` before continuing to other operations e.g. `exec`."
 | 
			
		||||
        switch "--services",
 | 
			
		||||
@ -150,6 +153,7 @@ module Homebrew
 | 
			
		||||
        verbose = args.verbose?
 | 
			
		||||
        force = args.force?
 | 
			
		||||
        zap = args.zap?
 | 
			
		||||
        Homebrew::Bundle.upgrade_formulae = args.upgrade_formulae
 | 
			
		||||
 | 
			
		||||
        no_type_args = !args.brews? && !args.casks? && !args.taps? && !args.mas? && !args.whalebrew? && !args.vscode?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,12 @@ class Homebrew::Cmd::Bundle::Args < Homebrew::CLI::Args
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def upgrade?; end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T.nilable(String)) }
 | 
			
		||||
  def upgrade_formula; end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T.nilable(String)) }
 | 
			
		||||
  def upgrade_formulae; end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def vscode?; end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -219,7 +219,7 @@ RSpec.describe Homebrew::Bundle::Commands::Cleanup do
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "uninstalls extensions" do
 | 
			
		||||
      expect(Kernel).to receive(:system).with(Pathname("code"), "--uninstall-extension", "GitHub.codespaces")
 | 
			
		||||
      expect(Kernel).to receive(:system).with("code", "--uninstall-extension", "GitHub.codespaces")
 | 
			
		||||
      expect(described_class).to receive(:system_output_no_stderr).and_return("")
 | 
			
		||||
      described_class.run(force: true)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -667,6 +667,7 @@ _brew_bundle() {
 | 
			
		||||
      --services
 | 
			
		||||
      --tap
 | 
			
		||||
      --upgrade
 | 
			
		||||
      --upgrade-formulae
 | 
			
		||||
      --verbose
 | 
			
		||||
      --vscode
 | 
			
		||||
      --whalebrew
 | 
			
		||||
 | 
			
		||||
@ -521,7 +521,8 @@ __fish_brew_complete_arg 'bundle' -l no-vscode -d '`dump` without VSCode (and fo
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l quiet -d 'Make some output more quiet'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l services -d 'Temporarily start services while running the `exec` or `sh` command'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l tap -d '`list` or `dump` Homebrew tap dependencies'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l upgrade -d '`install` runs `brew upgrade` on outdated dependencies, even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set. '
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l upgrade -d '`install` runs `brew upgrade` on outdated dependencies, even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l upgrade-formulae -d '`install` runs `brew upgrade` on any of these comma-separated formulae, even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l verbose -d '`install` prints output from commands as they are run. `check` lists all missing dependencies'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l vscode -d '`list` or `dump` VSCode (and forks/variants) extensions'
 | 
			
		||||
__fish_brew_complete_arg 'bundle' -l whalebrew -d '`list` or `dump` Whalebrew dependencies'
 | 
			
		||||
 | 
			
		||||
@ -664,7 +664,8 @@ _brew_bundle() {
 | 
			
		||||
    '--quiet[Make some output more quiet]' \
 | 
			
		||||
    '--services[Temporarily start services while running the `exec` or `sh` command]' \
 | 
			
		||||
    '--tap[`list` or `dump` Homebrew tap dependencies]' \
 | 
			
		||||
    '(--install)--upgrade[`install` runs `brew upgrade` on outdated dependencies, even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set. ]' \
 | 
			
		||||
    '(--install)--upgrade[`install` runs `brew upgrade` on outdated dependencies, even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set]' \
 | 
			
		||||
    '--upgrade-formulae[`install` runs `brew upgrade` on any of these comma-separated formulae, even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set]' \
 | 
			
		||||
    '--verbose[`install` prints output from commands as they are run. `check` lists all missing dependencies]' \
 | 
			
		||||
    '(--no-vscode)--vscode[`list` or `dump` VSCode (and forks/variants) extensions]' \
 | 
			
		||||
    '--whalebrew[`list` or `dump` Whalebrew dependencies]' \
 | 
			
		||||
 | 
			
		||||
@ -247,6 +247,11 @@ flags which will help with finding keg-only dependencies like `openssl`,
 | 
			
		||||
: `install` runs `brew upgrade` on outdated dependencies, even if
 | 
			
		||||
  `$HOMEBREW_BUNDLE_NO_UPGRADE` is set.
 | 
			
		||||
 | 
			
		||||
`--upgrade-formulae`
 | 
			
		||||
 | 
			
		||||
: `install` runs `brew upgrade` on any of these comma-separated formulae, even
 | 
			
		||||
  if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set.
 | 
			
		||||
 | 
			
		||||
`--install`
 | 
			
		||||
 | 
			
		||||
: Run `install` before continuing to other operations e.g. `exec`.
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
.\" generated by kramdown
 | 
			
		||||
.TH "BREW" "1" "March 2025" "Homebrew"
 | 
			
		||||
.TH "BREW" "1" "April 2025" "Homebrew"
 | 
			
		||||
.SH NAME
 | 
			
		||||
brew \- The Missing Package Manager for macOS (or Linux)
 | 
			
		||||
.SH "SYNOPSIS"
 | 
			
		||||
@ -150,6 +150,9 @@ Read from or write to the \fBBrewfile\fP from \fB$HOMEBREW_BUNDLE_FILE_GLOBAL\fP
 | 
			
		||||
\fB\-\-upgrade\fP
 | 
			
		||||
\fBinstall\fP runs \fBbrew upgrade\fP on outdated dependencies, even if \fB$HOMEBREW_BUNDLE_NO_UPGRADE\fP is set\.
 | 
			
		||||
.TP
 | 
			
		||||
\fB\-\-upgrade\-formulae\fP
 | 
			
		||||
\fBinstall\fP runs \fBbrew upgrade\fP on any of these comma\-separated formulae, even if \fB$HOMEBREW_BUNDLE_NO_UPGRADE\fP is set\.
 | 
			
		||||
.TP
 | 
			
		||||
\fB\-\-install\fP
 | 
			
		||||
Run \fBinstall\fP before continuing to other operations e\.g\. \fBexec\fP\&\.
 | 
			
		||||
.TP
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user