Cask Cookbook

Each cask is a Ruby block, beginning with a special header line. The cask definition itself is always enclosed in a do … end block. Example:

cask "anybar" do
  version "0.2.3"
  sha256 "c87dbc6aff5411676a471e84905d69c671b62b93b1210bd95c9d776d087de95c"

  url "https://github.com/tonsky/AnyBar/releases/download/#{version}/AnyBar-#{version}.zip"
  name "AnyBar"
  desc "Menu bar status indicator"
  homepage "https://github.com/tonsky/AnyBar"

  app "AnyBar.app"
end

The cask language is declarative

Each cask contains a series of stanzas (or “fields”) which declare how the software is to be obtained and installed. In a declarative language, the author does not need to worry about order. As long as all the needed fields are present, Homebrew Cask will figure out what needs to be done at install time.

To make maintenance easier, the most-frequently-updated stanzas are usually placed at the top. But that’s a convention, not a rule.

Exception: do blocks such as postflight may enclose a block of pure Ruby code. Lines within that block follow a procedural (order-dependent) paradigm.

Header line details

The Cask name (<cask-token>) on the header line cask <cask-token> do should match the cask filename, without the .rb extension, enclosed in double quotes.

There are currently some arbitrary limitations on cask tokens which are in the process of being removed. GitHub Actions will catch any errors during the transition.

Stanza order

Having a common order for stanzas makes casks easier to update and parse. Below is the complete stanza sequence (no cask will have all stanzas). The empty lines shown here are also important, as they help to visually delimit information.

version
sha256

language

url
name
desc
homepage

livecheck

deprecate!
disable!

auto_updates
conflicts_with
depends_on
container

suite
app
pkg
installer
binary
manpage
colorpicker
dictionary
font
input_method
internet_plugin
keyboard_layout
prefpane
qlplugin
mdimporter
screen_saver
service
audio_unit_plugin
vst_plugin
vst3_plugin
artifact, target: # target: shown here as is required with `artifact`
stage_only

preflight

postflight

uninstall_preflight

uninstall_postflight

uninstall

zap

caveats

Note that every stanza that has additional parameters (:symbols after a ,) shall have them on separate lines, one per line, in alphabetical order. An exception is target: which typically consists of short lines.

Stanzas

Required stanzas

Each of the following stanzas is required for every cask.

name multiple occurrences allowed? value
version no Application version.
sha256 no SHA-256 checksum of the file downloaded from url, calculated by the command shasum -a 256 <file>. Can be suppressed by using the special value :no_check.
url no URL to the .dmg/.zip/.tgz/.tbz2 file that contains the application. A comment should be added if the domains in the url and homepage stanzas differ. Block syntax should be used for URLs that change on every visit.
name yes String providing the full and proper name defined by the vendor.
desc no One-line description of the cask. Shown when running brew info.
homepage no Application homepage; used for the brew home command.
livecheck no Ruby block describing how to find updates for this cask. Supersedes appcast.
depends_on yes List of dependencies and requirements for this cask.
zap yes Additional procedures for a more complete uninstall, including user files and shared resources.

At least one artifact stanza is also required

Each cask must declare one or more artifacts (i.e. something to install).

name multiple occurrences allowed? value
app yes Relative path to an .app that should be moved into the /Applications folder on installation.
suite yes Relative path to a containing directory that should be moved into the /Applications folder on installation.
pkg yes Relative path to a .pkg file containing the distribution.
installer yes Describes an executable which must be run to complete the installation.
binary yes Relative path to a Binary that should be linked into the $(brew --prefix)/bin folder on installation.
manpage yes Relative path to a Man Page that should be linked into the respective man page folder on installation, e.g. /usr/local/share/man/man3 for my_app.3.
colorpicker yes Relative path to a ColorPicker plugin that should be moved into the ~/Library/ColorPickers folder on installation.
dictionary yes Relative path to a Dictionary that should be moved into the ~/Library/Dictionaries folder on installation.
font yes Relative path to a Font that should be moved into the ~/Library/Fonts folder on installation.
input_method yes Relative path to an Input Method that should be moved into the ~/Library/Input Methods folder on installation.
internet_plugin yes Relative path to an Internet Plugin that should be moved into the ~/Library/Internet Plug-Ins folder on installation.
keyboard_layout yes Relative path to a Keyboard Layout that should be moved into the /Library/Keyboard Layouts folder on installation.
prefpane yes Relative path to a Preference Pane that should be moved into the ~/Library/PreferencePanes folder on installation.
qlplugin yes Relative path to a Quick Look Plugin that should be moved into the ~/Library/QuickLook folder on installation.
mdimporter yes Relative path to a Spotlight Metadata Importer that should be moved into the ~/Library/Spotlight folder on installation.
screen_saver yes Relative path to a Screen Saver that should be moved into the ~/Library/Screen Savers folder on installation.
service yes Relative path to a Service that should be moved into the ~/Library/Services folder on installation.
audio_unit_plugin yes Relative path to an Audio Unit Plugin that should be moved into the ~/Library/Audio/Components folder on installation.
vst_plugin yes Relative path to a VST Plugin that should be moved into the ~/Library/Audio/VST folder on installation.
vst3_plugin yes Relative path to a VST3 Plugin that should be moved into the ~/Library/Audio/VST3 folder on installation.
artifact yes Relative path to an arbitrary path that should be moved on installation. Must provide an absolute path as a target. (Example: free-gpgmail.rb) This is only for unusual cases; the app stanza is strongly preferred when moving .app bundles.
stage_only no true. Asserts that the cask contains no activatable artifacts.

