Add Cask::Installer#prelude to check before download queueing

Fixes #20374

When using HOMEBREW_DOWNLOAD_CONCURRENCY, cask binaries were being
downloaded before checking if the cask could actually be installed
(e.g., disabled casks or conflict checks). This resulted in unnecessary
downloads for casks that would ultimately fail to install.

This change adds a `prelude` method to Cask::Installer that performs
early validation checks (deprecation/disable status and conflicts)
similar to Formula#prelude_fetch. The prelude method is called before
enqueueing downloads in all download queue scenarios (install, reinstall,
and upgrade commands), ensuring that validation failures occur before
the "Fetching downloads for:" message is displayed.

Key changes:
- Add Cask::Installer#prelude method with @ran_prelude tracking
- Call prelude before enqueueing downloads in install/reinstall/upgrade
- Refactor to avoid creating installer objects multiple times
- Maintain backward compatibility for non-download-queue scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mike McQuaid 2025-08-05 14:38:24 +01:00
parent a7ea27a9c8
commit 64311c2889
No known key found for this signature in database
4 changed files with 31 additions and 12 deletions

View File

@ -46,6 +46,7 @@ module Cask
@verify_download_integrity = verify_download_integrity @verify_download_integrity = verify_download_integrity
@quiet = quiet @quiet = quiet
@download_queue = download_queue @download_queue = download_queue
@ran_prelude = T.let(false, T::Boolean)
end end
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
@ -140,8 +141,7 @@ module Cask
old_config = @cask.config old_config = @cask.config
predecessor = @cask if reinstall? && @cask.installed? predecessor = @cask if reinstall? && @cask.installed?
check_deprecate_disable prelude
check_conflicts
print caveats print caveats
fetch fetch
@ -791,6 +791,16 @@ on_request: true)
) )
end end
sig { void }
def prelude
return if @ran_prelude
check_deprecate_disable
check_conflicts
@ran_prelude = true
end
sig { void } sig { void }
def enqueue_downloads def enqueue_downloads
download_queue = @download_queue download_queue = @download_queue

View File

@ -30,6 +30,8 @@ module Cask
end end
if download_queue if download_queue
cask_installers.each(&:prelude)
oh1 "Fetching downloads for: #{casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence}", oh1 "Fetching downloads for: #{casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence}",
truncate: false truncate: false
cask_installers.each(&:enqueue_downloads) cask_installers.each(&:enqueue_downloads)

View File

@ -115,19 +115,22 @@ module Cask
download_queue = Homebrew::DownloadQueue.new(pour: true) download_queue = Homebrew::DownloadQueue.new(pour: true)
fetchable_casks = upgradable_casks.map(&:last) fetchable_casks = upgradable_casks.map(&:last)
fetchable_casks_sentence = fetchable_casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence fetchable_cask_installers = fetchable_casks.map do |cask|
oh1 "Fetching downloads for: #{fetchable_casks_sentence}", truncate: false
fetchable_casks.each do |cask|
# This is significantly easier given the weird difference in Sorbet signatures here. # This is significantly easier given the weird difference in Sorbet signatures here.
# rubocop:disable Style/DoubleNegation # rubocop:disable Style/DoubleNegation
Installer.new(cask, binaries: !!binaries, verbose: !!verbose, force: !!force, Installer.new(cask, binaries: !!binaries, verbose: !!verbose, force: !!force,
skip_cask_deps: !!skip_cask_deps, require_sha: !!require_sha, skip_cask_deps: !!skip_cask_deps, require_sha: !!require_sha,
upgrade: true, quarantine:, download_queue:) upgrade: true, quarantine:, download_queue:)
.enqueue_downloads
# rubocop:enable Style/DoubleNegation # rubocop:enable Style/DoubleNegation
end end
fetchable_cask_installers.each(&:prelude)
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(&:enqueue_downloads)
download_queue.fetch download_queue.fetch
end end

View File

@ -248,17 +248,21 @@ module Homebrew
fetch_casks = Homebrew::EnvConfig.no_install_upgrade? ? new_casks : casks fetch_casks = Homebrew::EnvConfig.no_install_upgrade? ? new_casks : casks
if download_queue if download_queue
fetch_casks_sentence = fetch_casks.map { |cask| Formatter.identifier(cask.full_name) }.to_sentence fetch_cask_installers = fetch_casks.map do |cask|
oh1 "Fetching downloads for: #{fetch_casks_sentence}", truncate: false
fetch_casks.each do |cask|
Cask::Installer.new(cask, binaries: args.binaries?, verbose: args.verbose?, Cask::Installer.new(cask, binaries: args.binaries?, verbose: args.verbose?,
force: args.force?, skip_cask_deps: args.skip_cask_deps?, force: args.force?, skip_cask_deps: args.skip_cask_deps?,
require_sha: args.require_sha?, reinstall: true, require_sha: args.require_sha?, reinstall: true,
quarantine: args.quarantine?, zap: args.zap?, download_queue:) quarantine: args.quarantine?, zap: args.zap?, download_queue:)
.enqueue_downloads
end 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 download_queue.fetch
end end