Merge pull request #14123 from bevanjkay/cask-wildcard-launchctl

cask/artifact/abstract_uninstall: allow wildcard entries for launchctl
This commit is contained in:
Mike McQuaid 2022-12-29 19:26:53 +00:00 committed by GitHub
commit 5994f165f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 6 deletions

View File

@ -90,7 +90,21 @@ module Cask
# :launchctl must come before :quit/:signal for cases where app would instantly re-launch # :launchctl must come before :quit/:signal for cases where app would instantly re-launch
def uninstall_launchctl(*services, command: nil, **_) def uninstall_launchctl(*services, command: nil, **_)
booleans = [false, true] booleans = [false, true]
all_services = []
# if launchctl item contains a wildcard, find matching process(es)
services.each do |service| services.each do |service|
all_services << service unless service.include?("*")
next unless service.include?("*")
found_services = find_launchctl_with_wildcard(service)
next if found_services.blank?
found_services.each { |found_service| all_services << found_service }
end
all_services.each do |service|
ohai "Removing launchctl service #{service}" ohai "Removing launchctl service #{service}"
booleans.each do |with_sudo| booleans.each do |with_sudo|
plist_status = command.run( plist_status = command.run(
@ -131,6 +145,16 @@ module Cask
end end
end end
def find_launchctl_with_wildcard(search)
regex = Regexp.escape(search).gsub("\\*", ".*")
system_command!("/bin/launchctl", args: ["list"])
.stdout.lines.drop(1) # skip stdout column headers
.map do |line|
pid, _state, id = line.chomp.split(/\s+/)
id if pid.to_i.nonzero? && id.match?(regex)
end.compact
end
sig { returns(String) } sig { returns(String) }
def automation_access_instructions def automation_access_instructions
<<~EOS <<~EOS

View File

@ -61,6 +61,64 @@ shared_examples "#uninstall_phase or #zap_phase" do
end end
end end
context "using :launchctl with regex wildcard" do
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl-wildcard")) }
let(:launchctl_regex) { "my.fancy.package.service.*" }
let(:unknown_response) { "launchctl list returned unknown response\n" }
let(:service_info) do
<<~EOS
{
"LimitLoadToSessionType" = "Aqua";
"Label" = "my.fancy.package.service.12345";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 0;
"ProgramArguments" = (
"argument";
);
};
EOS
end
let(:launchctl_list) do
<<~EOS
PID Status Label
1111 0 my.fancy.package.service.12345
- 0 com.apple.SafariHistoryServiceAgent
- 0 com.apple.progressd
555 0 my.fancy.package.service.test
EOS
end
it "searches installed launchctl items" do
expect(subject).to receive(:find_launchctl_with_wildcard)
.with(launchctl_regex)
.and_return(["my.fancy.package.service.12345"])
allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service.12345"], print_stderr: false, sudo: false)
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service.12345"], print_stderr: false, sudo: true)
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
expect(fake_system_command).to receive(:run!)
.with("/bin/launchctl", args: ["remove", "my.fancy.package.service.12345"], sudo: true)
.and_return(instance_double(SystemCommand::Result))
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
end
it "returns the matching launchctl services" do
expect(subject).to receive(:system_command!)
.with("/bin/launchctl", args: ["list"])
.and_return(instance_double(SystemCommand::Result, stdout: launchctl_list))
expect(subject.send(:find_launchctl_with_wildcard,
"my.fancy.package.service.*")).to eq(["my.fancy.package.service.12345",
"my.fancy.package.service.test"])
end
end
context "using :pkgutil" do context "using :pkgutil" do
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) } let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) }
@ -117,9 +175,9 @@ shared_examples "#uninstall_phase or #zap_phase" do
allow(User.current).to receive(:gui?).and_return false allow(User.current).to receive(:gui?).and_return false
allow(subject).to receive(:running?).with(bundle_id).and_return(true) allow(subject).to receive(:running?).with(bundle_id).and_return(true)
expect { expect do
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command) subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
}.to output(/Not logged into a GUI; skipping quitting application ID 'my.fancy.package.app'\./).to_stderr end.to output(/Not logged into a GUI; skipping quitting application ID 'my.fancy.package.app'\./).to_stderr
end end
it "quits a running application" do it "quits a running application" do
@ -130,9 +188,9 @@ shared_examples "#uninstall_phase or #zap_phase" do
.and_return(instance_double("SystemCommand::Result", success?: true)) .and_return(instance_double("SystemCommand::Result", success?: true))
expect(subject).to receive(:running?).with(bundle_id).ordered.and_return(false) expect(subject).to receive(:running?).with(bundle_id).ordered.and_return(false)
expect { expect do
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command) subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
}.to output(/Application 'my.fancy.package.app' quit successfully\./).to_stdout end.to output(/Application 'my.fancy.package.app' quit successfully\./).to_stdout
end end
it "tries to quit the application for 10 seconds" do it "tries to quit the application for 10 seconds" do
@ -143,9 +201,9 @@ shared_examples "#uninstall_phase or #zap_phase" do
.and_return(instance_double("SystemCommand::Result", success?: false)) .and_return(instance_double("SystemCommand::Result", success?: false))
time = Benchmark.measure do time = Benchmark.measure do
expect { expect do
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command) subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
}.to output(/Application 'my.fancy.package.app' did not quit\./).to_stderr end.to output(/Application 'my.fancy.package.app' did not quit\./).to_stderr
end end
expect(time.real).to be_within(3).of(10) expect(time.real).to be_within(3).of(10)

View File

@ -0,0 +1,11 @@
cask "with-uninstall-launchctl-wildcard" do
version "1.2.3"
sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
url "file://#{TEST_FIXTURE_DIR}/cask/MyFancyApp.zip"
homepage "https://brew.sh/fancy"
app "Fancy.app"
uninstall launchctl: "my.fancy.package.service.*"
end

View File

@ -0,0 +1,11 @@
cask "with-zap-launchctl-wildcard" do
version "1.2.3"
sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
url "file://#{TEST_FIXTURE_DIR}/cask/MyFancyApp.zip"
homepage "https://brew.sh/fancy"
app "Fancy.app"
zap launchctl: "my.fancy.package.service.*"
end