Optional stanzas

name multiple occurrences allowed? value
uninstall yes Procedures to uninstall a cask. Optional unless the pkg stanza is used.
conflicts_with yes List of conflicts with this cask (not yet functional).
caveats yes String or Ruby block providing the user with cask-specific information at install time.
deprecate! no Date as a String in YYYY-MM-DD format and a String or Symbol providing a reason.
disable! no Date as a String in YYYY-MM-DD format and a String or Symbol providing a reason.
preflight yes Ruby block containing preflight install operations (needed only in very rare cases).
postflight 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.
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)
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)
auto_updates no true. Asserts that the cask artifacts auto-update. Use if Check for Updates… or similar is present in an app menu, but not if it only opens a webpage and does not do the download and installation for you.

Stanza descriptions

Stanza: app

In the simple case of a string argument to app, the source file is moved to the target /Applications directory. For example:

app "Alfred 2.app"

by default moves the source to:

/Applications/Alfred 2.app

Renaming the target

You can rename the target which appears in your /Applications directory by adding a target: key to app. Example (from scala-ide.rb):

app "eclipse.app", target: "Scala IDE.app"

target may contain an absolute path

If target: has a leading slash, it is interpreted as an absolute path. The containing directory for the absolute path will be created if it does not already exist. Example (from sapmachine-jdk.rb):

artifact "sapmachine-jdk-#{version}.jdk", target: "/Library/Java/JavaVirtualMachines/sapmachine-jdk-#{version}.jdk"

target works on most artifact types

The target: key works similarly for most cask artifacts, such as app, binary, colorpicker, dictionary, font, input_method, internet_plugin, keyboard_layout, prefpane, qlplugin, mdimporter, screen_saver, service, suite, audio_unit_plugin, vst_plugin, vst3_plugin, and artifact.

target should only be used in select cases

Don’t use target: for aesthetic reasons, like removing version numbers (app "Slack #{version}.app", target: "Slack.app"). Use it when it makes sense functionally and document your reason clearly in the cask, using one of the templates: for clarity; for consistency; to prevent conflicts; due to developer suggestion.

Stanza: binary

In the simple case of a string argument to binary, the source file is linked into the $(brew --prefix)/bin directory on installation. For example (from operadriver.rb):

binary "operadriver_mac64/operadriver"

creates a symlink to:

$(brew --prefix)/bin/operadriver

from a source file such as:

$(brew --caskroom)/operadriver/106.0.5249.119/operadriver_mac64/operadriver

A binary (or multiple) can also be contained in an application bundle:

app "Atom.app"
binary "#{appdir}/Atom.app/Contents/Resources/app/apm/bin/apm"

You can rename the target which appears in your binaries directory by adding a target: key to binary:

binary "#{appdir}/Atom.app/Contents/Resources/app/atom.sh", target: "atom"

Behaviour and usage of target: is the same as with app. However, for binary the select cases don’t apply as rigidly. It’s fine to take extra liberties with target: to be consistent with other command-line tools, like changing case, removing an extension, or cleaning up the name.

Stanza: caveats

Sometimes there are particularities with the installation of a piece of software that cannot or should not be handled programmatically by Homebrew Cask. In those instances, caveats is the way to inform the user. Information in caveats is displayed when a cask is invoked with either install or info.

To avoid flooding users with too many messages (thus desensitising them to the important ones), caveats should be used sparingly and exclusively for installation-related matters. If you’re not sure a caveat you find pertinent is installation-related or not, ask a maintainer. As a general rule, if your case isn’t already covered in our comprehensive caveats Mini-DSL, it’s unlikely to be accepted.

caveats as a string

When caveats is a string, it is evaluated at compile time. The following methods are available for interpolation if caveats is placed in its customary position at the end of the cask:

method description
token the cask token
version the cask version
homepage the cask homepage
caskroom_path the containing directory for this cask: $(brew --caskroom)/<token> (only available with block form)
staged_path the staged location for this cask, including version number: $(brew --caskroom)/<token>/<version> (only available with block form)

Example:

caveats "Using #{token} may be hazardous to your health."

caveats as a block

When caveats is a Ruby block, evaluation is deferred until install time. Within a block you may refer to the @cask instance variable, and invoke any method available on @cask.

caveats mini-DSL

There is a mini-DSL available within caveats blocks.

The following methods may be called to generate standard warning messages:

method description
path_environment_variable "path" Users should make sure path is in their PATH environment variable.
zsh_path_helper "path" zsh users must take additional steps to make sure path is in their PATH environment variable.
depends_on_java "version" Users should make sure they have the specified version of Java installed. version can be exact (e.g. 6), a minimum (e.g. 7+), or omitted (when any version works).
requires_rosetta The cask requires Rosetta 2 for it to run on Apple Silicon.
logout Users should log out and log back in to complete installation.
reboot Users should reboot to complete installation.
files_in_usr_local The cask installs files to /usr/local, which may confuse Homebrew.
kext Users may need to enable their kexts in System Settings → Privacy & Security (or System Preferences → Security & Privacy → General in earlier macOS versions).
unsigned_accessibility Users will need to re-enable the app on each update in System Settings → Privacy & Security (or System Preferences → Security & Privacy → Privacy in earlier macOS versions) as it is unsigned.
license "web_page" Users may find the software’s usage license at web_page.
free_license "web_page" Users may obtain an official license to use the software at web_page.

Example:

caveats do
  path_environment_variable "/usr/texbin"
end

Stanza: deprecate! / disable!

deprecate! and disable! are used to declare that a cask is no longer functional or supported. Casks that contain a deprecate! stanza can still be installed, but will print a warning message when they are installed or upgraded. Casks that contain a disable! stanza cannot be installed or upgraded and will print an error message.

The syntax for both stanzas is the same:

deprecate! date: "YYYY-MM-DD", because: "is ..."
disable! date: "YYYY-MM-DD", because: "is ..."

# Or with a preset reason (see the `because:` argument section below)
deprecate! date: "YYYY-MM-DD", because: :discontinued
disable! date: "YYYY-MM-DD", because: :discontinued

date: argument

The date: argument controls when the deprecation or disabling will take effect. Casks that have a deprecate! stanza with a date in the future will not be treated as being deprecated until that date. Casks that have a disable! stanza with a date in the future will be automatically deprecated until that date, at which point they will be disabled.

because: argument

The because: argument accepts a reason for the cask being deprecated or disabled. The info message will be <cask> is deprecated because it <reason>!, so format the reason to fit that sentence. For example, because: "is broken" will result in <cask> is deprecated because it is broken!.

The because: argument can also accept a symbol that corresponds to a preset reason, for example:

deprecate! date: "YYYY-MM-DD", because: :discontinued

A complete list of allowable symbols can be found in the DeprecateDisable module.

Stanza: conflicts_with

conflicts_with is used to declare conflicts that keep a cask from installing or working correctly.

conflicts_with cask

The value should be another cask token.

Example: the macFUSE cask, which conflicts with macfuse-dev.

conflicts_with cask: "macfuse-dev"

conflicts_with formula

Note: conflicts_with formula: is a stub and is not yet functional.

The value should be another formula name.

Example: MacVim, which conflicts with the macvim formula.

conflicts_with formula: "macvim"

Stanza: depends_on

depends_on is used to declare dependencies and requirements for a cask. depends_on is not consulted until install is attempted.

depends_on cask

The value should be another cask token, needed by the current cask.

Example: NTFSTool, which depends on macFUSE.

depends_on cask: "macfuse"

depends_on formula

The value should name a Homebrew formula needed by the cask.

Example: some distributions are contained in archive formats such as 7z which are not supported by stock Apple tools. For these cases, a more capable archive reader may be pulled in at install time by declaring a dependency on the unar formula:

depends_on formula: "unar"

depends_on macos

Requiring an exact macOS release

The value for depends_on macos: may be a symbol or an array of symbols, listing the exact compatible macOS releases. The available values for macOS releases are defined in the MacOSVersion class.

Only major releases are covered (10.x numbers containing a single dot or whole numbers since macOS 11). The symbol form is used for readability. The following are all valid ways to enumerate the exact macOS release requirements for a cask:

depends_on macos: :big_sur
depends_on macos: [
  :catalina,
  :big_sur,
]
Setting a minimum macOS release

depends_on macos: can also accept a string starting with a comparison operator such as >=, followed by an macOS release in the form above. The following is a valid expression meaning “at least macOS Big Sur (11.0)”:

depends_on macos: ">= :big_sur"

A comparison expression cannot be combined with any other form of depends_on macos:.

depends_on arch

The value for depends_on arch: may be a symbol or an array of symbols, listing the hardware compatibility requirements for a cask. The requirement is satisfied at install time if any one of multiple arch: values matches the user’s hardware.

The available symbols for hardware are:

symbol meaning
:x86_64 64-bit Intel
:intel 64-bit Intel
:arm64 Apple Silicon

The following are all valid expressions:

depends_on arch: :intel
depends_on arch: :x86_64            # same meaning as above
depends_on arch: [:x86_64]          # same meaning as above
depends_on arch: :arm64

depends_on parameters

key description
formula: Homebrew formula
cask: cask token
macos: symbol, array, or string comparison expression defining macOS release requirements
arch: symbol or array defining hardware requirements
java: stub - not yet functional

Stanza: desc

desc accepts a single-line UTF-8 string containing a short description of the software. It’s used to help with searchability and disambiguation, thus it must concisely describe what the software does (or what you can accomplish with it).

desc is not for app slogans! Vendors’ descriptions tend to be filled with generic adjectives such as “modern” and “lightweight”. Those are meaningless marketing fluff (do you ever see apps proudly describing themselves as outdated and bulky?) which must be deleted. It’s fine to use the information on the software’s website as a starting point, but it will require editing in almost all cases.

Dos and Don’ts

Stanza: *flight

The stanzas preflight, postflight, uninstall_preflight, and uninstall_postflight define operations to be run before or after installation or uninstallation.

Evaluation of blocks is always deferred

The Ruby blocks defined by these stanzas are not evaluated until install time or uninstall time. Within a block you may refer to the @cask instance variable, and invoke any method available on @cask.

*flight mini-DSL

There is a mini-DSL available within these blocks.

The following methods may be called to perform standard tasks:

method availability description
set_ownership(paths) preflight, postflight, uninstall_preflight Set user and group ownership of paths. (Example: docker-toolbox.rb)
set_permissions(paths, permissions_str) preflight, postflight, uninstall_preflight Set permissions in paths to permissions_str. (Example: ngrok.rb)

set_ownership(paths) defaults user ownership to the current user and group ownership to staff. These can be changed by passing in extra options: set_ownership(paths, user: "user", group: "group"). (Example: hummingbird.rb)

Stanza: installer

This stanza must always be accompanied by uninstall.

The installer stanza takes a series of key-value pairs, the first key of which must be manual: or script:.

installer manual

installer manual: takes a single string value, describing a GUI installer which must be run by the user at a later time. The path may be absolute, or relative to the cask. Example (from rubymotion.rb):

installer manual: "RubyMotion Installer.app"

installer script

installer script: introduces a series of key-value pairs describing a command which will automate completion of the install. It should never be used for interactive installations. The form is similar to uninstall script::

key value
executable: path to an install script to be run
args: array of arguments to the install script
input: array of lines of input to be sent to stdin of the script
must_succeed: set to false if the script is allowed to fail
sudo: set to true if the script needs sudo

The path may be absolute, or relative to the cask. Example (from miniforge.rb):

installer script: {
  executable: "Miniforge3-#{version}-MacOSX-x86_64.sh",
  args:       ["-b", "-p", "#{caskroom_path}/base"],
}

If the installer script: does not require any of the key-values it can point directly to the path of the install script:

installer script: "#{staged_path}/install.sh"

Stanza: language

The language stanza can match ISO 639-1 language codes, script codes (ISO 15924) and regional identifiers (ISO 3166-1 Alpha 2), or a combination thereof.

US English should always be used as the default language:

language "zh", "CN" do
  "zh_CN"
end

language "de" do
  "de_DE"
end

language "en-GB" do
  "en_GB"
end

language "en", default: true do
  "en_US"
end

Note that the following are not the same:

language "en", "GB" do
  # matches all locales containing "en" or "GB"
end

language "en-GB" do
  # matches only locales containing "en" and "GB"
end

The return value of the matching language block can be accessed by simply calling language.

homepage "https://example.org/#{language}"

Examples: firefox.rb, battle-net.rb

Installation

To install a cask in a specific language, you can pass the --language= option to brew install:

brew install firefox --language=it

Stanza: livecheck

The livecheck stanza is used to automatically fetch the latest version of a cask from changelogs, release notes, appcasts, etc.

Every livecheck block must contain a url, which can be either a string or a symbol pointing to other URLs in the cask (:url or :homepage).

Refer to the brew livecheck documentation for how to write a livecheck block.

Stanza: name

name accepts a UTF-8 string defining the name of the software, including capitalization and punctuation. It is used to help with searchability and disambiguation.

Unlike the token, which is simplified and reduced to a limited set of characters, the name stanza can include the proper capitalization, spacing and punctuation to match the official name of the software. For disambiguation purposes, it is recommended to spell out the name of the application, including the vendor name if necessary. A good example is the pycharm-ce cask, whose name is spelled out as Jetbrains PyCharm Community Edition, even though it is likely never referenced as such anywhere.

Additional details about the software can be provided in the desc stanza.

The name stanza can be repeated multiple times if there are useful alternative names. The first instance should use the Latin alphabet. For example, see the cave-story cask, whose original name does not use the Latin alphabet.

Stanza: pkg

This stanza must always be accompanied by uninstall.

The first argument to the pkg stanza should be a relative path to the .pkg file to be installed. Example:

pkg "Unity.pkg"

Subsequent arguments to pkg are key/value pairs which modify the install process. Currently supported keys are allow_untrusted: and choices:.

pkg allow_untrusted

pkg allow_untrusted: true can be used to install a .pkg containing an untrusted certificate by passing -allowUntrusted to /usr/sbin/installer.

This option is not permitted in official Homebrew Cask taps; it is only provided for use in third-party taps or local casks.

Historical example (from alinof-timer.rb):

pkg "AlinofTimer.pkg", allow_untrusted: true

pkg choices

pkg choices: can be used to override a .pkg’s default install options via -applyChoiceChangesXML. It uses a deserialized version of the choiceChanges property list (refer to the CHOICE CHANGES FILE section of the installer manual page by running man -P 'less --pattern "^CHOICE CHANGES FILE"' installer).

Running this macOS installer command:

installer -showChoicesXML -pkg '/path/to/my.pkg'

will output XML that you can use to extract the choices: values, as well as their equivalents to the GUI options.

See this pull request for wireshark-chmodbpf and this one for wine-staging for some examples of the procedure.

Example (from lando.rb):

pkg "LandoInstaller.pkg",
    choices: [
      {
        "choiceIdentifier" => "choiceDocker",
        "choiceAttribute"  => "selected",
        "attributeSetting" => 0,
      },
      {
        "choiceIdentifier" => "choiceLando",
        "choiceAttribute"  => "selected",
        "attributeSetting" => 1,
      },
    ]

Example (from microsoft-office.rb):

pkg "Microsoft_365_and_Office_#{version}_Installer.pkg",
    choices: [
      {
        "choiceIdentifier" => "com.microsoft.autoupdate", # Office16_all_autoupdate.pkg
        "choiceAttribute"  => "selected",
        "attributeSetting" => 0,
      },
    ]

Stanza: sha256

Calculating the SHA-256

The sha256 value is usually calculated by the shasum command:

shasum --algorithm 256 <file>

Special value :no_check

The special value sha256 :no_check is used to turn off SHA checking whenever checksumming is impractical due to the upstream configuration.

version :latest requires sha256 :no_check, and this pairing is common. However, sha256 :no_check does not require version :latest.

We use a checksum whenever possible.

Stanza: suite

Some distributions provide a suite of multiple applications, or an application with required data, to be installed together in a subdirectory of /Applications.

For these casks, use the suite stanza to define the directory containing the application suite. Example (from racket.rb):

suite "Racket v#{version}"

The value of suite is never an .app bundle, but a plain directory.

Stanza: uninstall

If you cannot design a working uninstall stanza, please submit your cask anyway. The maintainers can help you write an uninstall stanza, just ask!

uninstall pkgutil: is the easiest and most useful

The easiest and most useful uninstall directive is pkgutil:. It should cover most use cases.

