| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  | # typed: true | 
					
						
							|  |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-12 01:42:17 +05:30
										 |  |  | require "installed_dependents" | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							|  |  |  |   # Helper module for uninstalling kegs. | 
					
						
							|  |  |  |   # | 
					
						
							|  |  |  |   # @api private | 
					
						
							|  |  |  |   module Uninstall | 
					
						
							| 
									
										
										
										
											2023-04-17 10:37:59 -07:00
										 |  |  |     def self.uninstall_kegs(kegs_by_rack, casks: [], force: false, ignore_dependencies: false, named_args: []) | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       handle_unsatisfied_dependents(kegs_by_rack, | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |                                     casks:, | 
					
						
							|  |  |  |                                     ignore_dependencies:, | 
					
						
							|  |  |  |                                     named_args:) | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       return if Homebrew.failed? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       kegs_by_rack.each do |rack, kegs| | 
					
						
							|  |  |  |         if force | 
					
						
							|  |  |  |           name = rack.basename | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if rack.directory? | 
					
						
							|  |  |  |             puts "Uninstalling #{name}... (#{rack.abv})" | 
					
						
							|  |  |  |             kegs.each do |keg| | 
					
						
							|  |  |  |               keg.unlink | 
					
						
							|  |  |  |               keg.uninstall | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           rm_pin rack | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           kegs.each do |keg| | 
					
						
							|  |  |  |             begin | 
					
						
							|  |  |  |               f = Formulary.from_rack(rack) | 
					
						
							|  |  |  |               if f.pinned? | 
					
						
							|  |  |  |                 onoe "#{f.full_name} is pinned. You must unpin it to uninstall." | 
					
						
							| 
									
										
										
										
											2021-11-24 11:37:34 -08:00
										 |  |  |                 break # exit keg loop and move on to next rack | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |               end | 
					
						
							|  |  |  |             rescue | 
					
						
							|  |  |  |               nil | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             keg.lock do | 
					
						
							|  |  |  |               puts "Uninstalling #{keg}... (#{keg.abv})" | 
					
						
							|  |  |  |               keg.unlink | 
					
						
							|  |  |  |               keg.uninstall | 
					
						
							|  |  |  |               rack = keg.rack | 
					
						
							|  |  |  |               rm_pin rack | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               if rack.directory? | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  |                 versions = rack.subdirs.map(&:basename) | 
					
						
							| 
									
										
										
										
											2021-01-24 21:25:12 -05:00
										 |  |  |                 puts <<~EOS | 
					
						
							| 
									
										
										
										
											2023-02-27 21:33:16 -08:00
										 |  |  |                   #{keg.name} #{versions.to_sentence} #{(versions.count == 1) ? "is" : "are"} still installed. | 
					
						
							| 
									
										
										
										
											2021-01-24 21:25:12 -05:00
										 |  |  |                   To remove all versions, run: | 
					
						
							|  |  |  |                     brew uninstall --force #{keg.name} | 
					
						
							|  |  |  |                 EOS | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |               end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               next unless f | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               paths = f.pkgetc.find.map(&:to_s) if f.pkgetc.exist? | 
					
						
							|  |  |  |               if paths.present? | 
					
						
							|  |  |  |                 puts | 
					
						
							|  |  |  |                 opoo <<~EOS | 
					
						
							|  |  |  |                   The following #{f.name} configuration files have not been removed! | 
					
						
							|  |  |  |                   If desired, remove them manually with `rm -rf`: | 
					
						
							|  |  |  |                     #{paths.sort.uniq.join("\n  ")} | 
					
						
							|  |  |  |                 EOS | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               unversioned_name = f.name.gsub(/@.+$/, "") | 
					
						
							|  |  |  |               maybe_paths = Dir.glob("#{f.etc}/*#{unversioned_name}*") | 
					
						
							|  |  |  |               maybe_paths -= paths if paths.present? | 
					
						
							|  |  |  |               if maybe_paths.present? | 
					
						
							|  |  |  |                 puts | 
					
						
							|  |  |  |                 opoo <<~EOS | 
					
						
							|  |  |  |                   The following may be #{f.name} configuration files and have not been removed! | 
					
						
							|  |  |  |                   If desired, remove them manually with `rm -rf`: | 
					
						
							|  |  |  |                     #{maybe_paths.sort.uniq.join("\n  ")} | 
					
						
							|  |  |  |                 EOS | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-11-19 12:57:10 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     rescue MultipleVersionsInstalledError => e | 
					
						
							|  |  |  |       ofail e | 
					
						
							|  |  |  |     ensure | 
					
						
							|  |  |  |       # If we delete Cellar/newname, then Cellar/oldname symlink | 
					
						
							|  |  |  |       # can become broken and we have to remove it. | 
					
						
							|  |  |  |       if HOMEBREW_CELLAR.directory? | 
					
						
							|  |  |  |         HOMEBREW_CELLAR.children.each do |rack| | 
					
						
							|  |  |  |           rack.unlink if rack.symlink? && !rack.resolved_path_exists? | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-17 10:37:59 -07:00
										 |  |  |     def self.handle_unsatisfied_dependents(kegs_by_rack, casks: [], ignore_dependencies: false, named_args: []) | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       return if ignore_dependencies | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       all_kegs = kegs_by_rack.values.flatten(1) | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |       check_for_dependents(all_kegs, casks:, named_args:) | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |     rescue MethodDeprecatedError | 
					
						
							|  |  |  |       # Silently ignore deprecations when uninstalling. | 
					
						
							|  |  |  |       nil | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-17 10:37:59 -07:00
										 |  |  |     def self.check_for_dependents(kegs, casks: [], named_args: []) | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |       return false unless (result = InstalledDependents.find_some_installed_dependents(kegs, casks:)) | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       if Homebrew::EnvConfig.developer? | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |         DeveloperDependentsMessage.new(*result, named_args:).output | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |         NondeveloperDependentsMessage.new(*result, named_args:).output | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       true | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  |     # @api private | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |     class DependentsMessage | 
					
						
							|  |  |  |       attr_reader :reqs, :deps, :named_args | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       def initialize(requireds, dependents, named_args: []) | 
					
						
							|  |  |  |         @reqs = requireds | 
					
						
							|  |  |  |         @deps = dependents | 
					
						
							|  |  |  |         @named_args = named_args | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       protected | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       def sample_command | 
					
						
							|  |  |  |         "brew uninstall --ignore-dependencies #{named_args.join(" ")}" | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       def are_required_by_deps | 
					
						
							| 
									
										
										
										
											2023-02-27 21:33:16 -08:00
										 |  |  |         "#{(reqs.count == 1) ? "is" : "are"} required by #{deps.to_sentence}, " \ | 
					
						
							|  |  |  |           "which #{(deps.count == 1) ? "is" : "are"} currently installed" | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # @api private | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |     class DeveloperDependentsMessage < DependentsMessage | 
					
						
							|  |  |  |       def output | 
					
						
							|  |  |  |         opoo <<~EOS | 
					
						
							|  |  |  |           #{reqs.to_sentence} #{are_required_by_deps}. | 
					
						
							|  |  |  |           You can silence this warning with: | 
					
						
							|  |  |  |             #{sample_command} | 
					
						
							|  |  |  |         EOS | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # @api private | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |     class NondeveloperDependentsMessage < DependentsMessage | 
					
						
							|  |  |  |       def output | 
					
						
							|  |  |  |         ofail <<~EOS | 
					
						
							|  |  |  |           Refusing to uninstall #{reqs.to_sentence} | 
					
						
							| 
									
										
										
										
											2023-02-27 20:49:02 -08:00
										 |  |  |           because #{(reqs.count == 1) ? "it" : "they"} #{are_required_by_deps}. | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |           You can override this and force removal with: | 
					
						
							|  |  |  |             #{sample_command} | 
					
						
							|  |  |  |         EOS | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-11-04 18:53:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-17 10:37:59 -07:00
										 |  |  |     def self.rm_pin(rack) | 
					
						
							| 
									
										
										
										
											2020-11-01 14:45:11 -05:00
										 |  |  |       Formulary.from_rack(rack).unpin | 
					
						
							|  |  |  |     rescue | 
					
						
							|  |  |  |       nil | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |