From c25ed8f088171367cd736cb2dc6a212cc1b8089f Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sun, 1 Mar 2026 13:59:20 +0000 Subject: [PATCH] Cleanup Download Queue - cleanup various cases conditional on the download queue to always use it, now it's default and has been for a while - ensure that both casks and formulae are fetched at the same time when possible to maximise concurrency - generally DRY up the relevant download queue code --- Library/Homebrew/api.rb | 30 ++--- Library/Homebrew/api/cask.rb | 32 +++-- Library/Homebrew/api/formula.rb | 32 +++-- Library/Homebrew/api/internal.rb | 16 ++- Library/Homebrew/brew.rb | 2 +- Library/Homebrew/cask/installer.rb | 11 +- Library/Homebrew/cask/reinstall.rb | 53 ++++++-- Library/Homebrew/cask/upgrade.rb | 54 +++++--- Library/Homebrew/cmd/install.rb | 153 +++++++++++---------- Library/Homebrew/cmd/reinstall.rb | 42 +++++- Library/Homebrew/cmd/upgrade.rb | 183 ++++++++++++++++++++++++-- Library/Homebrew/download_queue.rb | 21 ++- Library/Homebrew/formula_installer.rb | 60 ++++----- Library/Homebrew/install.rb | 67 ++++++++-- Library/Homebrew/upgrade.rb | 9 +- 15 files changed, 536 insertions(+), 229 deletions(-) diff --git a/Library/Homebrew/api.rb b/Library/Homebrew/api.rb index b98881a047a98..fff1a6050a498 100644 --- a/Library/Homebrew/api.rb +++ b/Library/Homebrew/api.rb @@ -8,6 +8,7 @@ require "api/formula_struct" require "api/cask_struct" require "base64" +require "download_queue" require "utils/output" module Homebrew @@ -53,11 +54,13 @@ def self.skip_download?(target:, stale_seconds:) endpoint: String, target: Pathname, stale_seconds: T.nilable(Integer), - download_queue: T.nilable(DownloadQueue), + download_queue: DownloadQueue, + enqueue: T::Boolean, ).returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean]) } def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint, - stale_seconds: nil, download_queue: nil) + stale_seconds: nil, download_queue: Homebrew.default_download_queue, + enqueue: false) # Lazy-load dependency. require "development_tools" @@ -80,7 +83,7 @@ def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint, DevelopmentTools.curl_substitution_required? skip_download = skip_download?(target:, stale_seconds:) - if download_queue + if enqueue unless skip_download require "api/json_download" download = Homebrew::API::JSONDownload.new(endpoint, target:, stale_seconds:) @@ -167,10 +170,7 @@ def self.merge_variations(json, bottle_tag: T.unsafe(nil)) sig { void } def self.fetch_api_files! - download_queue = if Homebrew::EnvConfig.download_concurrency > 1 - require "download_queue" - Homebrew::DownloadQueue.new - end + download_queue = Homebrew::DownloadQueue.new stale_seconds = if ENV["HOMEBREW_API_UPDATED"].present? || (Homebrew::EnvConfig.no_auto_update? && !Homebrew::EnvConfig.force_api_auto_update?) @@ -182,19 +182,19 @@ def self.fetch_api_files! end if Homebrew::EnvConfig.use_internal_api? - Homebrew::API::Internal.fetch_formula_api!(download_queue:, stale_seconds:) - Homebrew::API::Internal.fetch_cask_api!(download_queue:, stale_seconds:) + Homebrew::API::Internal.fetch_formula_api!(download_queue:, stale_seconds:, enqueue: true) + Homebrew::API::Internal.fetch_cask_api!(download_queue:, stale_seconds:, enqueue: true) else - Homebrew::API::Formula.fetch_api!(download_queue:, stale_seconds:) - Homebrew::API::Formula.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS) - Homebrew::API::Cask.fetch_api!(download_queue:, stale_seconds:) - Homebrew::API::Cask.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS) + Homebrew::API::Formula.fetch_api!(download_queue:, stale_seconds:, enqueue: true) + Homebrew::API::Formula.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS, + enqueue: true) + Homebrew::API::Cask.fetch_api!(download_queue:, stale_seconds:, enqueue: true) + Homebrew::API::Cask.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS, + enqueue: true) end ENV["HOMEBREW_API_UPDATED"] = "1" - return unless download_queue - begin download_queue.fetch ensure diff --git a/Library/Homebrew/api/cask.rb b/Library/Homebrew/api/cask.rb index d8054a46a1047..6090dbbc39826 100644 --- a/Library/Homebrew/api/cask.rb +++ b/Library/Homebrew/api/cask.rb @@ -23,11 +23,10 @@ def self.cask_json(name) cache.fetch("cask_json").fetch(name) end - sig { params(name: String, download_queue: T.nilable(DownloadQueue)).void } - def self.fetch_cask_json!(name, download_queue: nil) + sig { params(name: String).void } + def self.fetch_cask_json!(name) endpoint = "cask/#{name}.json" - json_cask, updated = Homebrew::API.fetch_json_api_file endpoint, download_queue: download_queue - return if download_queue + json_cask, updated = Homebrew::API.fetch_json_api_file endpoint json_cask = JSON.parse((HOMEBREW_CACHE_API/endpoint).read) unless updated @@ -35,8 +34,14 @@ def self.fetch_cask_json!(name, download_queue: nil) cache["cask_json"][name] = json_cask end - sig { params(cask: ::Cask::Cask, download_queue: T.nilable(Homebrew::DownloadQueue)).returns(Homebrew::API::SourceDownload) } - def self.source_download(cask, download_queue: nil) + sig { + params( + cask: ::Cask::Cask, + download_queue: Homebrew::DownloadQueue, + enqueue: T::Boolean, + ).returns(Homebrew::API::SourceDownload) + } + def self.source_download(cask, download_queue: Homebrew.default_download_queue, enqueue: false) path = cask.ruby_source_path.to_s sha256 = cask.ruby_source_checksum[:sha256] checksum = Checksum.new(sha256) if sha256 @@ -52,7 +57,7 @@ def self.source_download(cask, download_queue: nil) cache: HOMEBREW_CACHE_API_SOURCE/"#{tap}/#{git_head}/Cask", ) - if download_queue + if enqueue download_queue.enqueue(download) elsif !download.symlink_location.exist? download.fetch @@ -75,19 +80,20 @@ def self.cached_json_file_path end sig { - params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer)) + params(download_queue: ::Homebrew::DownloadQueue, stale_seconds: T.nilable(Integer), enqueue: T::Boolean) .returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean]) } - def self.fetch_api!(download_queue: nil, stale_seconds: nil) - Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue: + def self.fetch_api!(download_queue: Homebrew.default_download_queue, stale_seconds: nil, enqueue: false) + Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:, enqueue: end sig { - params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer)) + params(download_queue: ::Homebrew::DownloadQueue, stale_seconds: T.nilable(Integer), enqueue: T::Boolean) .returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean]) } - def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: nil) - Homebrew::API.fetch_json_api_file "cask_tap_migrations.jws.json", stale_seconds:, download_queue: + def self.fetch_tap_migrations!(download_queue: Homebrew.default_download_queue, stale_seconds: nil, + enqueue: false) + Homebrew::API.fetch_json_api_file "cask_tap_migrations.jws.json", stale_seconds:, download_queue:, enqueue: end sig { returns(T::Boolean) } diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index 8bafb1fe0fa0f..6539b3fd32e30 100644 --- a/Library/Homebrew/api/formula.rb +++ b/Library/Homebrew/api/formula.rb @@ -25,11 +25,10 @@ def self.formula_json(name) cache.fetch("formula_json").fetch(name) end - sig { params(name: String, download_queue: T.nilable(DownloadQueue)).void } - def self.fetch_formula_json!(name, download_queue: nil) + sig { params(name: String).void } + def self.fetch_formula_json!(name) endpoint = "formula/#{name}.json" - json_formula, updated = Homebrew::API.fetch_json_api_file endpoint, download_queue: download_queue - return if download_queue + json_formula, updated = Homebrew::API.fetch_json_api_file endpoint json_formula = JSON.parse((HOMEBREW_CACHE_API/endpoint).read) unless updated @@ -37,8 +36,14 @@ def self.fetch_formula_json!(name, download_queue: nil) cache["formula_json"][name] = json_formula end - sig { params(formula: ::Formula, download_queue: T.nilable(Homebrew::DownloadQueue)).returns(Homebrew::API::SourceDownload) } - def self.source_download(formula, download_queue: nil) + sig { + params( + formula: ::Formula, + download_queue: Homebrew::DownloadQueue, + enqueue: T::Boolean, + ).returns(Homebrew::API::SourceDownload) + } + def self.source_download(formula, download_queue: Homebrew.default_download_queue, enqueue: false) path = formula.ruby_source_path || "Formula/#{formula.name}.rb" git_head = formula.tap_git_head || "HEAD" tap = formula.tap&.full_name || "Homebrew/homebrew-core" @@ -49,7 +54,7 @@ def self.source_download(formula, download_queue: nil) cache: HOMEBREW_CACHE_API_SOURCE/"#{tap}/#{git_head}/Formula", ) - if download_queue + if enqueue download_queue.enqueue(download) elsif !download.symlink_location.exist? download.fetch @@ -76,19 +81,20 @@ def self.cached_json_file_path end sig { - params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer)) + params(download_queue: Homebrew::DownloadQueue, stale_seconds: T.nilable(Integer), enqueue: T::Boolean) .returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean]) } - def self.fetch_api!(download_queue: nil, stale_seconds: nil) - Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue: + def self.fetch_api!(download_queue: Homebrew.default_download_queue, stale_seconds: nil, enqueue: false) + Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:, enqueue: end sig { - params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer)) + params(download_queue: Homebrew::DownloadQueue, stale_seconds: T.nilable(Integer), enqueue: T::Boolean) .returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean]) } - def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: nil) - Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json", stale_seconds:, download_queue: + def self.fetch_tap_migrations!(download_queue: Homebrew.default_download_queue, stale_seconds: nil, + enqueue: false) + Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json", stale_seconds:, download_queue:, enqueue: end sig { returns(T::Boolean) } diff --git a/Library/Homebrew/api/internal.rb b/Library/Homebrew/api/internal.rb index b1920f5451be7..a3f1afdb2cc70 100644 --- a/Library/Homebrew/api/internal.rb +++ b/Library/Homebrew/api/internal.rb @@ -50,20 +50,24 @@ def self.cached_cask_json_file_path end sig { - params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer)) + params(download_queue: Homebrew::DownloadQueue, stale_seconds: T.nilable(Integer), enqueue: T::Boolean) .returns([T::Hash[String, T.untyped], T::Boolean]) } - def self.fetch_formula_api!(download_queue: nil, stale_seconds: nil) - json_contents, updated = Homebrew::API.fetch_json_api_file(formula_endpoint, stale_seconds:, download_queue:) + def self.fetch_formula_api!(download_queue: Homebrew.default_download_queue, stale_seconds: nil, + enqueue: false) + json_contents, updated = Homebrew::API.fetch_json_api_file(formula_endpoint, stale_seconds:, download_queue:, + enqueue:) [T.cast(json_contents, T::Hash[String, T.untyped]), updated] end sig { - params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer)) + params(download_queue: Homebrew::DownloadQueue, stale_seconds: T.nilable(Integer), enqueue: T::Boolean) .returns([T::Hash[String, T.untyped], T::Boolean]) } - def self.fetch_cask_api!(download_queue: nil, stale_seconds: nil) - json_contents, updated = Homebrew::API.fetch_json_api_file(cask_endpoint, stale_seconds:, download_queue:) + def self.fetch_cask_api!(download_queue: Homebrew.default_download_queue, stale_seconds: nil, + enqueue: false) + json_contents, updated = Homebrew::API.fetch_json_api_file(cask_endpoint, stale_seconds:, download_queue:, + enqueue:) [T.cast(json_contents, T::Hash[String, T.untyped]), updated] end diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index e98d44e60f08d..836e24b9135be 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -90,7 +90,7 @@ cmd_class = Homebrew::AbstractCommand.command(cmd) Homebrew.running_command = cmd if cmd_class - if !Homebrew::EnvConfig.no_install_from_api? && Homebrew::EnvConfig.download_concurrency > 1 + unless Homebrew::EnvConfig.no_install_from_api? require "api" Homebrew::API.fetch_api_files! end diff --git a/Library/Homebrew/cask/installer.rb b/Library/Homebrew/cask/installer.rb index 0bb24096da8aa..0ec081cd1dedc 100644 --- a/Library/Homebrew/cask/installer.rb +++ b/Library/Homebrew/cask/installer.rb @@ -25,14 +25,15 @@ class Installer skip_cask_deps: T::Boolean, binaries: T::Boolean, verbose: T::Boolean, zap: T::Boolean, require_sha: T::Boolean, upgrade: T::Boolean, reinstall: T::Boolean, installed_as_dependency: T::Boolean, installed_on_request: T::Boolean, quarantine: T::Boolean, verify_download_integrity: T::Boolean, - quiet: T::Boolean, download_queue: T.nilable(Homebrew::DownloadQueue) + quiet: T::Boolean, download_queue: Homebrew::DownloadQueue, defer_fetch: T::Boolean ).void } def initialize(cask, command: SystemCommand, force: false, adopt: false, skip_cask_deps: false, binaries: true, verbose: false, zap: false, require_sha: false, upgrade: false, reinstall: false, installed_as_dependency: false, installed_on_request: true, - quarantine: true, verify_download_integrity: true, quiet: false, download_queue: nil) + quarantine: true, verify_download_integrity: true, quiet: false, + download_queue: Homebrew.default_download_queue, defer_fetch: false) @cask = cask @command = command @force = force @@ -50,6 +51,7 @@ def initialize(cask, command: SystemCommand, force: false, adopt: false, @verify_download_integrity = verify_download_integrity @quiet = quiet @download_queue = download_queue + @defer_fetch = T.let(defer_fetch, T::Boolean) @ran_prelude = T.let(false, T::Boolean) end @@ -118,7 +120,7 @@ def fetch(quiet: nil, timeout: nil) forbidden_cask_and_formula_check forbidden_cask_artifacts_check - download(quiet:, timeout:) if @download_queue.nil? + download(quiet:, timeout:) unless @defer_fetch satisfy_cask_and_formula_dependencies end @@ -853,7 +855,6 @@ def prelude sig { void } def enqueue_downloads download_queue = @download_queue - return if download_queue.nil? # FIXME: We need to load Cask source before enqueuing to support # language-specific URLs, but this will block the main process. @@ -861,7 +862,7 @@ def enqueue_downloads if @cask.languages.any? load_cask_from_source_api! else - Homebrew::API::Cask.source_download(@cask, download_queue:) + Homebrew::API::Cask.source_download(@cask, download_queue:, enqueue: true) end end diff --git a/Library/Homebrew/cask/reinstall.rb b/Library/Homebrew/cask/reinstall.rb index e0fcf03b1179b..e1428ebe964be 100644 --- a/Library/Homebrew/cask/reinstall.rb +++ b/Library/Homebrew/cask/reinstall.rb @@ -10,7 +10,8 @@ class Reinstall sig { params( casks: ::Cask::Cask, verbose: T::Boolean, force: T::Boolean, skip_cask_deps: T::Boolean, binaries: T::Boolean, - require_sha: T::Boolean, quarantine: T::Boolean, zap: T::Boolean + require_sha: T::Boolean, quarantine: T::Boolean, zap: T::Boolean, skip_prefetch: T::Boolean, + download_queue: T.nilable(Homebrew::DownloadQueue) ).void } def self.reinstall_casks( @@ -21,25 +22,51 @@ def self.reinstall_casks( binaries: false, require_sha: false, quarantine: false, - zap: false + zap: false, + skip_prefetch: false, + download_queue: nil ) require "cask/installer" quarantine = true if quarantine.nil? - - download_queue = Homebrew::DownloadQueue.new_if_concurrency_enabled(pour: true) - cask_installers = casks.map do |cask| - Installer.new(cask, binaries:, verbose:, force:, skip_cask_deps:, require_sha:, reinstall: true, - quarantine:, zap:, download_queue:) + created_download_queue = T.let(false, T::Boolean) + if download_queue.nil? + if skip_prefetch + download_queue = Homebrew.default_download_queue + else + download_queue = Homebrew::DownloadQueue.new(pour: true) + created_download_queue = true + end end - if download_queue - cask_installers.each(&:prelude) + cask_installers = T.let([], T::Array[Installer]) + begin + cask_installers = casks.map do |cask| + Installer.new( + cask, + binaries:, + verbose:, + force:, + skip_cask_deps:, + require_sha:, + reinstall: true, + quarantine:, + zap:, + download_queue:, + defer_fetch: true, + ) + end + + unless skip_prefetch + cask_installers.each(&:prelude) - oh1 "Fetching downloads for: #{casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence}", - truncate: false - cask_installers.each(&:enqueue_downloads) - download_queue.fetch + oh1 "Fetching downloads for: #{casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence}", + truncate: false + cask_installers.each(&:enqueue_downloads) + download_queue.fetch + end + ensure + download_queue.shutdown if created_download_queue end exit 1 if Homebrew.failed? diff --git a/Library/Homebrew/cask/upgrade.rb b/Library/Homebrew/cask/upgrade.rb index 97a094e022879..63b832cd9dcc1 100644 --- a/Library/Homebrew/cask/upgrade.rb +++ b/Library/Homebrew/cask/upgrade.rb @@ -71,6 +71,8 @@ def self.outdated_casks(casks, args:, force:, quiet:, binaries: T.nilable(T::Boolean), quarantine: T.nilable(T::Boolean), require_sha: T.nilable(T::Boolean), + skip_prefetch: T::Boolean, + download_queue: T.nilable(Homebrew::DownloadQueue), ).returns(T::Boolean) } def self.upgrade_casks!( @@ -86,7 +88,9 @@ def self.upgrade_casks!( quiet: false, binaries: nil, quarantine: nil, - require_sha: nil + require_sha: nil, + skip_prefetch: false, + download_queue: nil ) quarantine = true if quarantine.nil? @@ -134,27 +138,36 @@ def self.upgrade_casks!( return false if upgradable_casks.empty? - if !dry_run && Homebrew::EnvConfig.download_concurrency > 1 - download_queue = Homebrew::DownloadQueue.new(pour: true) - - fetchable_casks = upgradable_casks.map(&:last) - fetchable_cask_installers = fetchable_casks.map do |cask| - # This is significantly easier given the weird difference in Sorbet signatures here. - # rubocop:disable Style/DoubleNegation - Installer.new(cask, binaries: !!binaries, verbose: !!verbose, force: !!force, - skip_cask_deps: !!skip_cask_deps, require_sha: !!require_sha, - upgrade: true, quarantine:, download_queue:) - # rubocop:enable Style/DoubleNegation - end + created_download_queue = T.let(false, T::Boolean) + download_queue ||= if !dry_run && !skip_prefetch + created_download_queue = true + Homebrew::DownloadQueue.new(pour: true) + end - fetchable_cask_installers.each(&:prelude) + if !dry_run && !skip_prefetch + prefetch_download_queue = download_queue || Homebrew.default_download_queue + begin + fetchable_casks = upgradable_casks.map(&:last) + fetchable_cask_installers = fetchable_casks.map do |cask| + # This is significantly easier given the weird difference in Sorbet signatures here. + # rubocop:disable Style/DoubleNegation + Installer.new(cask, binaries: !!binaries, verbose: !!verbose, force: !!force, + skip_cask_deps: !!skip_cask_deps, require_sha: !!require_sha, + upgrade: true, quarantine:, download_queue: prefetch_download_queue, + defer_fetch: true) + # rubocop:enable Style/DoubleNegation + end - fetchable_casks_sentence = fetchable_casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence - oh1 "Fetching downloads for: #{fetchable_casks_sentence}", truncate: false + fetchable_cask_installers.each(&:prelude) - fetchable_cask_installers.each(&:enqueue_downloads) + fetchable_casks_sentence = fetchable_casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence + oh1 "Fetching downloads for: #{fetchable_casks_sentence}", truncate: false - download_queue.fetch + fetchable_cask_installers.each(&:enqueue_downloads) + prefetch_download_queue.fetch + ensure + prefetch_download_queue.shutdown if created_download_queue + end end verb = dry_run ? "Would upgrade" : "Upgrading" @@ -167,6 +180,8 @@ def self.upgrade_casks!( .join("\n") return true if dry_run + download_queue ||= Homebrew.default_download_queue + upgradable_casks.each do |(old_cask, new_cask)| upgrade_cask( old_cask, new_cask, @@ -197,7 +212,7 @@ def self.upgrade_casks!( require_sha: T.nilable(T::Boolean), skip_cask_deps: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean), - download_queue: T.nilable(Homebrew::DownloadQueue), + download_queue: Homebrew::DownloadQueue, ).void } def self.upgrade_cask( @@ -231,6 +246,7 @@ def self.upgrade_cask( upgrade: true, quarantine:, download_queue:, + defer_fetch: true, }.compact new_cask_installer = diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 73b8a1241c693..6ed64752949f5 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -220,6 +220,9 @@ def run raise end + installed_casks = T.let([], T::Array[Cask::Cask]) + new_casks = T.let([], T::Array[Cask::Cask]) + fetch_casks = T.let([], T::Array[Cask::Cask]) if casks.any? Install.ask_casks casks if args.ask? if args.dry_run? @@ -245,73 +248,10 @@ def run installed_casks, new_casks = casks.partition(&:installed?) - download_queue = Homebrew::DownloadQueue.new_if_concurrency_enabled(pour: true) - fetch_casks = Homebrew::EnvConfig.no_install_upgrade? ? new_casks : casks - outdated_casks = Cask::Upgrade.outdated_casks(fetch_casks, args:, force: true, quiet: true) - fetch_casks = outdated_casks.intersection(fetch_casks) - - if download_queue && fetch_casks.any? - binaries = args.binaries? - verbose = args.verbose? - force = args.force? - require_sha = args.require_sha? - quarantine = args.quarantine? - skip_cask_deps = args.skip_cask_deps? - zap = args.zap? - - fetch_cask_installers = fetch_casks.map do |cask| - Cask::Installer.new(cask, reinstall: true, binaries:, verbose:, force:, skip_cask_deps:, - require_sha:, quarantine:, zap:, download_queue:) - end - - # Run prelude checks for all casks before enqueueing downloads - fetch_cask_installers.each(&:prelude) - - fetch_casks_sentence = fetch_casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence - oh1 "Fetching downloads for: #{fetch_casks_sentence}", truncate: false - - fetch_cask_installers.each(&:enqueue_downloads) - - download_queue.fetch - end - - exit 1 if Homebrew.failed? - - begin - new_casks.each do |cask| - Cask::Installer.new( - cask, - adopt: args.adopt?, - binaries: args.binaries?, - force: args.force?, - quarantine: args.quarantine?, - quiet: args.quiet?, - require_sha: args.require_sha?, - skip_cask_deps: args.skip_cask_deps?, - verbose: args.verbose?, - ).install - end - rescue => e - ofail e - end - - if !Homebrew::EnvConfig.no_install_upgrade? && installed_casks.any? - begin - Cask::Upgrade.upgrade_casks!( - *installed_casks, - force: args.force?, - dry_run: args.dry_run?, - binaries: args.binaries?, - quarantine: args.quarantine?, - require_sha: args.require_sha?, - skip_cask_deps: args.skip_cask_deps?, - verbose: args.verbose?, - quiet: args.quiet?, - args:, - ) - rescue => e - ofail e - end + fetch_casks = if Homebrew::EnvConfig.no_install_upgrade? + new_casks + else + new_casks | Cask::Upgrade.outdated_casks(casks, args:, force: true, quiet: true) end end @@ -346,7 +286,7 @@ def run ) end - return if formulae.any? && installed_formulae.empty? + return if formulae.any? && installed_formulae.empty? && casks.empty? Install.perform_preinstall_checks_once Install.check_cc_argv(args.cc) @@ -397,7 +337,41 @@ def run # Main block: if asking the user is enabled, show dependency and size information. Install.ask_formulae(formulae_installer, dependants, args: args) if args.ask? - formulae_installer = Install.fetch_formulae(formulae_installer) unless args.dry_run? + if !args.dry_run? && (formulae_installer.any? || fetch_casks.any?) + download_queue = Homebrew::DownloadQueue.new(pour: true) + begin + Install.show_combined_fetch_downloads_heading( + formula_names: formulae_installer.map { |fi| fi.formula.name }, + cask_names: fetch_casks.map(&:full_name), + ) + + formulae_installer = Install.enqueue_formulae(formulae_installer, download_queue:) + + if fetch_casks.any? + fetch_cask_installers = fetch_casks.map do |cask| + Cask::Installer.new( + cask, + reinstall: true, + binaries: args.binaries?, + verbose: args.verbose?, + force: args.force?, + skip_cask_deps: args.skip_cask_deps?, + require_sha: args.require_sha?, + quarantine: args.quarantine?, + zap: args.zap?, + download_queue:, + defer_fetch: true, + ) + end + + Install.enqueue_cask_installers(fetch_cask_installers) + end + + download_queue.fetch + ensure + download_queue.shutdown + end + end exit 1 if Homebrew.failed? @@ -420,6 +394,47 @@ def run verbose: args.verbose? ) + if casks.any? + begin + new_casks.each do |cask| + Cask::Installer.new( + cask, + adopt: args.adopt?, + binaries: args.binaries?, + defer_fetch: fetch_casks.include?(cask), + force: args.force?, + quarantine: args.quarantine?, + quiet: args.quiet?, + require_sha: args.require_sha?, + skip_cask_deps: args.skip_cask_deps?, + verbose: args.verbose?, + ).install + end + rescue => e + ofail e + end + + if !Homebrew::EnvConfig.no_install_upgrade? && installed_casks.any? + begin + Cask::Upgrade.upgrade_casks!( + *installed_casks, + force: args.force?, + dry_run: args.dry_run?, + binaries: args.binaries?, + quarantine: args.quarantine?, + require_sha: args.require_sha?, + skip_cask_deps: args.skip_cask_deps?, + verbose: args.verbose?, + quiet: args.quiet?, + skip_prefetch: true, + args:, + ) + rescue => e + ofail e + end + end + end + Cleanup.periodic_clean!(dry_run: args.dry_run?) Homebrew.messages.display_messages(display_times: args.display_times?) diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index d63b6a029b942..7f704985ad2d9 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -146,6 +146,8 @@ def run end formulae = Homebrew::Attestation.sort_formulae_for_install(formulae) if Homebrew::Attestation.enabled? + shared_download_queue = T.let(nil, T.nilable(Homebrew::DownloadQueue)) + casks_prefetched = T.let(false, T::Boolean) unless formulae.empty? Install.perform_preinstall_checks_once @@ -192,7 +194,43 @@ def run # Main block: if asking the user is enabled, show dependency and size information. Install.ask_formulae(formulae_installers, dependants, args: args) if args.ask? - valid_formula_installers = Install.fetch_formulae(formulae_installers) + valid_formula_installers = if casks.any? + shared_download_queue = Homebrew::DownloadQueue.new(pour: true) + begin + Install.show_combined_fetch_downloads_heading( + formula_names: formulae_installers.map { |fi| fi.formula.name }, + cask_names: casks.map(&:full_name), + ) + + valid_formula_installers = Install.enqueue_formulae(formulae_installers, + download_queue: shared_download_queue) + + require "cask/installer" + fetch_cask_installers = casks.map do |cask| + Cask::Installer.new( + cask, + binaries: args.binaries?, + verbose: args.verbose?, + force: args.force?, + skip_cask_deps: args.skip_cask_deps?, + require_sha: args.require_sha?, + reinstall: true, + quarantine: args.quarantine?, + zap: args.zap?, + download_queue: shared_download_queue, + defer_fetch: true, + ) + end + Install.enqueue_cask_installers(fetch_cask_installers) + shared_download_queue.fetch + casks_prefetched = true + valid_formula_installers + ensure + shared_download_queue.shutdown + end + else + Install.fetch_formulae(formulae_installers) + end exit 1 if Homebrew.failed? @@ -230,6 +268,8 @@ def run skip_cask_deps: args.skip_cask_deps?, quarantine: args.quarantine?, zap: args.zap?, + skip_prefetch: casks_prefetched, + download_queue: nil, ) rescue => e ofail e diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index f953c42c69c2c..f87230432a823 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -14,6 +14,12 @@ module Homebrew module Cmd class UpgradeCmd < AbstractCommand + class FormulaeUpgradeContext < T::Struct + const :formulae_to_install, T::Array[Formula] + const :formulae_installer, T::Array[FormulaInstaller] + const :dependants, Homebrew::Upgrade::Dependents + end + cmd_args do description <<~EOS Upgrade outdated casks and outdated, unpinned formulae using the same options they were originally @@ -136,6 +142,9 @@ def run [], T::Array[T.any(FormulaOrCaskUnavailableError, NoSuchKegError)], ) + @prefetched_formulae_upgrade_context = T.let(nil, T.nilable(FormulaeUpgradeContext)) + prefetched_formulae_names = T.let([], T::Array[String]) + prefetched_cask_names = T.let([], T::Array[String]) if args.named.present? args.named.to_formulae_and_casks_and_unavailable(method: :resolve).each do |item| @@ -163,8 +172,42 @@ def run formulae = Homebrew::Attestation.sort_formulae_for_install(formulae) if Homebrew::Attestation.enabled? - upgrade_outdated_formulae!(formulae) unless only_upgrade_casks - upgrade_outdated_casks!(casks) unless only_upgrade_formulae + prefetched_casks = T.let(false, T::Boolean) + shared_download_queue = T.let(nil, T.nilable(Homebrew::DownloadQueue)) + if !args.dry_run? && !only_upgrade_formulae && !only_upgrade_casks + shared_download_queue = Homebrew::DownloadQueue.new(pour: true) + begin + upgrade_outdated_formulae!( + formulae, + prefetch_only: true, + download_queue: shared_download_queue, + prefetch_names: prefetched_formulae_names, + show_downloads_heading: false, + ) + prefetched_casks = prefetch_outdated_casks!( + casks, + download_queue: shared_download_queue, + prefetch_names: prefetched_cask_names, + show_downloads_heading: false, + ) + Install.show_combined_fetch_downloads_heading( + formula_names: prefetched_formulae_names, + cask_names: prefetched_cask_names, + ) + shared_download_queue.fetch + ensure + shared_download_queue.shutdown + end + end + + upgrade_outdated_formulae!(formulae, use_prefetched: true) unless only_upgrade_casks + unless only_upgrade_formulae + upgrade_outdated_casks!( + casks, + skip_prefetch: prefetched_casks, + download_queue: nil, + ) + end unavailable_errors.each { |e| ofail e } @@ -177,10 +220,8 @@ def run private - sig { params(formulae: T::Array[Formula]).returns(T::Boolean) } - def upgrade_outdated_formulae!(formulae) - return false if args.cask? - + sig { params(formulae: T::Array[Formula]).returns(T.nilable(FormulaeUpgradeContext)) } + def formulae_upgrade_context(formulae) if args.build_from_source? unless DevelopmentTools.installed? raise BuildFlagsError.new(["--build-from-source"], bottled: formulae.all?(&:bottled?)) @@ -211,7 +252,7 @@ def upgrade_outdated_formulae!(formulae) end end - return false if outdated.blank? + return if outdated.blank? pinned = outdated.select(&:pinned?) outdated -= pinned @@ -269,7 +310,7 @@ def upgrade_outdated_formulae!(formulae) verbose: args.verbose?, ) - return false if formulae_installer.blank? + return if formulae_installer.blank? dependants = Upgrade.dependants( formulae_to_install, @@ -290,12 +331,64 @@ def upgrade_outdated_formulae!(formulae) # Main block: if asking the user is enabled, show dependency and size information. Install.ask_formulae(formulae_installer, dependants, args: args) if args.ask? - Upgrade.upgrade_formulae(formulae_installer, - dry_run: args.dry_run?, - verbose: args.verbose?) + FormulaeUpgradeContext.new( + formulae_to_install:, + formulae_installer: formulae_installer, + dependants:, + ) + end + + sig { + params( + formulae: T::Array[Formula], + prefetch_only: T::Boolean, + use_prefetched: T::Boolean, + download_queue: T.nilable(Homebrew::DownloadQueue), + prefetch_names: T.nilable(T::Array[String]), + show_downloads_heading: T::Boolean, + ).returns(T::Boolean) + } + def upgrade_outdated_formulae!(formulae, prefetch_only: false, use_prefetched: false, + download_queue: nil, + prefetch_names: nil, + show_downloads_heading: true) + return false if args.cask? + + use_prefetched_context = use_prefetched && @prefetched_formulae_upgrade_context + context = if use_prefetched_context + @prefetched_formulae_upgrade_context + else + formulae_upgrade_context(formulae) + end + return false if context.blank? + + if prefetch_only + prefetch_download_queue = download_queue || Homebrew.default_download_queue + valid_formula_installers = Install.enqueue_formulae(context.formulae_installer, + download_queue: prefetch_download_queue) + if show_downloads_heading + Install.show_combined_fetch_downloads_heading( + formula_names: valid_formula_installers.map { |fi| fi.formula.name }, + ) + end + prefetch_names&.replace(valid_formula_installers.map { |fi| fi.formula.name }) + @prefetched_formulae_upgrade_context = FormulaeUpgradeContext.new( + formulae_to_install: context.formulae_to_install, + formulae_installer: valid_formula_installers, + dependants: context.dependants, + ) + return valid_formula_installers.present? + end + + Upgrade.upgrade_formulae( + context.formulae_installer, + dry_run: args.dry_run?, + verbose: args.verbose?, + fetch: !use_prefetched_context, + ) Upgrade.upgrade_dependents( - dependants, formulae_to_install, + context.dependants, context.formulae_to_install, flags: args.flags_only, dry_run: args.dry_run?, force_bottle: args.force_bottle?, @@ -309,11 +402,71 @@ def upgrade_outdated_formulae!(formulae) verbose: args.verbose? ) + @prefetched_formulae_upgrade_context = nil if use_prefetched_context true end - sig { params(casks: T::Array[Cask::Cask]).returns(T::Boolean) } - def upgrade_outdated_casks!(casks) + sig { + params(casks: T::Array[Cask::Cask], download_queue: Homebrew::DownloadQueue, + prefetch_names: T.nilable(T::Array[String]), + show_downloads_heading: T::Boolean) + .returns(T::Boolean) + } + def prefetch_outdated_casks!(casks, download_queue:, prefetch_names: nil, show_downloads_heading: true) + return false if args.formula? + + outdated_casks = Cask::Upgrade.outdated_casks( + casks, + args:, + force: args.force?, + quiet: args.quiet?, + greedy: args.greedy?, + greedy_latest: args.greedy_latest?, + greedy_auto_updates: args.greedy_auto_updates?, + ) + return false if outdated_casks.empty? + + manual_installer_casks = outdated_casks.select do |cask| + cask.artifacts.any? do |artifact| + artifact.is_a?(Cask::Artifact::Installer) && artifact.manual_install + end + end + outdated_casks -= manual_installer_casks + return false if outdated_casks.empty? + + require "cask/installer" + fetchable_cask_installers = outdated_casks.map do |cask| + Cask::Installer.new( + cask, + binaries: args.binaries?, + verbose: args.verbose?, + force: args.force?, + skip_cask_deps: args.skip_cask_deps?, + require_sha: args.require_sha?, + upgrade: true, + quarantine: args.quarantine?, + download_queue:, + defer_fetch: true, + ) + end + cask_names = outdated_casks.map(&:full_name) + prefetch_names&.replace(cask_names) + Install.show_combined_fetch_downloads_heading(cask_names:) if show_downloads_heading + Install.enqueue_cask_installers(fetchable_cask_installers) + + true + rescue => e + ofail e + false + end + + sig { + params(casks: T::Array[Cask::Cask], skip_prefetch: T::Boolean, + download_queue: T.nilable(Homebrew::DownloadQueue)) + .returns(T::Boolean) + } + def upgrade_outdated_casks!(casks, skip_prefetch: false, + download_queue: nil) return false if args.formula? Install.ask_casks casks if args.ask? @@ -331,6 +484,8 @@ def upgrade_outdated_casks!(casks) skip_cask_deps: args.skip_cask_deps?, verbose: args.verbose?, quiet: args.quiet?, + skip_prefetch:, + download_queue:, args:, ) rescue => e diff --git a/Library/Homebrew/download_queue.rb b/Library/Homebrew/download_queue.rb index 89478e303fb85..bbf35fbff8cb7 100644 --- a/Library/Homebrew/download_queue.rb +++ b/Library/Homebrew/download_queue.rb @@ -17,13 +17,6 @@ class CancelledDownloadError < StandardError; end class DownloadQueue include Utils::Output::Mixin - sig { params(retries: Integer, force: T::Boolean, pour: T::Boolean).returns(T.nilable(DownloadQueue)) } - def self.new_if_concurrency_enabled(retries: 1, force: false, pour: false) - return if Homebrew::EnvConfig.download_concurrency <= 1 - - new(retries:, force:, pour:) - end - sig { params(retries: Integer, force: T::Boolean, pour: T::Boolean).void } def initialize(retries: 1, force: false, pour: false) @concurrency = T.let(EnvConfig.download_concurrency, Integer) @@ -388,4 +381,18 @@ def to_s end end end + + sig { returns(DownloadQueue) } + def self.default_download_queue + @default_download_queue ||= T.let(DownloadQueue.new, T.nilable(DownloadQueue)) + end + + sig { void } + def self.shutdown_default_download_queue + @default_download_queue&.shutdown + end + + at_exit do + Homebrew.shutdown_default_download_queue + end end diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index da017c8ac882e..b38a52748aa0d 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -46,7 +46,7 @@ class FormulaInstaller sig { returns(T::Boolean) } attr_accessor :link_keg - sig { returns(T.nilable(Homebrew::DownloadQueue)) } + sig { returns(Homebrew::DownloadQueue) } attr_accessor :download_queue sig { @@ -142,7 +142,7 @@ def initialize( @hold_locks = T.let(false, T::Boolean) @show_summary_heading = T.let(false, T::Boolean) @etc_var_preinstall = T.let([], T::Array[Pathname]) - @download_queue = T.let(nil, T.nilable(Homebrew::DownloadQueue)) + @download_queue = T.let(Homebrew.default_download_queue, Homebrew::DownloadQueue) # Take the original formula instance, which might have been swapped from an API instance to a source instance @formula = T.let(T.must(previously_fetched_formula), Formula) if previously_fetched_formula @@ -325,9 +325,9 @@ def prelude_fetch if pour_bottle? # Needs to be done before expand_dependencies for compute_dependencies - fetch_bottle_tab + fetch_bottle_tab(enqueue: true) elsif formula.loaded_from_api? - Homebrew::API::Formula.source_download(formula, download_queue:) + Homebrew::API::Formula.source_download(formula, download_queue:, enqueue: true) end fetch_fetch_deps unless ignore_deps? @@ -367,8 +367,7 @@ def prelude check_install_sanity - # with the download queue: these should have already been installed - install_fetch_deps if !ignore_deps? && download_queue.nil? + install_fetch_deps if !ignore_deps? && Homebrew::EnvConfig.download_concurrency <= 1 end sig { void } @@ -842,7 +841,7 @@ def fetch_dependency(dep) ) fi.download_queue = download_queue fi.prelude - fi.fetch + fi.enqueue_fetch end sig { params(dep: Dependency).void } @@ -1378,13 +1377,6 @@ def fetch_dependencies return if deps.empty? - unless download_queue - dependencies_string = deps.map { |dep| Formatter.identifier(dep) } - .to_sentence - oh1 "Fetching dependencies for #{formula.full_name}: #{dependencies_string}", - truncate: false - end - deps.each { fetch_dependency(it) } end @@ -1400,13 +1392,13 @@ def previously_fetched_formula end end - sig { params(quiet: T::Boolean).void } - def fetch_bottle_tab(quiet: false) + sig { params(quiet: T::Boolean, enqueue: T::Boolean).void } + def fetch_bottle_tab(quiet: false, enqueue: false) return if @fetch_bottle_tab - if (download_queue = self.download_queue) && - (bottle = formula.bottle) && - (manifest_resource = bottle.github_packages_manifest_resource) + if (bottle = formula.bottle) && + (manifest_resource = bottle.github_packages_manifest_resource) && + enqueue download_queue.enqueue(manifest_resource) else begin @@ -1421,6 +1413,12 @@ def fetch_bottle_tab(quiet: false) sig { void } def fetch + enqueue_fetch + download_queue.fetch + end + + sig { void } + def enqueue_fetch return if previously_fetched_formula fetch_dependencies @@ -1428,22 +1426,15 @@ def fetch return if only_deps? return if formula.local_bottle_path.present? - oh1 "Fetching #{Formatter.identifier(formula.full_name)}".strip unless download_queue - downloadable_object = downloadable check_attestation = if pour_bottle?(output_warning: true) - fetch_bottle_tab + fetch_bottle_tab(enqueue: true) !downloadable_object.cached_download.exist? else @formula = Homebrew::API::Formula.source_download_formula(formula) if formula.loaded_from_api? - if (download_queue = self.download_queue) - formula.enqueue_resources_and_patches(download_queue:) - else - formula.fetch_patches - formula.resources.each(&:fetch) - end + formula.enqueue_resources_and_patches(download_queue:) downloadable_object = downloadable @@ -1457,15 +1448,8 @@ def fetch check_attestation &&= Homebrew::Attestation.enabled? && (formula.tap&.core_tap? || false) && formula.name != "gh" - if (download_queue = self.download_queue) - # Check attestation after download completes. - download_queue.enqueue(downloadable_object, check_attestation:) - else - downloadable_object.fetch - if check_attestation && downloadable_object.is_a?(Bottle) - Utils::Attestation.check_attestation(downloadable_object, quiet: @quiet) - end - end + # Check attestation after download completes. + download_queue.enqueue(downloadable_object, check_attestation:) self.class.fetched << formula rescue CannotInstallFormulaError @@ -1495,7 +1479,7 @@ def pour formula.rack.mkpath # Download queue may have already extracted the bottle to a temporary directory. - # We cannot check `download_queue` as it is nil when pouring dependencies. + # We cannot rely on `download_queue` here as dependencies may be poured by another installer. formula_prefix_relative_to_cellar = formula.prefix.relative_path_from(HOMEBREW_CELLAR) bottle_tmp_keg = HOMEBREW_TEMP_CELLAR/formula_prefix_relative_to_cellar bottle_poured_file = Pathname("#{bottle_tmp_keg}.poured") diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb index 4b5558bad6c96..f16de6cb8e70d 100644 --- a/Library/Homebrew/install.rb +++ b/Library/Homebrew/install.rb @@ -329,24 +329,39 @@ def formula_installers( end end - sig { params(formula_installers: T::Array[FormulaInstaller]).returns(T::Array[FormulaInstaller]) } - def fetch_formulae(formula_installers) + sig { + params( + formula_installers: T::Array[FormulaInstaller], + download_queue: T.nilable(Homebrew::DownloadQueue), + fetch_after_enqueue: T::Boolean, + shutdown_download_queue: T::Boolean, + show_downloads_heading: T::Boolean, + ).returns(T::Array[FormulaInstaller]) + } + def fetch_formulae( + formula_installers, + download_queue: nil, + fetch_after_enqueue: true, + shutdown_download_queue: true, + show_downloads_heading: true + ) formulae_names_to_install = formula_installers.map { |fi| fi.formula.name } return formula_installers if formulae_names_to_install.empty? - formula_sentence = formulae_names_to_install.map { |name| Formatter.identifier(name) }.to_sentence - oh1 "Fetching downloads for: #{formula_sentence}", truncate: false - if EnvConfig.download_concurrency > 1 - download_queue = Homebrew::DownloadQueue.new(pour: true) - formula_installers.each do |fi| - fi.download_queue = download_queue - end + download_queue = T.let(download_queue || Homebrew::DownloadQueue.new(pour: true), Homebrew::DownloadQueue) + + if show_downloads_heading + formula_sentence = formulae_names_to_install.map { |name| Formatter.identifier(name) }.to_sentence + oh1 "Fetching downloads for: #{formula_sentence}", truncate: false + end + formula_installers.each do |fi| + fi.download_queue = download_queue end valid_formula_installers = formula_installers.dup begin - [:prelude_fetch, :prelude, :fetch].each do |step| + [:prelude_fetch, :prelude, :enqueue_fetch].each do |step| valid_formula_installers.select! do |fi| fi.public_send(step) true @@ -357,15 +372,43 @@ def fetch_formulae(formula_installers) ofail "#{fi.formula}: #{e}" false end - download_queue&.fetch + next if step == :enqueue_fetch && !fetch_after_enqueue + + download_queue.fetch end ensure - download_queue&.shutdown + download_queue.shutdown if shutdown_download_queue end valid_formula_installers end + sig { params(formula_installers: T::Array[FormulaInstaller], download_queue: Homebrew::DownloadQueue).returns(T::Array[FormulaInstaller]) } + def enqueue_formulae(formula_installers, download_queue:) + fetch_formulae( + formula_installers, + download_queue:, + fetch_after_enqueue: false, + shutdown_download_queue: false, + show_downloads_heading: false, + ) + end + + sig { params(formula_names: T::Array[String], cask_names: T::Array[String]).void } + def show_combined_fetch_downloads_heading(formula_names: [], cask_names: []) + combined_fetch_targets = formula_names.map { |name| Formatter.identifier(name) } + + cask_names.map { |name| Formatter.identifier(name) } + return if combined_fetch_targets.empty? + + oh1 "Fetching downloads for: #{combined_fetch_targets.to_sentence}", truncate: false + end + + sig { params(cask_installers: T::Array[T.untyped]).void } + def enqueue_cask_installers(cask_installers) + cask_installers.each(&:prelude) + cask_installers.each(&:enqueue_downloads) + end + sig { params(formula_installers: T::Array[FormulaInstaller], installed_on_request: T::Boolean, installed_as_dependency: T::Boolean, build_bottle: T::Boolean, force_bottle: T::Boolean, diff --git a/Library/Homebrew/upgrade.rb b/Library/Homebrew/upgrade.rb index f6a27f1877b33..cee01cb8c39c8 100644 --- a/Library/Homebrew/upgrade.rb +++ b/Library/Homebrew/upgrade.rb @@ -112,9 +112,12 @@ def formula_installers( end end - sig { params(formula_installers: T::Array[FormulaInstaller], dry_run: T::Boolean, verbose: T::Boolean).void } - def upgrade_formulae(formula_installers, dry_run: false, verbose: false) - valid_formula_installers = if dry_run + sig { + params(formula_installers: T::Array[FormulaInstaller], dry_run: T::Boolean, verbose: T::Boolean, + fetch: T::Boolean).void + } + def upgrade_formulae(formula_installers, dry_run: false, verbose: false, fetch: true) + valid_formula_installers = if dry_run || !fetch formula_installers else Install.fetch_formulae(formula_installers)