uninstall is required for casks that install using pkg or installer manual:

For most casks, uninstall actions are determined automatically, and an explicit uninstall stanza is not needed. However, a cask which uses the pkg or installer manual: stanzas will not know how to uninstall correctly unless an uninstall stanza is given.

So, while the cask DSL does not enforce the requirement, it is much better for users if every pkg and installer manual: has a corresponding uninstall.

The uninstall stanza is available for non-pkg casks, and is useful for a few corner cases. However, the documentation below concerns the typical case of using uninstall to define procedures for a pkg.

There are multiple uninstall techniques

Since pkg installers can do arbitrary things, different techniques are needed to uninstall in each case. You may need to specify one, or several, of the following key/value pairs as arguments to uninstall.

Summary of keys

Each uninstall technique is applied according to the order above. The order in which uninstall keys appear in the cask file is ignored.

For assistance filling in the right values for uninstall keys, there are several helper scripts found under developer/bin in the Homebrew Cask repository. Each of these scripts responds to the -help option with additional documentation.

Working out an uninstall stanza is easiest when done on a system where the package is currently installed and operational. To operate on an uninstalled .pkg file, see Working With a .pkg File Manually, below.

uninstall pkgutil

This is the most useful uninstall key. pkgutil: is often sufficient to completely uninstall a pkg, and is strongly preferred over delete:.

IDs for the most recently installed packages can be listed using list_recent_pkg_ids:

"$(brew --repository homebrew/cask)/developer/bin/list_recent_pkg_ids"

pkgutil: also accepts a regular expression match against multiple package IDs. The regular expressions are somewhat nonstandard. To test a pkgutil: regular expression against currently installed packages, use list_pkg_ids_by_regexp:

"$(brew --repository homebrew/cask)/developer/bin/list_pkg_ids_by_regexp" <regular-expression>

List files associated with a package ID

Once you know the ID for an installed package (see above), you can list all files on your system associated with that package ID using the macOS pkgutil command:

pkgutil --files <package.id.goes.here>

Listing the associated files can help you assess whether the package included any launchd jobs or kernel extensions (kexts).

uninstall launchctl

IDs for currently loaded launchd jobs can be listed using list_loaded_launchjob_ids:

"$(brew --repository homebrew/cask)/developer/bin/list_loaded_launchjob_ids"

IDs for all installed launchd jobs can be listed using list_installed_launchjob_ids:

"$(brew --repository homebrew/cask)/developer/bin/list_installed_launchjob_ids"

uninstall quit

Bundle IDs for currently running applications can be listed using list_running_app_ids:

"$(brew --repository homebrew/cask)/developer/bin/list_running_app_ids"

Bundle IDs inside an application bundle on disk can be listed using list_ids_in_app:

"$(brew --repository homebrew/cask)/developer/bin/list_ids_in_app" '/path/to/application.app'

uninstall signal

signal: should only be needed in the rare case that a process does not respond to quit:.

Bundle IDs for signal: targets may be obtained in the same way as for quit:. The value for signal: is an array-of-arrays, with each cell containing two elements: the desired Unix signal followed by the corresponding bundle ID.

The Unix signal may be given in numeric or string form (see the kill(1) man page for more details).

The elements of the signal: array are applied in order, only if there is an existing process associated the bundle ID, and stopping when that process terminates. A bundle ID may be repeated to send more than one signal to the same process.

It is better to use the least-severe signals that are sufficient to stop a process. The KILL signal in particular can have unwanted side effects.

An example, with commonly used signals in ascending order of severity:

uninstall signal: [
  ["TERM", "fr.madrau.switchresx.daemon"],
  ["QUIT", "fr.madrau.switchresx.daemon"],
  ["INT",  "fr.madrau.switchresx.daemon"],
  ["HUP",  "fr.madrau.switchresx.daemon"],
  ["KILL", "fr.madrau.switchresx.daemon"],
]

Note that when multiple running processes match the given bundle ID, all matching processes will be signaled.

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 may also be considered a bug, and should 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:

"$(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:

"$(brew --repository homebrew/cask)/developer/bin/list_loaded_kext_ids"

IDs inside a kext bundle on disk can be listed using list_id_in_kext:

"$(brew --repository homebrew/cask)/developer/bin/list_id_in_kext" '/path/to/name.kext'

uninstall script

uninstall script: introduces a series of key-value pairs describing a command which will automate completion of the uninstall. Example (from virtualbox.rb):

uninstall script:  {
            executable: "VirtualBox_Uninstall.tool",
            args:       ["--unattended"],
            sudo:       true,
          },
          pkgutil: "org.virtualbox.pkg.*",
          delete:  "/usr/local/bin/vboximg-mount"

It is important to note that, although script: in the above example does attempt to completely uninstall the pkg, it should not be used in place of pkgutil:, but as a complement when possible.

uninstall delete

delete: should only be used as a last resort, if other uninstall methods are insufficient.

Arguments to uninstall delete: should use the following basic rules:

To remove user-specific files, use the zap stanza.

uninstall trash

trash: arguments follow the same rules listed above for delete:.

Working with a .pkg file manually

Advanced users may wish to work with a .pkg file manually, without having the package installed.

A list of files which may be installed from a .pkg can be extracted using list_payload_in_pkg:

"$(brew --repository homebrew/cask)/developer/bin/list_payload_in_pkg" '/path/to/my.pkg'

Candidate application names helpful for determining the name of a cask may be extracted from a .pkg file using list_apps_in_pkg:

"$(brew --repository homebrew/cask)/developer/bin/list_apps_in_pkg" '/path/to/my.pkg'

