| 
									
										
										
										
											2016-11-09 09:32:54 +01:00
										 |  |  | require "timeout" | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  | require "hbc/artifact/abstract_artifact" | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  | module Hbc | 
					
						
							|  |  |  |   module Artifact | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |     class AbstractUninstall < AbstractArtifact | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       ORDERED_DIRECTIVES = [ | 
					
						
							| 
									
										
										
										
											2016-10-14 20:33:16 +02:00
										 |  |  |         :early_script, | 
					
						
							|  |  |  |         :launchctl, | 
					
						
							|  |  |  |         :quit, | 
					
						
							|  |  |  |         :signal, | 
					
						
							|  |  |  |         :login_item, | 
					
						
							|  |  |  |         :kext, | 
					
						
							|  |  |  |         :script, | 
					
						
							|  |  |  |         :pkgutil, | 
					
						
							|  |  |  |         :delete, | 
					
						
							|  |  |  |         :trash, | 
					
						
							|  |  |  |         :rmdir, | 
					
						
							|  |  |  |       ].freeze | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def self.from_args(cask, **directives) | 
					
						
							|  |  |  |         new(cask, directives) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       attr_reader :directives | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def initialize(cask, directives) | 
					
						
							|  |  |  |         super(cask) | 
					
						
							| 
									
										
										
										
											2017-09-11 23:29:38 +02:00
										 |  |  |         directives[:signal] = [*directives[:signal]].flatten.each_slice(2).to_a | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |         @directives = directives | 
					
						
							| 
									
										
										
										
											2017-11-24 17:44:01 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return unless directives.key?(:kext) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cask.caveats do | 
					
						
							|  |  |  |           kext | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 14:59:18 +02:00
										 |  |  |       def to_h | 
					
						
							|  |  |  |         directives.to_h | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-11 23:29:38 +02:00
										 |  |  |       def summarize | 
					
						
							| 
									
										
										
										
											2017-10-01 23:33:52 +02:00
										 |  |  |         to_h.flat_map { |key, val| [*val].map { |v| "#{key.inspect} => #{v.inspect}" } }.join(", ") | 
					
						
							| 
									
										
										
										
											2017-09-11 23:29:38 +02:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       private | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def dispatch_uninstall_directives(**options) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         ohai "Running #{stanza} process for #{@cask}; your password may be necessary" | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |         warn_for_unknown_directives(directives) | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         ORDERED_DIRECTIVES.each do |directive_sym| | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           next unless directives.key?(directive_sym) | 
					
						
							|  |  |  |           args = directives[directive_sym] | 
					
						
							|  |  |  |           send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args), **options) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       def stanza | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |         self.class.dsl_key | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       def warn_for_unknown_directives(directives) | 
					
						
							|  |  |  |         unknown_keys = directives.keys - ORDERED_DIRECTIVES | 
					
						
							|  |  |  |         return if unknown_keys.empty? | 
					
						
							| 
									
										
										
										
											2016-10-14 20:08:05 +02:00
										 |  |  |         opoo %Q(Unknown arguments to #{stanza} -- #{unknown_keys.inspect}. Running "brew update; brew cleanup; brew cask cleanup" will likely fix it.) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       # Preserve prior functionality of script which runs first. Should rarely be needed. | 
					
						
							|  |  |  |       # :early_script should not delete files, better defer that to :script. | 
					
						
							|  |  |  |       # If Cask writers never need :early_script it may be removed in the future. | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_early_script(directives, **options) | 
					
						
							|  |  |  |         uninstall_script(directives, directive_name: :early_script, **options) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       # :launchctl must come before :quit/:signal for cases where app would instantly re-launch | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_launchctl(*services, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         services.each do |service| | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           ohai "Removing launchctl service #{service}" | 
					
						
							|  |  |  |           [false, true].each do |with_sudo| | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |             plist_status = command.run("/bin/launchctl", args: ["list", service], sudo: with_sudo, print_stderr: false).stdout | 
					
						
							| 
									
										
										
										
											2016-10-14 20:03:34 +02:00
										 |  |  |             if plist_status =~ /^\{/ | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |               command.run!("/bin/launchctl", args: ["remove", service], sudo: with_sudo) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |               sleep 1
 | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |             paths = ["/Library/LaunchAgents/#{service}.plist", | 
					
						
							|  |  |  |                      "/Library/LaunchDaemons/#{service}.plist"] | 
					
						
							|  |  |  |             paths.each { |elt| elt.prepend(ENV["HOME"]) } unless with_sudo | 
					
						
							|  |  |  |             paths = paths.map { |elt| Pathname(elt) }.select(&:exist?) | 
					
						
							|  |  |  |             paths.each do |path| | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |               command.run!("/bin/rm", args: ["-f", "--", path], sudo: with_sudo) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |             end | 
					
						
							|  |  |  |             # undocumented and untested: pass a path to uninstall :launchctl | 
					
						
							|  |  |  |             next unless Pathname(service).exist? | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |             command.run!("/bin/launchctl", args: ["unload", "-w", "--", service], sudo: with_sudo) | 
					
						
							|  |  |  |             command.run!("/bin/rm",        args: ["-f", "--", service], sudo: with_sudo) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |             sleep 1
 | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def running_processes(bundle_id, command: nil) | 
					
						
							|  |  |  |         command.run!("/bin/launchctl", args: ["list"]).stdout.lines | 
					
						
							|  |  |  |                .map { |line| line.chomp.split("\t") } | 
					
						
							|  |  |  |                .map { |pid, state, id| [pid.to_i, state.to_i, id] } | 
					
						
							|  |  |  |                .select do |fields| | 
					
						
							|  |  |  |                  next if fields[0].zero? | 
					
						
							|  |  |  |                  fields[2] =~ /^#{Regexp.escape(bundle_id)}($|\.\d+)/ | 
					
						
							|  |  |  |                end | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       # :quit/:signal must come before :kext so the kext will not be in use by a running process | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_quit(*bundle_ids, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         bundle_ids.each do |bundle_id| | 
					
						
							|  |  |  |           ohai "Quitting application ID #{bundle_id}" | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           next if running_processes(bundle_id, command: command).empty? | 
					
						
							|  |  |  |           command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{bundle_id}" to quit)], sudo: true) | 
					
						
							| 
									
										
										
										
											2016-11-09 09:32:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |           begin | 
					
						
							|  |  |  |             Timeout.timeout(3) do | 
					
						
							|  |  |  |               Kernel.loop do | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |                 break if running_processes(bundle_id, command: command).empty? | 
					
						
							| 
									
										
										
										
											2016-11-09 09:32:54 +01:00
										 |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           rescue Timeout::Error | 
					
						
							|  |  |  |             next | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       # :signal should come after :quit so it can be used as a backup when :quit fails | 
					
						
							| 
									
										
										
										
											2017-09-11 23:29:38 +02:00
										 |  |  |       def uninstall_signal(*signals, command: nil, **_) | 
					
						
							|  |  |  |         signals.each do |pair| | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |           unless pair.size == 2
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |             raise CaskInvalidError.new(cask, "Each #{stanza} :signal must consist of 2 elements.") | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-09 09:32:54 +01:00
										 |  |  |           signal, bundle_id = pair | 
					
						
							|  |  |  |           ohai "Signalling '#{signal}' to application ID '#{bundle_id}'" | 
					
						
							| 
									
										
										
										
											2017-09-11 23:29:38 +02:00
										 |  |  |           pids = running_processes(bundle_id, command: command).map(&:first) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           next unless pids.any? | 
					
						
							|  |  |  |           # Note that unlike :quit, signals are sent from the current user (not | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |           # upgraded to the superuser). This is a todo item for the future, but | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           # there should be some additional thought/safety checks about that, as a | 
					
						
							|  |  |  |           # misapplied "kill" by root could bring down the system. The fact that we | 
					
						
							|  |  |  |           # learned the pid from AppleScript is already some degree of protection, | 
					
						
							|  |  |  |           # though indirect. | 
					
						
							| 
									
										
										
										
											2016-11-09 09:32:54 +01:00
										 |  |  |           odebug "Unix ids are #{pids.inspect} for processes with bundle identifier #{bundle_id}" | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           Process.kill(signal, *pids) | 
					
						
							|  |  |  |           sleep 3
 | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_login_item(*login_items, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         login_items.each do |name| | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           ohai "Removing login item #{name}" | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           command.run!("/usr/bin/osascript", | 
					
						
							| 
									
										
										
										
											2016-10-14 20:08:05 +02:00
										 |  |  |                         args: ["-e", %Q(tell application "System Events" to delete every login item whose name is "#{name}")], | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |                         sudo: false) | 
					
						
							|  |  |  |           sleep 1
 | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       # :kext should be unloaded before attempting to delete the relevant file | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_kext(*kexts, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         kexts.each do |kext| | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           ohai "Unloading kernel extension #{kext}" | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           is_loaded = command.run!("/usr/sbin/kextstat", args: ["-l", "-b", kext], sudo: true).stdout | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |           if is_loaded.length > 1
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |             command.run!("/sbin/kextunload", args: ["-b", kext], sudo: true) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |             sleep 1
 | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           command.run!("/usr/sbin/kextfind", args: ["-b", kext], sudo: true).stdout.chomp.lines.each do |kext_path| | 
					
						
							| 
									
										
										
										
											2016-11-18 22:53:25 +01:00
										 |  |  |             ohai "Removing kernel extension #{kext_path}" | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |             command.run!("/bin/rm", args: ["-rf", kext_path], sudo: true) | 
					
						
							| 
									
										
										
										
											2016-11-18 22:53:25 +01:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       # :script must come before :pkgutil, :delete, or :trash so that the script file is not already deleted | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_script(directives, directive_name: :script, force: false, command: nil, **_) | 
					
						
							|  |  |  |         # TODO: Create a common `Script` class to run this and Artifact::Installer. | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         executable, script_arguments = self.class.read_script_arguments(directives, | 
					
						
							|  |  |  |                                                                         "uninstall", | 
					
						
							| 
									
										
										
										
											2017-03-12 22:09:13 +01:00
										 |  |  |                                                                         { must_succeed: true, sudo: false }, | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |                                                                         { print_stdout: true }, | 
					
						
							|  |  |  |                                                                         directive_name) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         ohai "Running uninstall script #{executable}" | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |         raise CaskInvalidError.new(cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil? | 
					
						
							|  |  |  |         executable_path = cask.staged_path.join(executable) | 
					
						
							| 
									
										
										
										
											2017-02-16 21:53:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         unless executable_path.exist? | 
					
						
							|  |  |  |           message = "uninstall script #{executable} does not exist" | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           raise CaskError, "#{message}." unless force | 
					
						
							| 
									
										
										
										
											2017-02-16 21:53:22 +01:00
										 |  |  |           opoo "#{message}, skipping." | 
					
						
							|  |  |  |           return | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |         command.run("/bin/chmod", args: ["--", "+x", executable_path]) | 
					
						
							|  |  |  |         command.run(executable_path, script_arguments) | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         sleep 1
 | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_pkgutil(*pkgs, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         ohai "Uninstalling packages:" | 
					
						
							|  |  |  |         pkgs.each do |regex| | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           Hbc::Pkg.all_matching(regex, command).each do |pkg| | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |             puts pkg.package_id | 
					
						
							|  |  |  |             pkg.uninstall | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def each_resolved_path(action, paths) | 
					
						
							| 
									
										
										
										
											2017-06-24 07:01:35 +02:00
										 |  |  |         return enum_for(:each_resolved_path, action, paths) unless block_given? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         paths.each do |path| | 
					
						
							|  |  |  |           resolved_path = Pathname.new(path) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 11:46:30 +01:00
										 |  |  |           resolved_path = resolved_path.expand_path if path.to_s.start_with?("~") | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if resolved_path.relative? || resolved_path.split.any? { |part| part.to_s == ".." } | 
					
						
							|  |  |  |             opoo "Skipping #{Formatter.identifier(action)} for relative path '#{path}'." | 
					
						
							|  |  |  |             next | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if MacOS.undeletable?(resolved_path) | 
					
						
							|  |  |  |             opoo "Skipping #{Formatter.identifier(action)} for undeletable path '#{path}'." | 
					
						
							|  |  |  |             next | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           yield path, Pathname.glob(resolved_path) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_delete(*paths, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         return if paths.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ohai "Removing files:" | 
					
						
							|  |  |  |         each_resolved_path(:delete, paths) do |path, resolved_paths| | 
					
						
							|  |  |  |           puts path | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |           command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "-r", "-f", "--"], input: resolved_paths.join("\0"), sudo: true) | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_trash(*paths, **options) | 
					
						
							| 
									
										
										
										
											2017-06-16 17:01:30 +02:00
										 |  |  |         return if paths.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-24 07:01:35 +02:00
										 |  |  |         resolved_paths = each_resolved_path(:trash, paths).to_a | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 17:01:30 +02:00
										 |  |  |         ohai "Trashing files:" | 
					
						
							| 
									
										
										
										
											2017-06-24 07:01:35 +02:00
										 |  |  |         puts resolved_paths.map(&:first) | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |         trash_paths(*resolved_paths.flat_map(&:last), **options) | 
					
						
							| 
									
										
										
										
											2017-06-24 08:34:01 +02:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def trash_paths(*paths, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2018-07-11 15:17:40 +02:00
										 |  |  |         result = command.run!("/usr/bin/osascript", args: ["-e", <<~APPLESCRIPT, *paths]) | 
					
						
							| 
									
										
										
										
											2017-06-24 07:01:35 +02:00
										 |  |  |           on run argv | 
					
						
							|  |  |  |             repeat with i from 1 to (count argv) | 
					
						
							|  |  |  |               set item i of argv to (item i of argv as POSIX file) | 
					
						
							|  |  |  |             end repeat | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-24 08:34:01 +02:00
										 |  |  |             tell application "Finder" | 
					
						
							|  |  |  |               set trashedItems to (move argv to trash) | 
					
						
							|  |  |  |               set output to "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               repeat with i from 1 to (count trashedItems) | 
					
						
							| 
									
										
										
										
											2017-07-30 18:44:53 +02:00
										 |  |  |                 set trashedItem to POSIX path of (item i of trashedItems as string) | 
					
						
							|  |  |  |                 set output to output & trashedItem | 
					
						
							|  |  |  |                 if i < count trashedItems then | 
					
						
							| 
									
										
										
										
											2017-10-01 01:47:08 +02:00
										 |  |  |                   set output to output & character id 0
 | 
					
						
							| 
									
										
										
										
											2017-07-30 18:44:53 +02:00
										 |  |  |                 end if | 
					
						
							| 
									
										
										
										
											2017-06-24 08:34:01 +02:00
										 |  |  |               end repeat | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               return output | 
					
						
							|  |  |  |             end tell | 
					
						
							| 
									
										
										
										
											2017-06-24 07:01:35 +02:00
										 |  |  |           end run | 
					
						
							| 
									
										
										
										
											2018-07-11 15:17:40 +02:00
										 |  |  |         APPLESCRIPT | 
					
						
							| 
									
										
										
										
											2017-10-01 01:47:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Remove AppleScript's automatic newline. | 
					
						
							|  |  |  |         result.tap { |r| r.stdout.sub!(/\n$/, "") } | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |       def uninstall_rmdir(*directories, command: nil, **_) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |         return if directories.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ohai "Removing directories if empty:" | 
					
						
							|  |  |  |         each_resolved_path(:rmdir, directories) do |path, resolved_paths| | 
					
						
							|  |  |  |           puts path | 
					
						
							|  |  |  |           resolved_paths.select(&:directory?).each do |resolved_path| | 
					
						
							|  |  |  |             if (ds_store = resolved_path.join(".DS_Store")).exist? | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |               command.run!("/bin/rm", args: ["-f", "--", ds_store], sudo: true, print_stderr: false) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 00:33:31 +02:00
										 |  |  |             command.run("/bin/rmdir", args: ["--", resolved_path], sudo: true, print_stderr: false) | 
					
						
							| 
									
										
										
										
											2017-03-08 03:03:36 +01:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-09-24 13:52:43 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |