Skip to content

Commit c6d84e1

Browse files
committed
Cleanup partial download queue installations on error/interrupt
This will port the similar logic from `formula_installer.rb` that handles errors or manual user interrupts from producing partial installations. I noticed this code is almost identical (with some missing edge-cases) in `formula_installer.rb` to `Keg#uninstall` so I added a new `Keg#uninstall_ignore_interrupts_and_raise_failures!` method and made both `formula_installer.rb` and `retryable_download.rb` use it.
1 parent f9553b6 commit c6d84e1

File tree

3 files changed

+29
-12
lines changed

3 files changed

+29
-12
lines changed

Library/Homebrew/formula_installer.rb

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -593,17 +593,7 @@ def install
593593
pour
594594
# Catch any other types of exceptions as they leave us with nothing installed.
595595
rescue Exception # rubocop:disable Lint/RescueException
596-
ignore_interrupts do
597-
begin
598-
FileUtils.rm_r(formula.prefix) if formula.prefix.directory?
599-
rescue Errno::EACCES, Errno::ENOTEMPTY
600-
odie <<~EOS
601-
Could not remove #{formula.prefix.basename} keg! Do so manually:
602-
sudo rm -rf #{formula.prefix}
603-
EOS
604-
end
605-
formula.rack.rmdir_if_possible
606-
end
596+
Keg.new(formula.prefix).uninstall_ignore_interrupts_and_raise_failures! if formula.prefix.directory?
607597
raise
608598
else
609599
@poured_bottle = true

Library/Homebrew/keg.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ def remove_opt_record
279279
opt_record.parent.rmdir_if_possible
280280
end
281281

282+
sig { params(raise_failures: T::Boolean).void }
282283
def uninstall(raise_failures: false)
283284
CacheStoreDatabase.use(:linkage) do |db|
284285
break unless db.created?
@@ -301,6 +302,13 @@ def uninstall(raise_failures: false)
301302
EOS
302303
end
303304

305+
sig { void }
306+
def uninstall_ignore_interrupts_and_raise_failures!
307+
ignore_interrupts do
308+
uninstall(raise_failures: true)
309+
end
310+
end
311+
304312
def unlink(verbose: false, dry_run: false)
305313
ObserverPathnameExtension.reset_counts!
306314

Library/Homebrew/retryable_download.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ def fetch(verify_download_integrity: true, timeout: nil, quiet: false)
8080

8181
if pour && downloadable.is_a?(Bottle)
8282
HOMEBREW_CELLAR.mkpath
83+
84+
bottle_filename = T.cast(downloadable, Bottle).filename
85+
bottle_keg = HOMEBREW_CELLAR/bottle_filename.name/bottle_filename.version.to_s
86+
8387
UnpackStrategy.detect(download, prioritize_extension: true)
8488
.extract_nestedly(to: HOMEBREW_CELLAR)
8589
elsif json_download
@@ -89,7 +93,10 @@ def fetch(verify_download_integrity: true, timeout: nil, quiet: false)
8993
download
9094
rescue DownloadError, ChecksumMismatchError, Resource::BottleManifest::Error
9195
tries_remaining = @tries - @try
92-
raise if tries_remaining.zero?
96+
if tries_remaining.zero?
97+
cleanup_partial_installation_on_error!(bottle_keg)
98+
raise
99+
end
93100

94101
wait = 2 ** @try
95102
unless quiet
@@ -100,6 +107,10 @@ def fetch(verify_download_integrity: true, timeout: nil, quiet: false)
100107

101108
downloadable.clear_cache
102109
retry
110+
# Catch any other types of exceptions as they leave us with partial installations.
111+
rescue Exception # rubocop:disable Lint/RescueException
112+
cleanup_partial_installation_on_error!(bottle_keg)
113+
raise
103114
end
104115

105116
sig { override.params(filename: Pathname).void }
@@ -112,5 +123,13 @@ def verify_download_integrity(filename) = downloadable.verify_download_integrity
112123

113124
sig { returns(T::Boolean) }
114125
attr_reader :pour
126+
127+
sig { params(path: T.nilable(Pathname)).void }
128+
def cleanup_partial_installation_on_error!(path)
129+
return if path.nil?
130+
return unless path.directory?
131+
132+
Keg.new(path).uninstall_ignore_interrupts_and_raise_failures!
133+
end
115134
end
116135
end

0 commit comments

Comments
 (0)