Skip to content

Commit b0cdf48

Browse files
committed
Add cask upgrade quit opt-out
- Support `--no-quit` for users who need cask apps left open. - Add `HOMEBREW_NO_UPGRADE_QUIT_CASKS` for persistent opt-out. - Cover command, upgrade and uninstall-stanza paths with regressions.
1 parent 03684f7 commit b0cdf48

10 files changed

Lines changed: 84 additions & 13 deletions

File tree

Library/Homebrew/cask/artifact/uninstall.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ class Uninstall < AbstractUninstall
1313
params(
1414
upgrade: T::Boolean,
1515
reinstall: T::Boolean,
16+
quit: T::Boolean,
1617
options: T.anything,
1718
).void
1819
}
19-
def uninstall_phase(upgrade: false, reinstall: false, **options)
20+
def uninstall_phase(upgrade: false, reinstall: false, quit: true, **options)
2021
raw_on_upgrade = directives[:on_upgrade]
2122
on_upgrade_syms =
2223
case raw_on_upgrade
@@ -31,6 +32,7 @@ def uninstall_phase(upgrade: false, reinstall: false, **options)
3132

3233
filtered_directives = ORDERED_DIRECTIVES.filter do |directive_sym|
3334
next false if directive_sym == :rmdir
35+
next false if directive_sym == :quit && !quit
3436

3537
if (upgrade || reinstall) &&
3638
UPGRADE_REINSTALL_SKIP_DIRECTIVES.include?(directive_sym) &&

Library/Homebrew/cask/installer.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -587,9 +587,9 @@ def remove_download_sha
587587
@cask.download_sha_path.parent.rmdir_if_possible
588588
end
589589

590-
sig { params(successor: T.nilable(Cask)).void }
591-
def start_upgrade(successor:)
592-
uninstall_artifacts(successor:)
590+
sig { params(successor: T.nilable(Cask), quit: T::Boolean).void }
591+
def start_upgrade(successor:, quit: true)
592+
uninstall_artifacts(successor:, quit:)
593593
backup
594594
end
595595

@@ -638,8 +638,8 @@ def finalize_upgrade
638638
puts summary
639639
end
640640

641-
sig { params(clear: T::Boolean, successor: T.nilable(Cask)).void }
642-
def uninstall_artifacts(clear: false, successor: nil)
641+
sig { params(clear: T::Boolean, successor: T.nilable(Cask), quit: T::Boolean).void }
642+
def uninstall_artifacts(clear: false, successor: nil, quit: true)
643643
odebug "Uninstalling artifacts"
644644
odebug "#{::Utils.pluralize("artifact", artifacts.length, include_count: true)} defined", artifacts
645645

@@ -659,15 +659,17 @@ def uninstall_artifacts(clear: false, successor: nil)
659659
)
660660

661661
odebug "Uninstalling artifact of class #{artifact.class}"
662-
artifact.uninstall_phase(
662+
uninstall_options = {
663663
command: @command,
664664
verbose: verbose?,
665665
skip: clear,
666666
force: force?,
667667
successor:,
668668
upgrade: upgrade?,
669669
reinstall: reinstall?,
670-
)
670+
}
671+
uninstall_options[:quit] = quit if artifact.is_a?(Artifact::Uninstall)
672+
artifact.uninstall_phase(**uninstall_options)
671673
end
672674

673675
next unless artifact.respond_to?(:post_uninstall_phase)

Library/Homebrew/cask/upgrade.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def self.show_upgrade_summary(cask_upgrades, dry_run: false)
112112
binaries: T.nilable(T::Boolean),
113113
quarantine: T.nilable(T::Boolean),
114114
require_sha: T.nilable(T::Boolean),
115+
quit: T::Boolean,
115116
skip_prefetch: T::Boolean,
116117
show_upgrade_summary: T::Boolean,
117118
download_queue: T.nilable(Homebrew::DownloadQueue),
@@ -135,6 +136,7 @@ def self.upgrade_casks!(
135136
binaries: nil,
136137
quarantine: nil,
137138
require_sha: nil,
139+
quit: true,
138140
skip_prefetch: false,
139141
show_upgrade_summary: true,
140142
download_queue: nil,
@@ -261,7 +263,7 @@ def self.upgrade_casks!(
261263
upgrade_cask(
262264
old_cask, new_cask,
263265
binaries:, force:, skip_cask_deps:, verbose:,
264-
quarantine:, require_sha:, download_queue:
266+
quarantine:, require_sha:, quit:, download_queue:
265267
)
266268
rescue => e
267269
new_exception = e.exception("#{new_cask.full_name}: #{e}")
@@ -331,14 +333,15 @@ def self.reopen_apps_after_upgrade(old_cask)
331333
force: T.nilable(T::Boolean),
332334
quarantine: T.nilable(T::Boolean),
333335
require_sha: T.nilable(T::Boolean),
336+
quit: T::Boolean,
334337
skip_cask_deps: T.nilable(T::Boolean),
335338
verbose: T.nilable(T::Boolean),
336339
download_queue: Homebrew::DownloadQueue,
337340
).void
338341
}
339342
def self.upgrade_cask(
340343
old_cask, new_cask,
341-
binaries:, force:, quarantine:, require_sha:, skip_cask_deps:, verbose:, download_queue:
344+
binaries:, force:, quarantine:, require_sha:, quit:, skip_cask_deps:, verbose:, download_queue:
342345
)
343346
require "cask/installer"
344347