Candidate package IDs which may be useful in a pkgutil: key may be extracted from a .pkg file using list_ids_in_pkg:

"$(brew --repository homebrew/cask)/developer/bin/list_ids_in_pkg" '/path/to/my.pkg'

A fully manual method for finding bundle IDs in a package file follows:

  1. Unpack /path/to/my.pkg (replace with your package name) with pkgutil --expand /path/to/my.pkg /tmp/expanded.unpkg.
  2. The unpacked package is a folder. Bundle IDs are contained within files named PackageInfo. These files can be found with the command find /tmp/expanded.unpkg -name PackageInfo.
  3. PackageInfo files are XML files, and bundle IDs are found within the identifier attributes of <pkg-info> tags that look like <pkg-info ... identifier="com.oracle.jdk7u51" ... >, where extraneous attributes have been snipped out and replaced with ellipses.
  4. Kexts inside packages are also described in PackageInfo files. If any kernel extensions are present, the command find /tmp/expanded.unpkg -name PackageInfo -print0 | xargs -0 grep -i kext should return a <bundle id> tag with a path attribute that contains a .kext extension, for example <bundle id="com.wavtap.driver.WavTap" ... path="./WavTap.kext" ... />.
  5. Once bundle IDs have been identified, the unpacked package directory can be deleted.

Stanza: url

HTTPS URLs are preferred

If available, an HTTPS URL is preferred. A plain HTTP URL should only be used in the absence of a secure alternative.

Additional url parameters

When a plain URL string is insufficient to fetch a file, additional information may be provided to the curl-based downloader, in the form of key/value pairs appended to url:

key value
verified: string repeating the beginning of url, for verification purposes
using: the symbols :post and :homebrew_curl are the only legal values
cookies: hash of cookies to be set in the download request (Example: oracle-jdk-javadoc.rb)
referer: string holding the URL to set as referer in the download request (Example: firealpaca.rb)
header: string or array of strings holding the header(s) to set for the download request (Example: pull-6545, issue-15590)
user_agent: string holding the user agent to set for the download request. Can also be set to the symbol :fake, which will use a generic browser-like user agent string. We prefer :fake when the server does not require a specific user agent.
data: hash of parameters to be set in the POST request (Example: segger-jlink.rb)

When URL and homepage domains differ, add verified:

When the domains of url and homepage differ, the discrepancy should be documented with the verified: parameter, repeating the smallest possible portion of the URL that uniquely identifies the app or vendor, excluding the protocol. (Example: 1password-cli.rb)

This must be added so a user auditing the cask knows the URL was verified by the Homebrew Cask team as the one provided by the vendor, even though it may look unofficial. It is our responsibility as Homebrew Cask maintainers to verify both the url and homepage information when first added (or subsequently modified, apart from versioning).

The parameter doesn’t mean you should trust the source blindly, but we only approve casks in which users can easily verify its authenticity with basic means, such as checking the official homepage or public repository. Occasionally, slightly more elaborate techniques may be used, such as inspecting a livecheck URL we established as official. Cases where such quick verifications aren’t possible (e.g. when the download URL is behind a registration wall) are treated in a stricter manner.

Difficulty finding a URL

Web browsers may obscure the direct url download location for a variety of reasons. Homebrew Cask supplies a list_url_attributes_on_file script which can read extended file attributes to extract the actual source URL of most files downloaded by a browser on macOS. The script usually emits multiple candidate URLs; you may have to test each of them:

$(brew --repository homebrew/cask)/developer/bin/list_url_attributes_on_file <file>

Subversion URLs

In rare cases, a distribution may not be available over ordinary HTTP/S. Subversion URLs are also supported, and can be specified by appending the following key/value pairs to url:

key value
using: the symbol :svn is the only legal value
revision: string identifying the Subversion revision to download
trust_cert: set to true to automatically trust the certificate presented by the server (avoiding an interactive prompt)

Git URLs

Artifacts also may be distributed via Git repositories. URLs that end in .git are automatically assumed to be Git repositories, and the following key/value pairs may be appended to url:

key value
using: the symbol :git is the only legal value
tag: string identifying the Git tag to download
revision: string identifying the Git revision to download
branch: string identifying the Git branch to download
only_path: path within the repository to limit the checkout to. If only a single directory of a large repository is required, using this option can significantly speed up downloads. If provided, artifact paths are relative to this path. (Example: font-geo.rb)

SourceForge/OSDN URLs

SourceForge and OSDN (formerly SourceForge.JP) projects are common ways to distribute binaries, but they provide many different styles of URLs to get to the goods.

We prefer URLs of this format:

https://downloads.sourceforge.net/<project_name>/<filename>.<ext>

Or, if it’s from OSDN, where <subdomain> is typically of the form dl or <user>.dl:

https://<subdomain>.osdn.jp/<project_name>/<release_id>/<filename>.<ext>

If these formats are not available, and the application is macOS-exclusive (otherwise a command-line download defaults to the Windows version) we prefer the use of this format:

https://sourceforge.net/projects/<project_name>/files/latest/download

Some providers block command-line downloads

Some hosting providers actively block command-line HTTP clients. Such URLs cannot be used in casks.

Other providers may use URLs that change periodically, or even on each visit (example: FossHub). While some cases could be circumvented, they tend to occur when the vendor is actively trying to prevent automated downloads, so we prefer to not add those casks to the main repository.

Using a block to defer code execution

Some casks—notably nightlies—have versioned download URLs but are updated so often that they become impractical to keep current with the usual process. For those, we want to dynamically determine url.

The Problem

