| 
									
										
										
										
											2024-08-12 10:30:59 +01:00
										 |  |  | # typed: true # rubocop:todo Sorbet/StrictSigil | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-25 17:57:51 +01:00
										 |  |  | require "utils/bottles" | 
					
						
							| 
									
										
										
										
											2020-12-09 10:36:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-28 11:45:18 -08:00
										 |  |  | require "attrable" | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  | require "formula" | 
					
						
							| 
									
										
										
										
											2018-09-03 19:39:07 +01:00
										 |  |  | require "cask/cask_loader" | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  | module Homebrew | 
					
						
							|  |  |  |   # Helper class for cleaning up the Homebrew cache. | 
					
						
							|  |  |  |   class Cleanup | 
					
						
							| 
									
										
										
										
											2020-12-09 10:36:02 +00:00
										 |  |  |     CLEANUP_DEFAULT_DAYS = Homebrew::EnvConfig.cleanup_periodic_full_days.to_i.freeze | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  |     private_constant :CLEANUP_DEFAULT_DAYS | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |     class << self | 
					
						
							|  |  |  |       sig { params(pathname: Pathname).returns(T::Boolean) } | 
					
						
							|  |  |  |       def incomplete?(pathname) | 
					
						
							|  |  |  |         pathname.extname.end_with?(".incomplete") | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       sig { params(pathname: Pathname).returns(T::Boolean) } | 
					
						
							|  |  |  |       def nested_cache?(pathname) | 
					
						
							|  |  |  |         pathname.directory? && %w[
 | 
					
						
							|  |  |  |           cargo_cache | 
					
						
							|  |  |  |           go_cache | 
					
						
							|  |  |  |           go_mod_cache | 
					
						
							|  |  |  |           glide_home | 
					
						
							|  |  |  |           java_cache | 
					
						
							|  |  |  |           npm_cache | 
					
						
							| 
									
										
										
										
											2023-11-22 13:11:53 -08:00
										 |  |  |           pip_cache | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |           gclient_cache | 
					
						
							|  |  |  |         ].include?(pathname.basename.to_s) | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       sig { params(pathname: Pathname).returns(T::Boolean) } | 
					
						
							|  |  |  |       def go_cache_directory?(pathname) | 
					
						
							|  |  |  |         # Go makes its cache contents read-only to ensure cache integrity, | 
					
						
							|  |  |  |         # which makes sense but is something we need to undo for cleanup. | 
					
						
							|  |  |  |         pathname.directory? && %w[go_cache go_mod_cache].include?(pathname.basename.to_s) | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       sig { params(pathname: Pathname, days: T.nilable(Integer)).returns(T::Boolean) } | 
					
						
							|  |  |  |       def prune?(pathname, days) | 
					
						
							|  |  |  |         return false unless days | 
					
						
							|  |  |  |         return true if days.zero? | 
					
						
							|  |  |  |         return true if pathname.symlink? && !pathname.exist? | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         days_ago = (DateTime.now - days).to_time | 
					
						
							|  |  |  |         pathname.mtime < days_ago && pathname.ctime < days_ago | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-09-01 06:42:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |       sig { params(entry: { path: Pathname, type: T.nilable(Symbol) }, scrub: T::Boolean).returns(T::Boolean) } | 
					
						
							|  |  |  |       def stale?(entry, scrub: false) | 
					
						
							|  |  |  |         pathname = entry[:path] | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         return false unless pathname.resolved_path.file? | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |         case entry[:type] | 
					
						
							|  |  |  |         when :api_source | 
					
						
							|  |  |  |           stale_api_source?(pathname, scrub) | 
					
						
							|  |  |  |         when :cask | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |           stale_cask?(pathname, scrub) | 
					
						
							| 
									
										
										
										
											2023-05-17 13:27:10 +08:00
										 |  |  |         when :gh_actions_artifact | 
					
						
							|  |  |  |           stale_gh_actions_artifact?(pathname, scrub) | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         else | 
					
						
							|  |  |  |           stale_formula?(pathname, scrub) | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       private | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-17 13:27:10 +08:00
										 |  |  |       GH_ACTIONS_ARTIFACT_CLEANUP_DAYS = 3
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) } | 
					
						
							|  |  |  |       def stale_gh_actions_artifact?(pathname, scrub) | 
					
						
							|  |  |  |         scrub || prune?(pathname, GH_ACTIONS_ARTIFACT_CLEANUP_DAYS) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |       sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) } | 
					
						
							|  |  |  |       def stale_api_source?(pathname, scrub) | 
					
						
							|  |  |  |         return true if scrub | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         org, repo, git_head, type, basename = pathname.each_filename.to_a.last(5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         name = "#{org}/#{repo}/#{File.basename(T.must(basename), ".rb")}" | 
					
						
							|  |  |  |         package = if type == "Cask" | 
					
						
							|  |  |  |           begin | 
					
						
							|  |  |  |             Cask::CaskLoader.load(name) | 
					
						
							|  |  |  |           rescue Cask::CaskError | 
					
						
							|  |  |  |             nil | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           begin | 
					
						
							|  |  |  |             Formulary.factory(name) | 
					
						
							|  |  |  |           rescue FormulaUnavailableError | 
					
						
							|  |  |  |             nil | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         return true if package.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         package.tap_git_head != git_head | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) } | 
					
						
							|  |  |  |       def stale_formula?(pathname, scrub) | 
					
						
							|  |  |  |         return false unless HOMEBREW_CELLAR.directory? | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         version = if HOMEBREW_BOTTLES_EXTNAME_REGEX.match?(to_s) | 
					
						
							|  |  |  |           begin | 
					
						
							| 
									
										
										
										
											2024-06-11 23:03:32 +01:00
										 |  |  |             Utils::Bottles.resolve_version(pathname).to_s | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |           rescue | 
					
						
							|  |  |  |             nil | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         end | 
					
						
							|  |  |  |         basename_str = pathname.basename.to_s | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         version ||= basename_str[/\A.*(?:--.*?)*--(.*?)#{Regexp.escape(pathname.extname)}\Z/, 1] | 
					
						
							|  |  |  |         version ||= basename_str[/\A.*--?(.*?)#{Regexp.escape(pathname.extname)}\Z/, 1] | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 13:44:26 +01:00
										 |  |  |         return false if version.blank? | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         version = Version.new(version) | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         unless (formula_name = basename_str[/\A(.*?)(?:--.*?)*--?(?:#{Regexp.escape(version.to_s)})/, 1]) | 
					
						
							|  |  |  |           return false | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-11-10 00:11:22 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         formula = begin | 
					
						
							|  |  |  |           Formulary.from_rack(HOMEBREW_CELLAR/formula_name) | 
					
						
							| 
									
										
										
										
											2024-02-16 21:27:02 +01:00
										 |  |  |         rescue FormulaUnavailableError, TapFormulaAmbiguityError | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |           nil | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-30 03:30:58 +00:00
										 |  |  |         if formula.blank? && formula_name.delete_suffix!("_bottle_manifest") | 
					
						
							|  |  |  |           formula = begin | 
					
						
							|  |  |  |             Formulary.from_rack(HOMEBREW_CELLAR/formula_name) | 
					
						
							|  |  |  |           rescue FormulaUnavailableError, TapFormulaAmbiguityError | 
					
						
							|  |  |  |             nil | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2024-04-01 14:36:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |           return false if formula.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # We can't determine an installed rebuild and parsing manifest version cannot be reliably done. | 
					
						
							|  |  |  |           return false unless formula.latest_version_installed? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return true if (bottle = formula.bottle).blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return version != GitHubPackages.version_rebuild(bottle.resource.version, bottle.rebuild) | 
					
						
							| 
									
										
										
										
											2024-03-30 03:30:58 +00:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         return false if formula.blank? | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         resource_name = basename_str[/\A.*?--(.*?)--?(?:#{Regexp.escape(version.to_s)})/, 1] | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         if resource_name == "patch" | 
					
						
							|  |  |  |           patch_hashes = formula.stable&.patches&.select(&:external?)&.map(&:resource)&.map(&:version) | 
					
						
							|  |  |  |           return true unless patch_hashes&.include?(Checksum.new(version.to_s)) | 
					
						
							|  |  |  |         elsif resource_name && (resource_version = formula.stable&.resources&.dig(resource_name)&.version) | 
					
						
							|  |  |  |           return true if resource_version != version | 
					
						
							| 
									
										
										
										
											2024-04-01 14:36:39 +01:00
										 |  |  |         elsif (formula.latest_version_installed? && formula.pkg_version.to_s != version) || | 
					
						
							|  |  |  |               formula.pkg_version.to_s > version | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |           return true | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         return true if scrub && !formula.latest_version_installed? | 
					
						
							|  |  |  |         return true if Utils::Bottles.file_outdated?(formula, pathname) | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         false | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       sig { params(pathname: Pathname, scrub: T::Boolean).returns(T::Boolean) } | 
					
						
							|  |  |  |       def stale_cask?(pathname, scrub) | 
					
						
							|  |  |  |         basename = pathname.basename | 
					
						
							|  |  |  |         return false unless (name = basename.to_s[/\A(.*?)--/, 1]) | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         cask = begin | 
					
						
							|  |  |  |           Cask::CaskLoader.load(name) | 
					
						
							|  |  |  |         rescue Cask::CaskError | 
					
						
							|  |  |  |           nil | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         return false if cask.blank? | 
					
						
							|  |  |  |         return true unless basename.to_s.match?(/\A#{Regexp.escape(name)}--#{Regexp.escape(cask.version)}\b/) | 
					
						
							| 
									
										
										
										
											2023-04-08 14:10:58 +02:00
										 |  |  |         return true if scrub && cask.installed_version != cask.version | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if cask.version.latest? | 
					
						
							|  |  |  |           cleanup_threshold = (DateTime.now - CLEANUP_DEFAULT_DAYS).to_time | 
					
						
							|  |  |  |           return pathname.mtime < cleanup_threshold && pathname.ctime < cleanup_threshold | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         false | 
					
						
							| 
									
										
										
										
											2020-08-17 03:52:17 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-28 11:45:18 -08:00
										 |  |  |     extend Attrable | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:46:20 +09:00
										 |  |  |     PERIODIC_CLEAN_FILE = (HOMEBREW_CACHE/".cleaned").freeze | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-31 13:40:49 +00:00
										 |  |  |     attr_predicate :dry_run?, :scrub?, :prune? | 
					
						
							| 
									
										
										
										
											2020-07-07 11:29:33 +01:00
										 |  |  |     attr_reader :args, :days, :cache, :disk_cleanup_size | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def initialize(*args, dry_run: false, scrub: false, days: nil, cache: HOMEBREW_CACHE) | 
					
						
							|  |  |  |       @disk_cleanup_size = 0
 | 
					
						
							|  |  |  |       @args = args | 
					
						
							|  |  |  |       @dry_run = dry_run | 
					
						
							|  |  |  |       @scrub = scrub | 
					
						
							| 
									
										
										
										
											2020-12-31 13:40:49 +00:00
										 |  |  |       @prune = days.present? | 
					
						
							| 
									
										
										
										
											2020-04-20 10:33:15 +01:00
										 |  |  |       @days = days || Homebrew::EnvConfig.cleanup_max_age_days.to_i | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |       @cache = cache | 
					
						
							| 
									
										
										
										
											2018-08-10 00:54:03 +02:00
										 |  |  |       @cleaned_up_paths = Set.new | 
					
						
							| 
									
										
										
										
											2017-08-13 04:21:07 +05:30
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |     def self.install_formula_clean!(formula, dry_run: false) | 
					
						
							| 
									
										
										
										
											2020-04-05 15:44:50 +01:00
										 |  |  |       return if Homebrew::EnvConfig.no_install_cleanup? | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |       return unless formula.latest_version_installed? | 
					
						
							|  |  |  |       return if skip_clean_formula?(formula) | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |       if dry_run | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |         ohai "Would run `brew cleanup #{formula}`" | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |         ohai "Running `brew cleanup #{formula}`..." | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       puts_no_install_cleanup_disable_message_if_not_already! | 
					
						
							|  |  |  |       return if dry_run | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |       Cleanup.new.cleanup_formula(formula) | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:52:37 -07:00
										 |  |  |     def self.puts_no_install_cleanup_disable_message | 
					
						
							| 
									
										
										
										
											2021-11-26 13:14:10 +00:00
										 |  |  |       return if Homebrew::EnvConfig.no_env_hints? | 
					
						
							|  |  |  |       return if Homebrew::EnvConfig.no_install_cleanup? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       puts "Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP." | 
					
						
							|  |  |  |       puts "Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`)." | 
					
						
							| 
									
										
										
										
											2022-07-20 08:52:37 -07:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def self.puts_no_install_cleanup_disable_message_if_not_already! | 
					
						
							|  |  |  |       return if @puts_no_install_cleanup_disable_message_if_not_already | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       puts_no_install_cleanup_disable_message | 
					
						
							| 
									
										
										
										
											2021-11-26 13:14:10 +00:00
										 |  |  |       @puts_no_install_cleanup_disable_message_if_not_already = true | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |     def self.skip_clean_formula?(formula) | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       no_cleanup_formula = Homebrew::EnvConfig.no_cleanup_formulae | 
					
						
							|  |  |  |       return false if no_cleanup_formula.blank? | 
					
						
							| 
									
										
										
										
											2021-08-27 18:00:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |       @skip_clean_formulae ||= no_cleanup_formula.split(",") | 
					
						
							| 
									
										
										
										
											2024-01-18 14:11:43 +00:00
										 |  |  |       @skip_clean_formulae.include?(formula.name) || @skip_clean_formulae.intersect?(formula.aliases) | 
					
						
							| 
									
										
										
										
											2021-08-27 18:00:43 +08:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |     def self.periodic_clean_due? | 
					
						
							| 
									
										
										
										
											2020-04-05 15:44:50 +01:00
										 |  |  |       return false if Homebrew::EnvConfig.no_install_cleanup? | 
					
						
							| 
									
										
										
										
											2020-05-07 09:58:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       unless PERIODIC_CLEAN_FILE.exist? | 
					
						
							| 
									
										
										
										
											2020-11-14 10:48:10 -05:00
										 |  |  |         HOMEBREW_CACHE.mkpath | 
					
						
							| 
									
										
										
										
											2020-05-07 09:58:27 +01:00
										 |  |  |         FileUtils.touch PERIODIC_CLEAN_FILE | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-04 13:51:35 -08:00
										 |  |  |       PERIODIC_CLEAN_FILE.mtime < (DateTime.now - CLEANUP_DEFAULT_DAYS).to_time | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |     def self.periodic_clean!(dry_run: false) | 
					
						
							|  |  |  |       return if Homebrew::EnvConfig.no_install_cleanup? | 
					
						
							|  |  |  |       return unless periodic_clean_due? | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |       if dry_run | 
					
						
							|  |  |  |         oh1 "Would run `brew cleanup` which has not been run in the last #{CLEANUP_DEFAULT_DAYS} days" | 
					
						
							| 
									
										
										
										
											2021-08-31 12:27:14 -04:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |         oh1 "`brew cleanup` has not been run in the last #{CLEANUP_DEFAULT_DAYS} days, running now..." | 
					
						
							| 
									
										
										
										
											2021-08-31 12:27:14 -04:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2021-11-25 09:10:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:52:37 -07:00
										 |  |  |       puts_no_install_cleanup_disable_message | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |       return if dry_run | 
					
						
							| 
									
										
										
										
											2021-11-25 09:10:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 08:24:55 -07:00
										 |  |  |       Cleanup.new.clean!(quiet: true, periodic: true) | 
					
						
							| 
									
										
										
										
											2019-01-03 16:23:44 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 12:56:36 +00:00
										 |  |  |     def clean!(quiet: false, periodic: false) | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |       if args.empty? | 
					
						
							| 
									
										
										
										
											2021-08-27 18:00:43 +08:00
										 |  |  |         Formula.installed | 
					
						
							|  |  |  |                .sort_by(&:name) | 
					
						
							| 
									
										
										
										
											2022-07-15 16:27:22 -07:00
										 |  |  |                .reject { |f| Cleanup.skip_clean_formula?(f) } | 
					
						
							| 
									
										
										
										
											2021-08-27 18:00:43 +08:00
										 |  |  |                .each do |formula| | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |           cleanup_formula(formula, quiet:, ds_store: false, cache_db: false) | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-26 15:39:45 +01:00
										 |  |  |         if ENV["HOMEBREW_AUTOREMOVE"].present? | 
					
						
							|  |  |  |           opoo "HOMEBREW_AUTOREMOVE is now a no-op as it is the default behaviour. " \ | 
					
						
							|  |  |  |                "Set HOMEBREW_NO_AUTOREMOVE=1 to disable it." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         Cleanup.autoremove(dry_run: dry_run?) unless Homebrew::EnvConfig.no_autoremove? | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |         cleanup_cache | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |         cleanup_empty_api_source_directories | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |         cleanup_logs | 
					
						
							| 
									
										
										
										
											2018-10-14 00:13:04 +02:00
										 |  |  |         cleanup_lockfiles | 
					
						
							| 
									
										
										
										
											2022-08-18 21:09:12 +08:00
										 |  |  |         cleanup_python_site_packages | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |         prune_prefix_symlinks_and_directories | 
					
						
							| 
									
										
										
										
											2019-02-13 12:56:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         unless dry_run? | 
					
						
							| 
									
										
										
										
											2020-09-11 12:56:15 +01:00
										 |  |  |           cleanup_cache_db | 
					
						
							| 
									
										
										
										
											2019-02-13 12:56:36 +00:00
										 |  |  |           rm_ds_store | 
					
						
							|  |  |  |           HOMEBREW_CACHE.mkpath | 
					
						
							|  |  |  |           FileUtils.touch PERIODIC_CLEAN_FILE | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Cleaning up Ruby needs to be done last to avoid requiring additional | 
					
						
							|  |  |  |         # files afterwards. Additionally, don't allow it on periodic cleans to | 
					
						
							|  |  |  |         # avoid having to try to do a `brew install` when we've just deleted | 
					
						
							|  |  |  |         # the running Ruby process... | 
					
						
							|  |  |  |         return if periodic | 
					
						
							| 
									
										
										
										
											2019-02-19 13:12:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 12:56:36 +00:00
										 |  |  |         cleanup_portable_ruby | 
					
						
							| 
									
										
										
										
											2021-02-02 11:51:08 +00:00
										 |  |  |         cleanup_bootsnap | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |       else | 
					
						
							|  |  |  |         args.each do |arg| | 
					
						
							|  |  |  |           formula = begin | 
					
						
							| 
									
										
										
										
											2018-09-11 17:44:18 +02:00
										 |  |  |             Formulary.resolve(arg) | 
					
						
							| 
									
										
										
										
											2024-02-16 21:27:02 +01:00
										 |  |  |           rescue FormulaUnavailableError, TapFormulaAmbiguityError | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |             nil | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           cask = begin | 
					
						
							| 
									
										
										
										
											2018-09-06 08:29:14 +02:00
										 |  |  |             Cask::CaskLoader.load(arg) | 
					
						
							| 
									
										
										
										
											2019-11-06 13:33:33 +00:00
										 |  |  |           rescue Cask::CaskError | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |             nil | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2017-03-21 04:13:13 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 16:27:22 -07:00
										 |  |  |           if formula && Cleanup.skip_clean_formula?(formula) | 
					
						
							| 
									
										
										
										
											2021-08-27 18:00:43 +08:00
										 |  |  |             onoe "Refusing to clean #{formula} because it is listed in " \ | 
					
						
							|  |  |  |                  "#{Tty.bold}HOMEBREW_NO_CLEANUP_FORMULAE#{Tty.reset}!" | 
					
						
							|  |  |  |           elsif formula | 
					
						
							|  |  |  |             cleanup_formula(formula) | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |           cleanup_cask(cask) if cask | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-21 04:13:13 -05:00
										 |  |  |     def unremovable_kegs | 
					
						
							|  |  |  |       @unremovable_kegs ||= [] | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-11 12:56:15 +01:00
										 |  |  |     def cleanup_formula(formula, quiet: false, ds_store: true, cache_db: true) | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |       formula.eligible_kegs_for_cleanup(quiet:) | 
					
						
							| 
									
										
										
										
											2024-04-08 09:47:06 -07:00
										 |  |  |              .each { cleanup_keg(_1) } | 
					
						
							| 
									
										
										
										
											2024-03-30 03:30:58 +00:00
										 |  |  |       cleanup_cache(Pathname.glob(cache/"#{formula.name}{_bottle_manifest,}--*").map { |path| { path:, type: nil } }) | 
					
						
							| 
									
										
										
										
											2019-09-18 11:39:40 +01:00
										 |  |  |       rm_ds_store([formula.rack]) if ds_store | 
					
						
							| 
									
										
										
										
											2020-09-11 12:56:15 +01:00
										 |  |  |       cleanup_cache_db(formula.rack) if cache_db | 
					
						
							| 
									
										
										
										
											2018-08-09 16:46:39 +02:00
										 |  |  |       cleanup_lockfiles(FormulaLock.new(formula.name).path) | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-18 11:39:40 +01:00
										 |  |  |     def cleanup_cask(cask, ds_store: true) | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |       cleanup_cache(Pathname.glob(cache/"Cask/#{cask.token}--*").map { |path| { path:, type: :cask } }) | 
					
						
							| 
									
										
										
										
											2019-09-18 11:39:40 +01:00
										 |  |  |       rm_ds_store([cask.caskroom_path]) if ds_store | 
					
						
							| 
									
										
										
										
											2018-08-09 16:46:39 +02:00
										 |  |  |       cleanup_lockfiles(CaskLock.new(cask.token).path) | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-21 04:13:13 -05:00
										 |  |  |     def cleanup_keg(keg) | 
					
						
							| 
									
										
										
										
											2021-01-21 13:03:52 +00:00
										 |  |  |       cleanup_path(keg) { keg.uninstall(raise_failures: true) } | 
					
						
							| 
									
										
										
										
											2021-10-19 23:39:58 +09:00
										 |  |  |     rescue Errno::EACCES, Errno::ENOTEMPTY => e | 
					
						
							| 
									
										
										
										
											2017-03-21 04:13:13 -05:00
										 |  |  |       opoo e.message | 
					
						
							|  |  |  |       unremovable_kegs << keg | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def cleanup_logs | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |       return unless HOMEBREW_LOGS.directory? | 
					
						
							| 
									
										
										
										
											2019-02-19 13:12:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-02 19:18:51 +00:00
										 |  |  |       logs_days = [days, CLEANUP_DEFAULT_DAYS].min | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |       HOMEBREW_LOGS.subdirs.each do |dir| | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         cleanup_path(dir) { dir.rmtree } if self.class.prune?(dir, logs_days) | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |     def cache_files | 
					
						
							|  |  |  |       files = cache.directory? ? cache.children : [] | 
					
						
							|  |  |  |       cask_files = (cache/"Cask").directory? ? (cache/"Cask").children : [] | 
					
						
							| 
									
										
										
										
											2024-04-30 11:10:23 +02:00
										 |  |  |       api_source_files = (cache/"api-source").glob("*/*/*/*/*") # `<org>/<repo>/<git_head>/<type>/<token>.rb` | 
					
						
							| 
									
										
										
										
											2023-05-17 13:27:10 +08:00
										 |  |  |       gh_actions_artifacts = (cache/"gh-actions-artifact").directory? ? (cache/"gh-actions-artifact").children : [] | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |       files.map { |path| { path:, type: nil } } + | 
					
						
							|  |  |  |         cask_files.map { |path| { path:, type: :cask } } + | 
					
						
							|  |  |  |         api_source_files.map { |path| { path:, type: :api_source } } + | 
					
						
							|  |  |  |         gh_actions_artifacts.map { |path| { path:, type: :gh_actions_artifact } } | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def cleanup_empty_api_source_directories(directory = cache/"api-source") | 
					
						
							|  |  |  |       return if dry_run? | 
					
						
							|  |  |  |       return unless directory.directory? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       directory.each_child do |child| | 
					
						
							|  |  |  |         next unless child.directory? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cleanup_empty_api_source_directories(child) | 
					
						
							|  |  |  |         child.rmdir if child.empty? | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-10 04:11:54 +02:00
										 |  |  |     def cleanup_unreferenced_downloads | 
					
						
							|  |  |  |       return if dry_run? | 
					
						
							|  |  |  |       return unless (cache/"downloads").directory? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-14 00:13:04 +02:00
										 |  |  |       downloads = (cache/"downloads").children | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |       referenced_downloads = cache_files.map { |file| file[:path] }.select(&:symlink?).map(&:resolved_path) | 
					
						
							| 
									
										
										
										
											2018-08-10 04:11:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-14 00:13:04 +02:00
										 |  |  |       (downloads - referenced_downloads).each do |download| | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         if self.class.incomplete?(download) | 
					
						
							| 
									
										
										
										
											2018-10-14 00:13:04 +02:00
										 |  |  |           begin | 
					
						
							| 
									
										
										
										
											2024-07-30 17:51:02 +01:00
										 |  |  |             DownloadLock.new(download).with_lock do | 
					
						
							| 
									
										
										
										
											2018-10-14 00:13:04 +02:00
										 |  |  |               download.unlink | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           rescue OperationInProgressError | 
					
						
							|  |  |  |             # Skip incomplete downloads which are still in progress. | 
					
						
							|  |  |  |             next | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2019-01-28 16:08:23 +00:00
										 |  |  |         elsif download.directory? | 
					
						
							|  |  |  |           FileUtils.rm_rf download | 
					
						
							| 
									
										
										
										
											2018-10-14 00:13:04 +02:00
										 |  |  |         else | 
					
						
							|  |  |  |           download.unlink | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-10 04:11:54 +02:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  |     def cleanup_cache(entries = nil) | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |       entries ||= cache_files | 
					
						
							| 
									
										
										
										
											2018-08-08 22:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |       entries.each do |entry| | 
					
						
							|  |  |  |         path = entry[:path] | 
					
						
							| 
									
										
										
										
											2019-04-17 21:06:47 +09:00
										 |  |  |         next if path == PERIODIC_CLEAN_FILE | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         FileUtils.chmod_R 0755, path if self.class.go_cache_directory?(path) && !dry_run? | 
					
						
							|  |  |  |         next cleanup_path(path) { path.unlink } if self.class.incomplete?(path) | 
					
						
							|  |  |  |         next cleanup_path(path) { FileUtils.rm_rf path } if self.class.nested_cache?(path) | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |         if self.class.prune?(path, days) | 
					
						
							| 
									
										
										
										
											2018-08-25 22:06:24 +02:00
										 |  |  |           if path.file? || path.symlink? | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |             cleanup_path(path) { path.unlink } | 
					
						
							|  |  |  |           elsif path.directory? && path.to_s.include?("--") | 
					
						
							|  |  |  |             cleanup_path(path) { FileUtils.rm_rf path } | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           next | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 11:20:01 +10:00
										 |  |  |         # If we've specified --prune don't do the (expensive) .stale? check. | 
					
						
							| 
									
										
										
										
											2023-04-18 00:22:13 +01:00
										 |  |  |         cleanup_path(path) { path.unlink } if !prune? && self.class.stale?(entry, scrub: scrub?) | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-08-10 04:11:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       cleanup_unreferenced_downloads | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-21 04:13:13 -05:00
										 |  |  |     def cleanup_path(path) | 
					
						
							| 
									
										
										
										
											2020-07-10 09:32:27 +01:00
										 |  |  |       return unless path.exist? | 
					
						
							| 
									
										
										
										
											2018-08-10 00:54:03 +02:00
										 |  |  |       return unless @cleaned_up_paths.add?(path) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-07 12:25:27 -05:00
										 |  |  |       @disk_cleanup_size += path.disk_usage | 
					
						
							| 
									
										
										
										
											2018-08-08 09:43:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 11:20:53 +02:00
										 |  |  |       if dry_run? | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |         puts "Would remove: #{path} (#{path.abv})" | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         puts "Removing: #{path}... (#{path.abv})" | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 16:46:39 +02:00
										 |  |  |     def cleanup_lockfiles(*lockfiles) | 
					
						
							| 
									
										
										
										
											2018-08-16 05:55:17 +02:00
										 |  |  |       return if dry_run? | 
					
						
							| 
									
										
										
										
											2018-08-09 16:46:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-19 13:11:32 +00:00
										 |  |  |       lockfiles = HOMEBREW_LOCKS.children.select(&:file?) if lockfiles.empty? && HOMEBREW_LOCKS.directory? | 
					
						
							| 
									
										
										
										
											2018-08-09 16:46:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |       lockfiles.each do |file| | 
					
						
							|  |  |  |         next unless file.readable? | 
					
						
							| 
									
										
										
										
											2018-08-09 16:46:39 +02:00
										 |  |  |         next unless file.open(File::RDWR).flock(File::LOCK_EX | File::LOCK_NB) | 
					
						
							| 
									
										
										
										
											2018-08-11 17:23:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         begin | 
					
						
							| 
									
										
										
										
											2018-08-16 05:55:17 +02:00
										 |  |  |           file.unlink | 
					
						
							| 
									
										
										
										
											2018-08-11 17:23:35 +02:00
										 |  |  |         ensure | 
					
						
							|  |  |  |           file.open(File::RDWR).flock(File::LOCK_UN) if file.exist? | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 19:26:49 +01:00
										 |  |  |     def cleanup_portable_ruby | 
					
						
							| 
									
										
										
										
											2020-09-03 09:43:41 +01:00
										 |  |  |       vendor_dir = HOMEBREW_LIBRARY/"Homebrew/vendor" | 
					
						
							| 
									
										
										
										
											2023-11-29 15:27:50 +00:00
										 |  |  |       portable_ruby_latest_version = (vendor_dir/"portable-ruby-version").read.chomp | 
					
						
							| 
									
										
										
										
											2020-09-03 09:43:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 16:04:24 +00:00
										 |  |  |       portable_rubies_to_remove = [] | 
					
						
							| 
									
										
										
										
											2020-09-03 09:43:41 +01:00
										 |  |  |       Pathname.glob(vendor_dir/"portable-ruby/*.*").select(&:directory?).each do |path| | 
					
						
							| 
									
										
										
										
											2022-11-20 10:49:53 -08:00
										 |  |  |         next if !use_system_ruby? && portable_ruby_latest_version == path.basename.to_s | 
					
						
							| 
									
										
										
										
											2019-02-19 13:12:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 16:04:24 +00:00
										 |  |  |         portable_rubies_to_remove << path | 
					
						
							| 
									
										
										
										
											2018-10-03 19:26:49 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 16:04:24 +00:00
										 |  |  |       return if portable_rubies_to_remove.empty? | 
					
						
							| 
									
										
										
										
											2018-10-03 19:26:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-07 22:28:03 +00:00
										 |  |  |       bundler_paths = (vendor_dir/"bundle/ruby").children.select do |child| | 
					
						
							|  |  |  |         basename = child.basename.to_s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         next false if basename == ".homebrew_gem_groups" | 
					
						
							|  |  |  |         next true unless child.directory? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         [ | 
					
						
							|  |  |  |           "#{Version.new(portable_ruby_latest_version).major_minor}.0", | 
					
						
							|  |  |  |           RbConfig::CONFIG["ruby_version"], | 
					
						
							|  |  |  |         ].uniq.exclude?(basename) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       bundler_paths.each do |bundler_path| | 
					
						
							|  |  |  |         if dry_run? | 
					
						
							|  |  |  |           puts Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "clean", "-nx", bundler_path).chomp | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           puts Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "clean", "-ffqx", bundler_path).chomp | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-10-03 19:26:49 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-01-16 16:04:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 21:25:17 +08:00
										 |  |  |       portable_rubies_to_remove.each do |portable_ruby| | 
					
						
							|  |  |  |         cleanup_path(portable_ruby) { portable_ruby.rmtree } | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-10-03 19:26:49 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-26 22:03:54 +01:00
										 |  |  |     def use_system_ruby? | 
					
						
							|  |  |  |       false | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2022-11-20 10:49:53 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 11:51:08 +00:00
										 |  |  |     def cleanup_bootsnap | 
					
						
							|  |  |  |       bootsnap = cache/"bootsnap" | 
					
						
							|  |  |  |       return unless bootsnap.exist? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 21:25:17 +08:00
										 |  |  |       cleanup_path(bootsnap) { bootsnap.rmtree } | 
					
						
							| 
									
										
										
										
											2021-02-02 11:51:08 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-11 12:56:15 +01:00
										 |  |  |     def cleanup_cache_db(rack = nil) | 
					
						
							| 
									
										
										
										
											2018-10-13 08:22:51 -07:00
										 |  |  |       FileUtils.rm_rf [ | 
					
						
							|  |  |  |         cache/"desc_cache.json", | 
					
						
							|  |  |  |         cache/"linkage.db", | 
					
						
							|  |  |  |         cache/"linkage.db.db", | 
					
						
							|  |  |  |       ] | 
					
						
							| 
									
										
										
										
											2020-09-11 12:56:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       CacheStoreDatabase.use(:linkage) do |db| | 
					
						
							|  |  |  |         break unless db.created? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         db.each_key do |keg| | 
					
						
							|  |  |  |           next if rack.present? && !keg.start_with?("#{rack}/") | 
					
						
							|  |  |  |           next if File.directory?(keg) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           LinkageCacheStore.new(keg, db).delete! | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-10-03 19:26:49 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 01:55:43 +02:00
										 |  |  |     def rm_ds_store(dirs = nil) | 
					
						
							| 
									
										
										
										
											2021-03-26 14:11:03 +00:00
										 |  |  |       dirs ||= Keg::MUST_EXIST_DIRECTORIES + [ | 
					
						
							|  |  |  |         HOMEBREW_PREFIX/"Caskroom", | 
					
						
							|  |  |  |       ] | 
					
						
							| 
									
										
										
										
											2019-09-18 11:39:40 +01:00
										 |  |  |       dirs.select(&:directory?) | 
					
						
							|  |  |  |           .flat_map { |d| Pathname.glob("#{d}/**/.DS_Store") } | 
					
						
							| 
									
										
										
										
											2021-04-23 14:49:12 +01:00
										 |  |  |           .each do |dir| | 
					
						
							|  |  |  |             dir.unlink | 
					
						
							|  |  |  |           rescue Errno::EACCES | 
					
						
							|  |  |  |             # don't care if we can't delete a .DS_Store | 
					
						
							|  |  |  |             nil | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 21:09:12 +08:00
										 |  |  |     def cleanup_python_site_packages | 
					
						
							|  |  |  |       pyc_files = Hash.new { |h, k| h[k] = [] } | 
					
						
							|  |  |  |       seen_non_pyc_file = Hash.new { |h, k| h[k] = false } | 
					
						
							|  |  |  |       unused_pyc_files = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       HOMEBREW_PREFIX.glob("lib/python*/site-packages").each do |site_packages| | 
					
						
							|  |  |  |         site_packages.each_child do |child| | 
					
						
							|  |  |  |           next unless child.directory? | 
					
						
							| 
									
										
										
										
											2024-04-30 11:10:23 +02:00
										 |  |  |           # TODO: Work out a sensible way to clean up `pip`'s, `setuptools`' and `wheel`'s | 
					
						
							|  |  |  |           #       `{dist,site}-info` directories. Alternatively, consider always removing | 
					
						
							| 
									
										
										
										
											2022-08-18 21:09:12 +08:00
										 |  |  |           #       all `-info` directories, because we may not be making use of them. | 
					
						
							|  |  |  |           next if child.basename.to_s.end_with?("-info") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # Clean up old *.pyc files in the top-level __pycache__. | 
					
						
							|  |  |  |           if child.basename.to_s == "__pycache__" | 
					
						
							|  |  |  |             child.find do |path| | 
					
						
							| 
									
										
										
										
											2023-04-18 15:06:50 -07:00
										 |  |  |               next if path.extname != ".pyc" | 
					
						
							| 
									
										
										
										
											2023-03-27 09:28:27 -07:00
										 |  |  |               next unless self.class.prune?(path, days) | 
					
						
							| 
									
										
										
										
											2022-08-18 21:09:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |               unused_pyc_files << path | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             next | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # Look for directories that contain only *.pyc files. | 
					
						
							|  |  |  |           child.find do |path| | 
					
						
							|  |  |  |             next if path.directory? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if path.extname == ".pyc" | 
					
						
							|  |  |  |               pyc_files[child] << path | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |               seen_non_pyc_file[child] = true | 
					
						
							|  |  |  |               break | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       unused_pyc_files += pyc_files.reject { |k,| seen_non_pyc_file[k] } | 
					
						
							|  |  |  |                                    .values | 
					
						
							| 
									
										
										
										
											2022-08-31 16:04:21 +08:00
										 |  |  |                                    .flatten | 
					
						
							| 
									
										
										
										
											2022-08-18 21:09:12 +08:00
										 |  |  |       return if unused_pyc_files.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       unused_pyc_files.each do |pyc| | 
					
						
							|  |  |  |         cleanup_path(pyc) { pyc.unlink } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |     def prune_prefix_symlinks_and_directories | 
					
						
							|  |  |  |       ObserverPathnameExtension.reset_counts! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       dirs = [] | 
					
						
							| 
									
										
										
										
											2024-03-10 15:27:30 -04:00
										 |  |  |       children_count = {} | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       Keg::MUST_EXIST_SUBDIRECTORIES.each do |dir| | 
					
						
							|  |  |  |         next unless dir.directory? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dir.find do |path| | 
					
						
							|  |  |  |           path.extend(ObserverPathnameExtension) | 
					
						
							|  |  |  |           if path.symlink? | 
					
						
							| 
									
										
										
										
											2022-01-25 12:10:50 +00:00
										 |  |  |             unless path.resolved_path_exists? | 
					
						
							| 
									
										
										
										
											2020-09-01 14:05:52 +01:00
										 |  |  |               path.uninstall_info if path.to_s.match?(Keg::INFOFILE_RX) && !dry_run? | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |               if dry_run? | 
					
						
							|  |  |  |                 puts "Would remove (broken link): #{path}" | 
					
						
							| 
									
										
										
										
											2024-03-10 15:27:30 -04:00
										 |  |  |                 children_count[path.dirname] -= 1 if children_count.key?(path.dirname) | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |               else | 
					
						
							|  |  |  |                 path.unlink | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							| 
									
										
										
										
											2020-12-01 17:04:59 +00:00
										 |  |  |           elsif path.directory? && Keg::MUST_EXIST_SUBDIRECTORIES.exclude?(path) | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |             dirs << path | 
					
						
							| 
									
										
										
										
											2024-03-10 15:27:30 -04:00
										 |  |  |             children_count[path] = path.children.length if dry_run? | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       dirs.reverse_each do |d| | 
					
						
							| 
									
										
										
										
											2024-03-10 15:27:30 -04:00
										 |  |  |         if !dry_run? | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |           d.rmdir_if_possible | 
					
						
							| 
									
										
										
										
											2024-03-10 15:27:30 -04:00
										 |  |  |         elsif children_count[d].zero? | 
					
						
							|  |  |  |           puts "Would remove (empty directory): #{d}" | 
					
						
							|  |  |  |           children_count[d.dirname] -= 1 if children_count.key?(d.dirname) | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 19:25:00 -04:00
										 |  |  |       require "cask/caskroom" | 
					
						
							|  |  |  |       if Cask::Caskroom.path.directory? | 
					
						
							|  |  |  |         Cask::Caskroom.path.each_child do |path| | 
					
						
							|  |  |  |           path.extend(ObserverPathnameExtension) | 
					
						
							|  |  |  |           next if !path.symlink? || path.resolved_path_exists? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if dry_run? | 
					
						
							|  |  |  |             puts "Would remove (broken link): #{path}" | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             path.unlink | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-02 13:21:34 +00:00
										 |  |  |       return if dry_run? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return if ObserverPathnameExtension.total.zero? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       n, d = ObserverPathnameExtension.counts | 
					
						
							|  |  |  |       print "Pruned #{n} symbolic links " | 
					
						
							|  |  |  |       print "and #{d} directories " if d.positive? | 
					
						
							|  |  |  |       puts "from #{HOMEBREW_PREFIX}" | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def self.autoremove(dry_run: false) | 
					
						
							| 
									
										
										
										
											2022-09-13 23:23:48 -07:00
										 |  |  |       require "utils/autoremove" | 
					
						
							| 
									
										
										
										
											2022-07-15 16:27:22 -07:00
										 |  |  |       require "cask/caskroom" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # If this runs after install, uninstall, reinstall or upgrade, | 
					
						
							|  |  |  |       # the cache of installed formulae may no longer be valid. | 
					
						
							|  |  |  |       Formula.clear_cache unless dry_run | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 12:37:38 -07:00
										 |  |  |       formulae = Formula.installed | 
					
						
							|  |  |  |       # Remove formulae listed in HOMEBREW_NO_CLEANUP_FORMULAE and their dependencies. | 
					
						
							|  |  |  |       if Homebrew::EnvConfig.no_cleanup_formulae.present? | 
					
						
							| 
									
										
										
										
											2024-04-08 09:47:06 -07:00
										 |  |  |         formulae -= formulae.select { skip_clean_formula?(_1) } | 
					
						
							| 
									
										
										
										
											2022-08-21 12:37:38 -07:00
										 |  |  |                             .flat_map { |f| [f, *f.runtime_formula_dependencies] } | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2022-07-15 16:27:22 -07:00
										 |  |  |       casks = Cask::Caskroom.casks | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-13 23:23:48 -07:00
										 |  |  |       removable_formulae = Utils::Autoremove.removable_formulae(formulae, casks) | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return if removable_formulae.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       formulae_names = removable_formulae.map(&:full_name).sort | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       verb = dry_run ? "Would autoremove" : "Autoremoving" | 
					
						
							| 
									
										
										
										
											2023-02-27 20:49:02 -08:00
										 |  |  |       oh1 "#{verb} #{formulae_names.count} unneeded #{Utils.pluralize("formula", formulae_names.count, plural: "e")}:" | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  |       puts formulae_names.join("\n") | 
					
						
							|  |  |  |       return if dry_run | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       require "uninstall" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-28 03:23:21 +02:00
										 |  |  |       kegs_by_rack = removable_formulae.filter_map(&:any_installed_keg).group_by(&:rack) | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  |       Uninstall.uninstall_kegs(kegs_by_rack) | 
					
						
							| 
									
										
										
										
											2022-07-15 16:27:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       # The installed formula cache will be invalid after uninstalling. | 
					
						
							|  |  |  |       Formula.clear_cache | 
					
						
							| 
									
										
										
										
											2022-07-14 13:16:26 -07:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2015-12-29 12:57:48 +01:00
										 |  |  |   end | 
					
						
							|  |  |  | end | 
					
						
							| 
									
										
										
										
											2022-11-20 10:49:53 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | require "extend/os/cleanup" |