@@ -402,7 +405,7 @@ def self.upgrade_cask(
402405
end
403406

404407
# Move the old cask's artifacts back to staging
405-
old_cask_installer.start_upgrade(successor: new_cask)
408+
old_cask_installer.start_upgrade(successor: new_cask, quit:)
406409
# And flag it so in case of error
407410
started_upgrade = true
408411

@@ -423,9 +426,9 @@ def self.upgrade_cask(
423426
# If successful, wipe the old cask from staging.
424427
old_cask_installer.finalize_upgrade
425428

426-
reopen_apps_after_upgrade(old_cask)
429+
reopen_apps_after_upgrade(old_cask) if quit
427430
rescue => e
428-
new_cask_installer.uninstall_artifacts(successor: old_cask) if new_artifacts_installed
431+
new_cask_installer.uninstall_artifacts(successor: old_cask, quit:) if new_artifacts_installed
429432
new_cask_installer.purge_versioned_files
430433
old_cask_installer.revert_upgrade(predecessor: new_cask) if started_upgrade
431434
raise e

Library/Homebrew/cmd/upgrade.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ class FinalUpgradeSummary < T::Struct
109109
[:switch, "--skip-cask-deps", {
110110
description: "Skip installing cask dependencies.",
111111
}],
112+
[:switch, "--no-quit", {
113+
description: "Prevent running cask applications from being quit during upgrade.",
114+
env: :no_upgrade_quit_casks,
115+
}],
112116
[:switch, "-g", "--greedy", {
113117
description: "Also include casks with `version :latest` and `auto_updates true` casks " \
114118
"that would otherwise be skipped.",
@@ -778,6 +782,7 @@ def upgrade_outdated_casks!(casks, skip_prefetch: false, show_upgrade_summary: t
778782
quarantine: args.quarantine?,
779783
require_sha: args.require_sha?,
780784
skip_cask_deps: args.skip_cask_deps?,
785+
quit: !args.no_quit?,
781786
verbose: args.verbose?,
782787
quiet: args.quiet?,
783788
skip_prefetch:,

Library/Homebrew/env_config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,10 @@ module EnvConfig
483483
boolean: true,
484484
hidden: true, # odeprecated: remove in 5.2.0
485485
},
486+
HOMEBREW_NO_UPGRADE_QUIT_CASKS: {
487+
description: "If set, `brew upgrade` will not quit running applications for casks during upgrades.",
488+
boolean: true,
489+
},
486490
HOMEBREW_NO_VERIFY_ATTESTATIONS: {
487491
description: "If set, Homebrew will not verify cryptographic attestations of build provenance for bottles " \
488492
"from homebrew-core.",

Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/upgrade_cmd.rbi

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Library/Homebrew/test/cask/artifact/uninstall_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@
2525
expect(quit_called).to be true
2626
end
2727

28+
it "skips :quit during upgrade when quit is false" do
29+
quit_called = false
30+
allow(artifact).to receive(:dispatch_uninstall_directive) do |directive, **options|
31+
quit_called ||= directive == :quit && options[:command] == fake_system_command
32+
end
33+
34+
artifact.uninstall_phase(upgrade: true, quit: false, command: fake_system_command)
35+
36+
expect(quit_called).to be false
37+
end
38+
2839
it "invokes :quit during reinstall" do
2940
quit_called = false
3041
allow(artifact).to receive(:dispatch_uninstall_directive) do |directive, **options|

Library/Homebrew/test/cask/upgrade_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,20 @@ def write_info_plist(path, short_version:, bundle_version:)
172172
expect(summary_deprecated).to include("local-caffeine")
173173
end
174174

175+
it "passes the quit option to cask upgrades" do
176+
expect(described_class).to receive(:upgrade_cask) do |_, _, **options|
177+
expect(options[:quit]).to be(false)
178+
end
179+
180+
described_class.upgrade_casks!(
181+
local_caffeine,
182+
quit: false,
183+
skip_prefetch: true,
184+
show_upgrade_summary: false,
185+
args:,
186+
)
187+
end
188+
175189
it "excludes pinned Casks" do
176190
local_caffeine.pin
177191
summary_pinned = []

Library/Homebrew/test/cmd/upgrade_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,30 @@ def install_formula_version(name, version, optlinked: false)
160160
cmd.send(:upgrade_outdated_casks!, [])
161161
end
162162

163+
it "passes --no-quit to cask upgrades" do
164+
cmd = described_class.new(["--cask", "--no-quit"])
165+
166+
expect(Cask::Upgrade).to receive(:upgrade_casks!) do |*_, **kwargs|
167+
expect(kwargs[:quit]).to be(false)
168+
true
169+
end
170+
171+
cmd.send(:upgrade_outdated_casks!, [])
172+
end
173+
174+
it "passes HOMEBREW_NO_UPGRADE_QUIT_CASKS to cask upgrades" do
175+
with_env("HOMEBREW_NO_UPGRADE_QUIT_CASKS" => "1") do
176+
cmd = described_class.new(["--cask"])
177+
178+
expect(Cask::Upgrade).to receive(:upgrade_casks!) do |*_, **kwargs|
179+
expect(kwargs[:quit]).to be(false)
180+
true
181+
end
182+
183+
cmd.send(:upgrade_outdated_casks!, [])
184+
end
185+
end
186+
163187
it "prints formula and cask ask plans before upgrading" do
164188
cmd = described_class.new(["--ask"])
165189

0 commit comments

Comments
 (0)