In theory, one can write arbitrary Ruby code right in the cask definition to fetch and construct a disposable URL.

However, this typically involves an HTTP round trip to a landing site, which may take a long time. Because of the way Homebrew Cask loads and parses casks, it is not acceptable that such expensive operations be performed directly in the body of a cask definition.

Writing the block

Similar to the preflight, postflight, uninstall_preflight, and uninstall_postflight blocks, the url stanza offers an optional block syntax:

url "https://handbrake.fr/nightly.php" do |page|
  file_path = page[/href=["']?([^"' >]*Handbrake[._-][^"' >]+\.dmg)["' >]/i, 1]
  file_path ? URI.join(page.url, file_path) : nil
end

You can also nest url do blocks inside url do blocks to follow a chain of URLs.

The block is only evaluated when needed, for example at download time or when auditing a cask. Inside a block, you may safely do things such as HTTP/S requests that may take a long time to execute. You may also refer to the @cask instance variable, and invoke any method available on @cask.

The block will be called immediately before downloading; its result value will be assumed to be a String (or a pair of a String and Hash containing parameters) and subsequently used as a download URL.

You can use the url stanza with either a direct argument or a block but not with both.

Example of using the block syntax: [email protected]

Mixing additional URL parameters with the block syntax

In rare cases, you might need to set URL parameters like cookies or referer while also using the block syntax.

This is possible by returning a two-element array as a block result. The first element of the array must be the download URL; the second element must be a Hash containing the parameters.

Stanza: version

version, while related to the app’s own versioning, doesn’t have to follow it exactly. It is common to change it slightly so it can be interpolated in other stanzas, usually in url to create a cask that only needs version and sha256 changes when updated. This can be taken further, when needed, with Ruby String methods.

For example, instead of:

version "1.2.3"
url "https://example.com/file-version-123.dmg"

we can use:

version "1.2.3"
url "https://example.com/file-version-#{version.delete(".")}.dmg"

We can also leverage the power of regular expressions. So instead of:

version "1.2.3build4"
url "https://example.com/1.2.3/file-version-1.2.3build4.dmg"

we can use:

version "1.2.3build4"
url "https://example.com/#{version.sub(/build\d+/, "")}/file-version-#{version}.dmg"

version methods

The examples above can become hard to read, however. Since many of these changes are common, we provide a number of helpers to clearly interpret otherwise obtuse cases:

method input output
major 1.2.3-a45,ccdd88 1
minor 1.2.3-a45,ccdd88 2
patch 1.2.3-a45,ccdd88 3-a45
major_minor 1.2.3-a45,ccdd88 1.2
major_minor_patch 1.2.3-a45,ccdd88 1.2.3-a45
minor_patch 1.2.3-a45,ccdd88 2.3-a45
before_comma 1.2.3-a45,ccdd88 1.2.3-a45
after_comma 1.2.3-a45,ccdd88 ccdd88
dots_to_hyphens 1.2.3-a45,ccdd88 1-2-3-a45,ccdd88
no_dots 1.2.3-a45,ccdd88 123-a45,ccdd88

Similar to dots_to_hyphens, we provide methods for all logical permutations of {dots,hyphens,underscores}_to_{dots,hyphens,underscores}. The same applies to no_dots in the form of no_{dots,hyphens,underscores}, with an extra no_dividers that applies all these at once.

Finally, there is csv which returns an array of comma-separated values. csv, before_comma and after_comma are extra-special to allow for otherwise complex cases, and should be used sparingly. There should be no more than two of , per version.

version :latest

The special value :latest is used when:

Stanza: zap

zap purpose

The zap stanza describes a more complete uninstallation of files associated with a cask. The zap procedures will never be performed by default, but only if the user uses --zap on uninstall:

brew uninstall --zap firefox

zap stanzas may remove:

zap stanzas should not remove:

Appending --force to the command will allow you to perform these actions even if the cask is no longer installed:

brew uninstall --zap --force firefox

zap syntax

The form of the zap stanza follows the uninstall stanza. All the same directives are available. The trash: key is preferred over delete:.

Example: dropbox.rb

zap creation

The simplest method is to use @nrlquaker’s CreateZap, which can automatically generate the stanza. In a few instances it may fail to pick up anything and manual creation may be required.

Manual creation can be facilitated with:

If no additional files are discovered, instead of a zap stanza, include the following comment:

# No zap stanza required

Conditional statements

Handling different system configurations

Casks can deliver specific versions of artifacts depending on the current macOS release or CPU architecture by either tailoring the URL / SHA-256 hash / version, using the on_<system> syntax (which replaces conditional statements using MacOS.version or Hardware::CPU), or both.

If your cask’s artifact is offered as separate downloads for Intel and Apple Silicon architectures, they’ll presumably be downloadable at distinct URLs that differ only slightly. To adjust the URL depending on the current CPU architecture, supply a hash for each to the arm: and intel: parameters of sha256, and use the special arch stanza to define the unique components of the respective URLs for substitution in the url. Additional substitutions can be defined by calling on_arch_conditional directly. Example (from libreoffice.rb):

cask "libreoffice" do
  arch arm: "aarch64", intel: "x86-64"
  folder = on_arch_conditional arm: "aarch64", intel: "x86_64"

  version "7.6.0"
  sha256 arm:   "81eab945a33622fc156951e804024d23aa9a745c06743b4947215ed9303ad1c4",
         intel: "ede541af151487f60eb518e310d20dad1a973f3dbe9ff78d782dd29b14ba2946"

  url "https://download.documentfoundation.org/libreoffice/stable/#{version}/mac/#{folder}/LibreOffice_#{version}_MacOS_#{arch}.dmg",
      verified: "download.documentfoundation.org/libreoffice/stable/"
end

If the version number is different for each architecture, locate the unique version and (if checked) sha256 stanzas within on_arm and on_intel blocks. Example (from inkscape.rb):

cask "inkscape" do
  arch arm: "arm64", intel: "x86_64"

  on_arm do
    version "1.3.0,42339"
    sha256 "e37b5f8b8995a0ecc41ca7fcae90d79bcd652b7a25d2f6e52c4e2e79aef7fec1"
  end
  on_intel do
    version "1.3.0,42338"
    sha256 "e97de6804d8811dd2f1bc45d709d87fb6fe45963aae710c24a4ed655ecd8eb8a"
  end

  url "https://inkscape.org/gallery/item/#{version.csv.second}/Inkscape-#{version.csv.first}_#{arch}.dmg"
end

To adjust the installed version depending on the current macOS release, use a series of on_<system> blocks that cover the range of supported releases. Each block can contain stanzas that set which version to download and customize installation/uninstallation and livecheck behaviour for one or more releases. Example (from calibre.rb):

cask "calibre" do
  on_high_sierra :or_older do
    version "3.48.0"
    sha256 "68829cd902b8e0b2b7d5cf7be132df37bcc274a1e5720b4605d2dd95f3a29168"

    livecheck do
      skip "Legacy version"
    end
  end
  on_mojave do
    # ...
  end
  on_catalina do
    # ...
  end
  on_big_sur :or_newer do
    version "6.25.0"
    sha256 "a7ed19ae0526630ccb138b9afee6dc5169904180b02f7a3089e78d3e0022753b"

    livecheck do
      url "https://github.com/kovidgoyal/calibre"
      strategy :github_latest
    end
  end
end

Such on_<system> blocks can be nested and contain other stanzas not listed here. Examples: calhash.rb, openzfs.rb, r.rb, wireshark.rb

Switch between languages or regions

If a cask is available in multiple languages, you can use the language stanza to switch between languages or regions based on the system locale.

Arbitrary Ruby methods

In the exceptional case that the cask DSL is insufficient, it is possible to define arbitrary Ruby variables and methods inside the cask by creating a Utils namespace. Example:

cask "myapp" do
  module Utils
    def self.arbitrary_method
      # ...
    end
  end

  version "1.0"
  sha256 "a32565cdb1673f4071593d4cc9e1c26bc884218b62fef8abc450daa47ba8fa92"

  url "https://#{Utils.arbitrary_method}"
  name "MyApp"
  homepage "https://www.example.com/"
  # ...
end

This should be used sparingly: any method which is needed by two or more casks should instead be rolled into Homebrew/brew. Care must also be taken that such methods be very efficient.

Variables and methods should not be defined outside the Utils namespace, as they may collide with Homebrew Cask internals.

Token reference

This section describes the algorithm implemented in the generate_cask_token script, and covers detailed rules and exceptions which are not needed in most cases.

Purpose

Software vendors are often inconsistent with their naming. By enforcing strict naming conventions we aim to:

Details of software names and brands will inevitably be lost in the conversion to a minimal token. To capture the vendor’s full name for a distribution, use the name within a cask. name accepts an unrestricted UTF-8 string.

Finding the simplified name of the vendor’s distribution

Simplified names of apps

Converting to ASCII

Simplified names of pkg-based installers

Simplified names of non-App software

Converting the simplified name to a token

The token is the primary identifier for a package in this project. It’s the unique string users refer to when operating on the cask.

To convert the App’s Simplified Name (above) to a token:

Casks pinned to specific versions

Casks pinned to a specific version of the application (i.e. carbon-copy-cloner@5) should use the same token as the standard cask with a suffix of @<version-number. For Carbon Copy Cloner (carbon-copy-cloner), pinned to version 5, the token should be carbon-copy-cloner@5.

Casks pinned to development channels

Casks that use an development “channel”, such as betas, should use the same token as the standard cask with a suffix of @<channel>. For Google Chrome (google-chrome), using the “beta” channel, the token should be google-chrome@beta.

Cask filenames

Casks are stored in a Ruby file named after the token, with the file extension .rb.

Cask headers

The token is also given in the header line for each cask.

Cask token examples

These illustrate most of the rules for generating a token:

App Name on Disk Simplified App Name Cask Token Filename
Audio Hijack Pro.app Audio Hijack Pro audio-hijack-pro audio-hijack-pro.rb
VLC.app VLC vlc vlc.rb
BetterTouchTool.app BetterTouchTool bettertouchtool bettertouchtool.rb
LPK25 Editor.app LPK25 Editor lpk25-editor lpk25-editor.rb
Sublime Text 2.app Sublime Text sublime-text sublime-text.rb

For versioned/development channel casks:

Standard Cask Token Derivative Cask Token Filename
google-chrome Beta Channel google-chrome@beta [email protected]
vlc Nightly Channel vlc@nightly [email protected]
carbon-copy-cloner Pinned to version 5 carbon-copy-cloner@5 [email protected]

Special affixes

A few situations require a prefix or suffix to be added to the token.

Token overlap

When the token for a new cask would otherwise conflict with the token of an already existing cask, the nature of that overlap dictates the token, potentially for both casks. See Forks and Apps with Conflicting Names for information on how to proceed.

Potentially misleading name

If the token for a piece of unofficial software that interacts with a popular service would make it look official and the vendor is not authorised to use the name, a prefix must be added for disambiguation.

In cases where the prefix is ambiguous and would make the app appear official, the -unofficial suffix may be used.

Fork me on GitHub