diff --git a/.ruby-version b/.ruby-version new file mode 120000 index 0000000000000..f9892605c753d --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.4.4 diff --git a/Library/Homebrew/ast_constants.rb b/Library/Homebrew/ast_constants.rb index 70b1ba829a8e4..eac6929bc8150 100644 --- a/Library/Homebrew/ast_constants.rb +++ b/Library/Homebrew/ast_constants.rb @@ -7,6 +7,7 @@ [{ name: :include, type: :method_call }], [{ name: :desc, type: :method_call }], [{ name: :homepage, type: :method_call }], + [{ name: :login_items, type: :method_call }], [{ name: :url, type: :method_call }], [{ name: :mirror, type: :method_call }], [{ name: :version, type: :method_call }], diff --git a/Library/Homebrew/cask/artifact/abstract_uninstall.rb b/Library/Homebrew/cask/artifact/abstract_uninstall.rb index 9a0791cc43781..6e5f69f855fdd 100644 --- a/Library/Homebrew/cask/artifact/abstract_uninstall.rb +++ b/Library/Homebrew/cask/artifact/abstract_uninstall.rb @@ -20,6 +20,7 @@ class AbstractUninstall < AbstractArtifact :launchctl, :quit, :signal, + # odeprecated: deprecate when all casks have been migrated to top-level login_items :login_item, :kext, :script, @@ -297,6 +298,7 @@ def uninstall_signal(*signals, command: nil, **_) end end + # TODO: Need to refer to attribute from the cask instead of this uninstall stanza def uninstall_login_item(*login_items, command: nil, successor: nil, **_) return if successor diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index 2370186f62b02..32cb6b6065ace 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -347,6 +347,10 @@ def languages @languages ||= @dsl.languages end + def login_items + @login_items ||= @dsl.login_items + end + def tap_git_head @tap_git_head ||= tap&.git_head rescue TapUnavailableError @@ -357,6 +361,7 @@ def populate_from_api!(json_cask) raise ArgumentError, "Expected cask to be loaded from the API" unless loaded_from_api? @languages = json_cask.fetch(:languages, []) + @login_items = json_cask.fetch(:login_items, []) @tap_git_head = json_cask.fetch(:tap_git_head, "HEAD") @ruby_source_path = json_cask[:ruby_source_path] @@ -427,6 +432,7 @@ def to_h "languages" => languages, "ruby_source_path" => ruby_source_path, "ruby_source_checksum" => ruby_source_checksum, + "login_items" => login_items, } end diff --git a/Library/Homebrew/cask/cask_loader.rb b/Library/Homebrew/cask/cask_loader.rb index 7ca1d7d6b7eca..b7fe33200a007 100644 --- a/Library/Homebrew/cask/cask_loader.rb +++ b/Library/Homebrew/cask/cask_loader.rb @@ -377,6 +377,7 @@ def load(config:) end desc json_cask[:desc] homepage json_cask[:homepage] + login_items json_cask[:login_items] if json_cask[:login_items].present? if (date = json_cask[:deprecation_date].presence) because = DeprecateDisable.to_reason_string_or_symbol json_cask[:deprecation_reason], type: :cask @@ -389,7 +390,7 @@ def load(config:) disable! date:, because: end - auto_updates json_cask[:auto_updates] unless json_cask[:auto_updates].nil? + auto_updates json_cask[:auto_updates] if json_cask[:auto_updates].present? conflicts_with(**json_cask[:conflicts_with]) if json_cask[:conflicts_with].present? if json_cask[:rename].present? diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 70d96cf784e09..284c414a1e054 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -108,6 +108,7 @@ class DSL :no_autobump!, :autobump?, :no_autobump_message, + :login_items, :on_system_blocks_exist?, :on_system_block_min_os, :depends_on_set_in_block?, @@ -243,6 +244,21 @@ def set_unique_stanza(stanza, should_return) raise CaskInvalidError.new(cask, "'#{stanza}' stanza failed with: #{e}") end + # Sets the cask's login items + # + # ### Example + # + # ```ruby + # login_items "Raycast" + # ``` + # + # @api public + def login_items(login_items = nil) + return [] if login_items.nil? + + set_unique_stanza(:login_items, login_items.nil?) { Array(login_items) } + end + # Sets the cask's homepage. # # ### Example diff --git a/Library/Homebrew/cask/info.rb b/Library/Homebrew/cask/info.rb index bbc9856a7a47b..0eca6c91253c0 100644 --- a/Library/Homebrew/cask/info.rb +++ b/Library/Homebrew/cask/info.rb @@ -30,6 +30,8 @@ def self.get_info(cask) language = language_info(cask) output << language if language output << "#{artifact_info(cask)}\n" + login_items = login_items_info(cask) + output << login_items if login_items caveats = Installer.caveats(cask) output << caveats if caveats output @@ -137,5 +139,15 @@ def self.artifact_info(cask) end artifact_output.freeze end + + sig { params(cask: Cask).returns(T.nilable(String)) } + def self.login_items_info(cask) + return if cask.login_items.empty? + + <<~EOS + #{ohai_title("Login Items")} + #{cask.login_items.join(", ")} + EOS + end end end diff --git a/Library/Homebrew/cask/installer.rb b/Library/Homebrew/cask/installer.rb index 2c672d852f009..1fc8cb5d25200 100644 --- a/Library/Homebrew/cask/installer.rb +++ b/Library/Homebrew/cask/installer.rb @@ -25,14 +25,15 @@ class Installer skip_cask_deps: T::Boolean, binaries: T::Boolean, verbose: T::Boolean, zap: T::Boolean, require_sha: T::Boolean, upgrade: T::Boolean, reinstall: T::Boolean, installed_as_dependency: T::Boolean, installed_on_request: T::Boolean, quarantine: T::Boolean, verify_download_integrity: T::Boolean, - quiet: T::Boolean, download_queue: T.nilable(Homebrew::DownloadQueue) + quiet: T::Boolean, download_queue: T.nilable(Homebrew::DownloadQueue), login_items: T::Boolean ).void } def initialize(cask, command: SystemCommand, force: false, adopt: false, skip_cask_deps: false, binaries: true, verbose: false, zap: false, require_sha: false, upgrade: false, reinstall: false, installed_as_dependency: false, installed_on_request: true, - quarantine: true, verify_download_integrity: true, quiet: false, download_queue: nil) + quarantine: true, verify_download_integrity: true, quiet: false, download_queue: nil, + login_items: false) @cask = cask @command = command @force = force @@ -51,6 +52,7 @@ def initialize(cask, command: SystemCommand, force: false, adopt: false, @quiet = quiet @download_queue = download_queue @ran_prelude = T.let(false, T::Boolean) + @login_items = login_items end sig { returns(T::Boolean) } @@ -68,6 +70,9 @@ def installed_as_dependency? = @installed_as_dependency sig { returns(T::Boolean) } def installed_on_request? = @installed_on_request + sig { returns(T::Boolean) } + def login_items? = @login_items + sig { returns(T::Boolean) } def quarantine? = @quarantine @@ -348,6 +353,17 @@ def install_artifacts(predecessor: nil) already_installed_artifacts.unshift(artifact) end + unless @cask.login_items.empty? + if login_items? + @cask.login_items.each do |lgi| + # TODO: register the login_items here using osascript + ohai "***** Will REGISTER login_item: #{lgi}" + end + else + ohai "Skipping processing of login_items" + end + end + save_config_file save_download_sha if @cask.version.latest? rescue => e @@ -592,6 +608,15 @@ def uninstall_artifacts(clear: false, successor: nil) odebug "Uninstalling artifacts" odebug "#{::Utils.pluralize("artifact", artifacts.length, include_count: true)} defined", artifacts + if login_items? + @cask.login_items.each do |lgi| + # TODO: unregister the login_items here using osascript + ohai "***** Will UNREGISTER login_item: #{lgi}" + end + else + ohai "Skipping processing of login_items" + end + artifacts.each do |artifact| if artifact.respond_to?(:uninstall_phase) artifact = T.cast( diff --git a/Library/Homebrew/cask/reinstall.rb b/Library/Homebrew/cask/reinstall.rb index 3ab7d4c177a9d..ed1c847c8337c 100644 --- a/Library/Homebrew/cask/reinstall.rb +++ b/Library/Homebrew/cask/reinstall.rb @@ -10,7 +10,7 @@ class Reinstall sig { params( casks: ::Cask::Cask, verbose: T::Boolean, force: T::Boolean, skip_cask_deps: T::Boolean, binaries: T::Boolean, - require_sha: T::Boolean, quarantine: T::Boolean, zap: T::Boolean + require_sha: T::Boolean, quarantine: T::Boolean, zap: T::Boolean, login_items: T::Boolean ).void } def self.reinstall_casks( @@ -21,7 +21,8 @@ def self.reinstall_casks( binaries: false, require_sha: false, quarantine: false, - zap: false + zap: false, + login_items: true ) require "cask/installer" @@ -30,7 +31,7 @@ def self.reinstall_casks( download_queue = Homebrew::DownloadQueue.new_if_concurrency_enabled(pour: true) cask_installers = casks.map do |cask| Installer.new(cask, binaries:, verbose:, force:, skip_cask_deps:, require_sha:, reinstall: true, - quarantine:, zap:, download_queue:) + quarantine:, zap:, download_queue:, login_items:) end if download_queue diff --git a/Library/Homebrew/cask/upgrade.rb b/Library/Homebrew/cask/upgrade.rb index 97a094e022879..e48b3adb7512f 100644 --- a/Library/Homebrew/cask/upgrade.rb +++ b/Library/Homebrew/cask/upgrade.rb @@ -71,6 +71,7 @@ def self.outdated_casks(casks, args:, force:, quiet:, binaries: T.nilable(T::Boolean), quarantine: T.nilable(T::Boolean), require_sha: T.nilable(T::Boolean), + login_items: T.nilable(T::Boolean), ).returns(T::Boolean) } def self.upgrade_casks!( @@ -86,7 +87,8 @@ def self.upgrade_casks!( quiet: false, binaries: nil, quarantine: nil, - require_sha: nil + require_sha: nil, + login_items: nil ) quarantine = true if quarantine.nil? @@ -171,7 +173,7 @@ def self.upgrade_casks!( upgrade_cask( old_cask, new_cask, binaries:, force:, skip_cask_deps:, verbose:, - quarantine:, require_sha:, download_queue: + quarantine:, require_sha:, download_queue:, login_items: ) rescue => e new_exception = e.exception("#{new_cask.full_name}: #{e}") @@ -195,6 +197,7 @@ def self.upgrade_casks!( force: T.nilable(T::Boolean), quarantine: T.nilable(T::Boolean), require_sha: T.nilable(T::Boolean), + login_items: T.nilable(T::Boolean), skip_cask_deps: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean), download_queue: T.nilable(Homebrew::DownloadQueue), @@ -202,7 +205,7 @@ def self.upgrade_casks!( } def self.upgrade_cask( old_cask, new_cask, - binaries:, force:, quarantine:, require_sha:, skip_cask_deps:, verbose:, download_queue: + binaries:, force:, quarantine:, require_sha:, login_items:, skip_cask_deps:, verbose:, download_queue: ) require "cask/installer" @@ -231,6 +234,7 @@ def self.upgrade_cask( upgrade: true, quarantine:, download_queue:, + login_items:, }.compact new_cask_installer = diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 56b3cf9388635..92ca36054fafc 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -151,6 +151,10 @@ class InstallCmd < AbstractCommand env: :cask_opts_quarantine, odeprecated: true, }], + [:switch, "--[no-]login-items", { + description: "Disable/enable registering of login item(s) (default: disabled).", + env: :cask_opts_login_items, + }], [:switch, "--adopt", { description: "Adopt existing artifacts in the destination that are identical to those being installed. " \ "Cannot be combined with `--force`.", @@ -282,6 +286,7 @@ def run binaries: args.binaries?, force: args.force?, quarantine: args.quarantine?, + login_items: args.login_items?, quiet: args.quiet?, require_sha: args.require_sha?, skip_cask_deps: args.skip_cask_deps?, @@ -296,6 +301,7 @@ def run dry_run: args.dry_run?, binaries: args.binaries?, quarantine: args.quarantine?, + login_items: args.login_items?, require_sha: args.require_sha?, skip_cask_deps: args.skip_cask_deps?, verbose: args.verbose?, diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index f8dc91fb95ced..dc3f9ad61e63a 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -91,6 +91,10 @@ class Reinstall < AbstractCommand env: :cask_opts_quarantine, odeprecated: true, }], + [:switch, "--[no-]login-items", { + description: "Disable/enable registering of login item(s) (default: disabled).", + env: :cask_opts_login_items, + }], [:switch, "--adopt", { description: "Adopt existing artifacts in the destination that are identical to those being installed. " \ "Cannot be combined with `--force`.", @@ -210,6 +214,7 @@ def run require_sha: args.require_sha?, skip_cask_deps: args.skip_cask_deps?, quarantine: args.quarantine?, + login_items: args.login_items?, zap: args.zap?, ) end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index 01ff5e74327ab..fdd3e93195e9d 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -67,7 +67,7 @@ def run raise Cask::CaskNotInstalledError, cask if !cask.installed? && !args.force? - Cask::Installer.new(cask, verbose: args.verbose?, force: args.force?).zap + Cask::Installer.new(cask, verbose: args.verbose?, force: args.force?, login_items: true).zap end else Cask::Uninstall.uninstall_casks( diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index 026452f4f6f98..9a1e4e7c8d994 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -112,6 +112,10 @@ class UpgradeCmd < AbstractCommand env: :cask_opts_quarantine, odeprecated: true, }], + [:switch, "--[no-]login-items", { + description: "Disable/enable registering of login item(s) (default: disabled).", + env: :cask_opts_login_items, + }], ].each do |args| options = args.pop send(*args, **options) @@ -301,6 +305,7 @@ def upgrade_outdated_casks!(casks) dry_run: args.dry_run?, binaries: args.binaries?, quarantine: args.quarantine?, + login_items: args.login_items?, require_sha: args.require_sha?, skip_cask_deps: args.skip_cask_deps?, verbose: args.verbose?, diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index ea1f063da8934..8623d3810d2e2 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -107,7 +107,7 @@ module EnvConfig }, HOMEBREW_CASK_OPTS: { description: "Append these options to all `cask` commands. All `--*dir` options, " \ - "`--language`, `--require-sha` and `--no-binaries` are supported. " \ + "`--language`, `--require-sha`, `--no-login-items` and `--no-binaries` are supported. " \ "For example, you might add something like the following to your " \ "`~/.profile`, `~/.bash_profile`, or `~/.zshenv`:" \ "\n\n `export HOMEBREW_CASK_OPTS=\"--appdir=${HOME}/Applications --fontdir=/Library/Fonts\"`", @@ -640,6 +640,16 @@ def cask_opts_quarantine? true end + sig { returns(T::Boolean) } + def cask_opts_login_items? + cask_opts.reverse_each do |opt| + return true if opt == "--login-items" + return false if opt == "--no-login-items" + end + + false + end + sig { returns(T::Boolean) } def cask_opts_require_sha? cask_opts.include?("--require-sha") diff --git a/Library/Homebrew/rubocops/cask/constants/stanza.rb b/Library/Homebrew/rubocops/cask/constants/stanza.rb index 9cd042b02f3bc..064983a224527 100644 --- a/Library/Homebrew/rubocops/cask/constants/stanza.rb +++ b/Library/Homebrew/rubocops/cask/constants/stanza.rb @@ -42,6 +42,7 @@ module Constants :app, :pkg, :installer, + :login_items, :binary, :manpage, :bash_completion, @@ -89,6 +90,7 @@ module Constants :launchctl, :quit, :signal, + # odeprecated: deprecate when all casks have been migrated to top-level login_items :login_item, :kext, :script, diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/fetch_cmd.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/fetch_cmd.rbi index 9275df2ee4a4a..269f0bf13a61a 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/fetch_cmd.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/fetch_cmd.rbi @@ -56,6 +56,9 @@ class Homebrew::Cmd::FetchCmd::Args < Homebrew::CLI::Args sig { returns(T::Boolean) } def quarantine?; end + sig { returns(T::Boolean) } + def login_items?; end + sig { returns(T::Boolean) } def retry?; end diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/install_cmd.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/install_cmd.rbi index 8215d6dc05914..7d2313e082c08 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/install_cmd.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/install_cmd.rbi @@ -143,6 +143,9 @@ class Homebrew::Cmd::InstallCmd::Args < Homebrew::CLI::Args sig { returns(T::Boolean) } def quarantine?; end + sig { returns(T::Boolean) } + def login_items?; end + sig { returns(T::Boolean) } def require_sha?; end diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/reinstall.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/reinstall.rbi index beee25bbbe15c..549b5d63ec367 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/reinstall.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/reinstall.rbi @@ -104,6 +104,9 @@ class Homebrew::Cmd::Reinstall::Args < Homebrew::CLI::Args sig { returns(T::Boolean) } def quarantine?; end + sig { returns(T::Boolean) } + def login_items?; end + sig { returns(T::Boolean) } def require_sha?; end diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/upgrade_cmd.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/upgrade_cmd.rbi index 3eb336f954f33..818c72015e3ad 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/upgrade_cmd.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/upgrade_cmd.rbi @@ -119,6 +119,9 @@ class Homebrew::Cmd::UpgradeCmd::Args < Homebrew::CLI::Args sig { returns(T::Boolean) } def quarantine?; end + sig { returns(T::Boolean) } + def login_items?; end + sig { returns(T::Boolean) } def require_sha?; end diff --git a/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb b/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb index 354491f54975a..2a9b346acdee8 100644 --- a/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb +++ b/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb @@ -391,7 +391,8 @@ end end - context "when using :login_item" do + # TODO: Need to test for array of login_items + context "when using :login_items" do let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-login-item")) } it "is supported" do diff --git a/Library/Homebrew/test/cask/info_spec.rb b/Library/Homebrew/test/cask/info_spec.rb index ea41b57d942e6..7956555ebe030 100644 --- a/Library/Homebrew/test/cask/info_spec.rb +++ b/Library/Homebrew/test/cask/info_spec.rb @@ -163,6 +163,42 @@ EOS end + it "prints login-items specified in the Cask" do + expect do + described_class.info(Cask::CaskLoader.load("with-login-items"), args:) + end.to output(<<~EOS).to_stdout + ==> with-login-items: 1.2.3 + https://brew.sh/ + Not installed + From: https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/w/with-login-items.rb + ==> Name + None + ==> Description + None + ==> Artifacts + Caffeine.app (App) + ==> Login Items + Caffeine.app + EOS + end + + it 'does not print "login-items" section divider if the login-items block has no output' do + expect do + described_class.info(Cask::CaskLoader.load("without-login-items"), args:) + end.to output(<<~EOS).to_stdout + ==> without-login-items: 1.2.3 + https://brew.sh/ + Not installed + From: https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/w/without-login-items.rb + ==> Name + None + ==> Description + None + ==> Artifacts + Caffeine.app (App) + EOS + end + it "prints install information for an installed Cask" do mktmpdir do |caskroom| FileUtils.mkdir caskroom/"2.61" diff --git a/Library/Homebrew/test/cask/reinstall_spec.rb b/Library/Homebrew/test/cask/reinstall_spec.rb index 6fb543e4724c8..9170074a4ca4b 100644 --- a/Library/Homebrew/test/cask/reinstall_spec.rb +++ b/Library/Homebrew/test/cask/reinstall_spec.rb @@ -12,6 +12,7 @@ output = Regexp.new <<~EOS ==> Fetching downloads for:.*caffeine ==> Uninstalling Cask local-caffeine + ==> Skipping processing of login_items ==> Backing App 'Caffeine.app' up to '.*Caffeine.app' ==> Removing App '.*Caffeine.app' ==> Purging files for version 1.2.3 of Cask local-caffeine @@ -32,6 +33,8 @@ output = Regexp.new <<~EOS ==> Fetching downloads for:.*caffeine + .* Cask .*caffeine .* + ==> Skipping processing of login_items ==> Backing App 'Caffeine.app' up to '.*Caffeine.app' ==> Removing App '.*Caffeine.app' ==> Dispatching zap stanza diff --git a/Library/Homebrew/test/cask/uninstall_spec.rb b/Library/Homebrew/test/cask/uninstall_spec.rb index 24ef6d34beb90..68ff393877adf 100644 --- a/Library/Homebrew/test/cask/uninstall_spec.rb +++ b/Library/Homebrew/test/cask/uninstall_spec.rb @@ -10,6 +10,7 @@ output = Regexp.new <<~EOS ==> Uninstalling Cask local-caffeine + ==> Skipping processing of login_items ==> Backing App 'Caffeine.app' up to '.*Caffeine.app' ==> Removing App '.*Caffeine.app' ==> Purging files for version 1.2.3 of Cask local-caffeine diff --git a/Library/Homebrew/test/rubocops/cask/uninstall_methods_order_spec.rb b/Library/Homebrew/test/rubocops/cask/uninstall_methods_order_spec.rb index 6ebf72c0ef621..f6e1836af5c46 100644 --- a/Library/Homebrew/test/rubocops/cask/uninstall_methods_order_spec.rb +++ b/Library/Homebrew/test/rubocops/cask/uninstall_methods_order_spec.rb @@ -267,7 +267,6 @@ launchctl: "com.example.foo", quit: "com.example.foo", signal: ["TERM", "com.example.foo"], - login_item: "FooApp", kext: "com.example.foo", script: { executable: "foo.sh", @@ -285,7 +284,6 @@ launchctl: "com.example.foo", quit: "com.example.foo", signal: ["TERM", "com.example.foo"], - login_item: "FooApp", kext: "com.example.foo", script: { executable: "foo.sh", diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/everything.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/everything.rb index 2f8279dc7e58a..939c0bd48ce81 100644 --- a/Library/Homebrew/test/support/fixtures/cask/Casks/everything.rb +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/everything.rb @@ -33,6 +33,7 @@ sudo: true, print_stderr: false, } + login_items "Everything" uninstall launchctl: "com.every.thing.agent", signal: [ diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-installable.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-installable.rb index 67d58c52d24ea..7d5d00e895ff2 100644 --- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-installable.rb +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-installable.rb @@ -6,16 +6,16 @@ homepage "https://brew.sh/fancy-pkg" pkg "MyFancyPkg/Fancy.pkg" + login_items "Fancy" - uninstall quit: "my.fancy.package.app", - login_item: "Fancy", - script: { executable: "MyFancyPkg/FancyUninstaller.tool", args: ["--please"] }, - delete: [ + uninstall quit: "my.fancy.package.app", + script: { executable: "MyFancyPkg/FancyUninstaller.tool", args: ["--please"] }, + delete: [ "#{TEST_TMPDIR}/absolute_path", "#{TEST_TMPDIR}/glob_path*", "/another/impermissible/../relative/path", "impermissible/relative/path", "~/path_with_tilde", ], - rmdir: "#{TEST_TMPDIR}/empty_directory_path" + rmdir: "#{TEST_TMPDIR}/empty_directory_path" end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-login-items.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-login-items.rb new file mode 100644 index 0000000000000..b69abf37326db --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-login-items.rb @@ -0,0 +1,9 @@ +cask "with-login-items" do + version "1.2.3" + + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage "https://brew.sh/" + + app "Caffeine.app" + login_items "Caffeine.app" +end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-login-item.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-login-item.rb index 14c52cab6d2f1..daf3ab8bf6bf9 100644 --- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-login-item.rb +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-uninstall-login-item.rb @@ -1,3 +1,4 @@ +# TODO: Check if this fixture is still needed once the login_items are moved to the top-level cask "with-uninstall-login-item" do version "1.2.3" sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" @@ -6,6 +7,5 @@ homepage "https://brew.sh/fancy-pkg" pkg "MyFancyPkg/Fancy.pkg" - - uninstall login_item: "Fancy" + login_items "Fancy" end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap-login-item.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap-login-item.rb index 7e6678af8007f..c9fa76ab36e0b 100644 --- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap-login-item.rb +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap-login-item.rb @@ -1,3 +1,4 @@ +# TODO: Check if this fixture is still needed once the login_items are moved to the top-level cask "with-zap-login-item" do version "1.2.3" sha256 "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" @@ -6,6 +7,5 @@ homepage "https://brew.sh/fancy-pkg" pkg "MyFancyPkg/Fancy.pkg" - - zap login_item: "Fancy" + login_items "Fancy" end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap.rb index 69f49f5dcfb70..ad3d6aab7aa8f 100644 --- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap.rb +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-zap.rb @@ -6,14 +6,14 @@ homepage "https://brew.sh/fancy-pkg" pkg "MyFancyPkg/Fancy.pkg" + login_items "Fancy" uninstall quit: "my.fancy.package.app.from.uninstall" - zap quit: "my.fancy.package.app", - login_item: "Fancy", - script: { + zap quit: "my.fancy.package.app", + script: { executable: "MyFancyPkg/FancyUninstaller.tool", args: ["--please"], }, - delete: "~/Library/Preferences/my.fancy.app.plist" + delete: "~/Library/Preferences/my.fancy.app.plist" end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/without-login-items.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/without-login-items.rb new file mode 100644 index 0000000000000..9de3e7cb1b763 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/without-login-items.rb @@ -0,0 +1,8 @@ +cask "without-login-items" do + version "1.2.3" + + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage "https://brew.sh/" + + app "Caffeine.app" +end diff --git a/Library/Homebrew/test/support/fixtures/cask/everything.json b/Library/Homebrew/test/support/fixtures/cask/everything.json index 38d85c9f732b1..4d3c1027462fa 100644 --- a/Library/Homebrew/test/support/fixtures/cask/everything.json +++ b/Library/Homebrew/test/support/fixtures/cask/everything.json @@ -123,5 +123,8 @@ "ruby_source_path": "Casks/everything.rb", "ruby_source_checksum": { "sha256": "d3c19b564ee5a17f22191599ad795a6cc9c4758d0e1269f2d13207155b378dea" - } + }, + "login_items": [ + "Everything" + ] } diff --git a/docs/Cask-Cookbook.md b/docs/Cask-Cookbook.md index e2dae6dda6f42..0494a8e5f2ed0 100644 --- a/docs/Cask-Cookbook.md +++ b/docs/Cask-Cookbook.md @@ -92,6 +92,7 @@ Having a common order for stanzas makes casks easier to update and parse. Below vst3_plugin artifact, target: # target: shown here as is required with `artifact` stage_only + login_items preflight @@ -171,6 +172,7 @@ Each cask must declare one or more [artifacts](/rubydoc/Cask/Artifact.html) (i.e | [`postflight`](#stanza-flight) | yes | Ruby block containing postflight install operations. | | `uninstall_preflight` | yes | Ruby block containing preflight uninstall operations (needed only in very rare cases). | | `uninstall_postflight` | yes | Ruby block containing postflight uninstall operations. | +| `login_items` | no | (string or array) - names of login items. | | [`language`](#stanza-language) | required | Ruby block, called with language code parameters, containing other stanzas and/or a return value. | | `container nested:` | no | Relative path to an inner container that must be extracted before moving on with the installation. This allows for support of `.dmg` inside `.tar`, `.zip` inside `.dmg`, etc. (Example: [blocs.rb](https://github.com/Homebrew/homebrew-cask/blob/aa461148bbb5119af26b82cccf5003e2b4e50d95/Casks/b/blocs.rb#L17-L19)) | | `container type:` | no | Symbol to override container-type autodetect. May be one of: `:air`, `:bz2`, `:cab`, `:dmg`, `:generic_unar`, `:gzip`, `:otf`, `:pkg`, `:rar`, `:seven_zip`, `:sit`, `:tar`, `:ttf`, `:xar`, `:zip`, `:naked`. (Example: [parse.rb](https://github.com/Homebrew/homebrew-cask/blob/aa461148bbb5119af26b82cccf5003e2b4e50d95/Casks/p/parse.rb#L10)) | @@ -521,6 +523,14 @@ Refer to [Deprecating, Disabling and Removing Casks](Deprecating-Disabling-and-R + desc "Sound and music editor" ``` +### Stanza: `login_items` + +Login items associated with an application bundle on disk can be listed using [`list_login_items_for_app`](https://github.com/Homebrew/homebrew-cask/blob/HEAD/developer/bin/list_login_items_for_app): + +```bash +"$(brew --repository homebrew/cask)/developer/bin/list_login_items_for_app" '/path/to/application.app' +``` + ### Stanza: `*flight` The stanzas `preflight`, `postflight`, `uninstall_preflight`, and `uninstall_postflight` define operations to be run before or after installation or uninstallation. @@ -795,7 +805,7 @@ The easiest and most useful `uninstall` directive is [`pkgutil:`](#uninstall-pkg * **`early_script:`** (string or hash) - like [`script:`](#uninstall-script), but runs early (for special cases, best avoided) * [`launchctl:`](#uninstall-launchctl) (string or array) - IDs of `launchd` jobs to remove * [`quit:`](#uninstall-quit) (string or array) - bundle IDs of running applications to quit (does not run when uninstall is initiated by `brew upgrade` or `brew reinstall`) -* [`signal:`](#uninstall-signal) (array of arrays) - signal numbers and bundle IDs of running applications to send a Unix signal to, for when `quit:` does not work (does not run when uninstall is initiated by `brew upgrade` or `brew reinstall`) +* [`signal:`](#uninstall-signal) (array of arrays) - signal numbers and bundle IDs of running applications to send a Unix signal to - for when `quit:` does not work (does not run when uninstall is initiated by `brew upgrade` or `brew reinstall`) * [`login_item:`](#uninstall-login_item) (string or array) - names of login items to remove * [`kext:`](#uninstall-kext) (string or array) - bundle IDs of kexts to unload from the system * [`script:`](#uninstall-script) (string or hash) - relative path to an uninstall script to be run via *sudo*; use hash if args are needed @@ -892,16 +902,6 @@ Note that when multiple running processes match the given bundle ID, all matchin Unlike `quit:` directives, Unix signals originate from the current user, not from the superuser. This is construed as a safety feature, since the superuser is capable of bringing down the system via signals. However, this inconsistency could also be considered a bug, and may be addressed in some fashion in a future version. -#### `uninstall` *login_item* - -Login items associated with an application bundle on disk can be listed using [`list_login_items_for_app`](https://github.com/Homebrew/homebrew-cask/blob/HEAD/developer/bin/list_login_items_for_app): - -```bash -"$(brew --repository homebrew/cask)/developer/bin/list_login_items_for_app" '/path/to/application.app' -``` - -Note that you will likely need to have opened the app at least once for any login items to be present. - #### `uninstall` *kext* IDs for currently loaded kernel extensions can be listed using [`list_loaded_kext_ids`](https://github.com/Homebrew/homebrew-cask/blob/HEAD/developer/bin/list_loaded_kext_ids):