Cask 食谱

每个 cask 都是一个 Ruby 块,以一个特殊标题行开头。cask 定义本身始终包含在 do … end 块中。示例

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

cask 语言是声明式的

每个 cask 都包含一系列节(或“字段”),这些节声明如何获取和安装软件。在声明式语言中,作者不必担心顺序。只要所有必需的字段都存在,Homebrew Cask 就会在安装时确定需要做什么。

为了简化维护,通常将最频繁更新的节放在顶部。但这只是惯例,并非规则。

例外:do 块,如 postflight,可能包含一个纯 Ruby 代码块。该块内的行遵循过程(顺序相关)范例。

标题行详细信息

头行 cask <cask-token> do 中的 Cask 名称(<cask-token>)应与 Cask 文件名匹配,不带 .rb 扩展名,并用双引号括起来。

目前对 Cask 令牌有一些任意限制,这些限制正在逐步取消。GitHub Actions 将在过渡期间捕获任何错误。

节顺序

为节设置一个通用顺序,可以简化 Cask 的更新和解析。以下是完整的节序列(没有 Cask 会包含所有节)。此处显示的空行也很重要,因为它们有助于直观地划定信息。

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

请注意,每个具有附加参数(, 后面的 :symbols)的节都应将其放在单独的行中,每行一个,按字母顺序排列。例外是 target:,它通常由短行组成。

必需节

每个 Cask 都需要以下每个节。

名称 允许多次出现?
版本 应用程序版本。
sha256 url 下载的文件的 SHA-256 校验和,由命令 shasum -a 256 <file> 计算。可以使用特殊值 :no_check 来禁止。
url 指向包含应用程序的 .dmg/.zip/.tgz/.tbz2 文件的 URL。如果 urlhomepage 节中的域不同,应添加注释。对于每次访问都会更改的 URL,应使用块语法。
名称 提供供应商定义的完整且正确的名称的字符串。
desc Cask 的单行描述。在运行 brew info 时显示。
homepage 应用程序主页;用于 brew home 命令。
livecheck 描述如何查找此 Cask 的更新的 Ruby 块。取代 appcast
依赖项 此 cask 的依赖项和要求列表。
zap 更彻底的卸载的附加程序,包括用户文件和共享资源。

至少还需要一个工件节

每个 cask 必须声明一个或多个工件(即要安装的内容)。

名称 允许多次出现?
app 在安装时应移至 /Applications 文件夹中的 .app 的相对路径。
suite 在安装时应移至 /Applications 文件夹中的包含目录的相对路径。
pkg 包含发行版的 .pkg 文件的相对路径。
installer 描述必须运行以完成安装的可执行文件。
binary 在安装时应链接到 $(brew --prefix)/bin 文件夹中的二进制文件的相对路径。
manpage 在安装时应链接到相应手册页文件夹中的手册页的相对路径,例如 my_app.3/usr/local/share/man/man3
colorpicker 在安装时应移至 ~/Library/ColorPickers 文件夹中的 ColorPicker 插件的相对路径。
dictionary 在安装时应移至 ~/Library/Dictionaries 文件夹中的词典的相对路径。
font 在安装时应移至 ~/Library/Fonts 文件夹中的字体的相对路径。
input_method 在安装时应移至 ~/Library/Input Methods 文件夹中的输入法的相对路径。
internet_plugin 在安装时应移至 ~/Library/Internet Plug-Ins 文件夹中的 Internet 插件的相对路径。
keyboard_layout 在安装时应移至 /Library/Keyboard Layouts 文件夹中的键盘布局的相对路径。
预置窗格 在安装时应移至 ~/Library/PreferencePanes 文件夹的预置窗格的相对路径。
qlplugin 在安装时应移至 ~/Library/QuickLook 文件夹的快速查看插件的相对路径。
mdimporter 在安装时应移至 ~/Library/Spotlight 文件夹的 Spotlight 元数据导入器的相对路径。
screen_saver 在安装时应移至 ~/Library/Screen Savers 文件夹的屏幕保护程序的相对路径。
service 在安装时应移至 ~/Library/Services 文件夹的服务的相对路径。
audio_unit_plugin 在安装时应移至 ~/Library/Audio/Components 文件夹的音频单元插件的相对路径。
vst_plugin 在安装时应移至 ~/Library/Audio/VST 文件夹的 VST 插件的相对路径。
vst3_plugin 在安装时应移至 ~/Library/Audio/VST3 文件夹的 VST3 插件的相对路径。
artifact 在安装时应移动的任意路径的相对路径。必须提供绝对路径作为 target。(示例:free-gpgmail.rb)这仅适用于不寻常的情况;在移动 .app 捆绑包时,强烈建议使用 app 节。
stage_only true。断言木桶不包含可激活的工件。

可选节

名称 允许多次出现?
uninstall 卸载木桶的过程。除非使用 pkg 节,否则为可选。
conflicts_with 与此木桶冲突的列表(尚未实现)。
caveats 在安装时向用户提供木桶特定信息的字符串或 Ruby 块。
deprecate! YYYY-MM-DD 格式的字符串形式的日期以及提供原因的字符串或符号。
禁用! YYYY-MM-DD 格式的字符串形式的日期以及提供原因的字符串或符号。
预检查 包含预检查安装操作的 Ruby 块(仅在非常罕见的情况下需要)。
后检查 包含后检查安装操作的 Ruby 块。
卸载预检查 包含预检查卸载操作的 Ruby 块(仅在非常罕见的情况下需要)。
卸载后检查 包含后检查卸载操作的 Ruby 块。
语言 必需 使用语言代码参数调用的 Ruby 块,包含其他节或返回值。
容器嵌套 必须在继续安装前提取的内部容器的相对路径。这允许支持 .dmg 中的 .tar.zip 中的 .dmg 等。(示例:blocs.rb
容器类型 覆盖容器类型自动检测的符号。可以是以下之一::air:bz2:cab:dmg:generic_unar:gzip:otf:pkg:rar:seven_zip:sit:tar:ttf:xar:zip:naked。(示例:parse.rb
自动更新 true。断言木桶工件自动更新。如果 检查更新… 或类似内容出现在应用程序菜单中,但如果没有打开网页并且没有为您执行下载和安装,则使用它。

节说明

节:app

app 的字符串参数的简单情况下,源文件将移动到目标 /Applications 目录。例如

app "Alfred 2.app"

默认情况下将源移动到

/Applications/Alfred 2.app

重命名目标

您可以通过向 app 添加 target: 键来重命名显示在 /Applications 目录中的目标。示例(来自 scala-ide.rb

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

target 可能包含绝对路径

如果 target: 有前导斜杠,则将其解释为绝对路径。如果绝对路径的包含目录不存在,则会创建该目录。示例(来自 sapmachine-jdk.rb)

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

target 适用于大多数工件类型

对于大多数 cask 工件,target: 键的工作方式类似,例如 appbinarycolorpickerdictionaryfontinput_methodinternet_pluginkeyboard_layoutprefpaneqlpluginmdimporterscreen_saverservicesuiteaudio_unit_pluginvst_pluginvst3_pluginartifact

target 仅应在特定情况下使用

不要出于美观原因使用 target:,例如删除版本号(app "Slack #{version}.app", target: "Slack.app")。当在功能上合理时使用它,并使用以下模板之一在 cask 中清楚地记录你的原因:为了清晰为了保持一致性为了避免冲突由于开发人员的建议

节:binary

binary 的字符串参数的简单情况下,源文件在安装时链接到 $(brew --prefix)/bin 目录。例如(来自 operadriver.rb)

binary "operadriver_mac64/operadriver"

创建指向

$(brew --prefix)/bin/operadriver

的符号链接,源文件如下

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

二进制文件(或多个)也可以包含在应用程序包中

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

你可以通过向 binary 添加 target: 键来重命名显示在二进制目录中的目标

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

app 相同,target: 的行为和用法 相同。但是,对于 binary,特定情况并不那么严格。你可以自由地使用 target:,以与其他命令行工具保持一致,例如 更改大小写删除扩展名清理名称

节:caveats

有时,软件的安装会有一些特殊情况,Homebrew Cask 无法或不应该以编程方式处理。在这些情况下,caveats 是告知用户的方法。caveats 中的信息会在使用 installinfo 调用 cask 时显示。

为了避免向用户发送过多消息(从而让他们对重要消息失去敏感性),caveats 应谨慎使用,并且仅用于与安装相关的事项。如果您不确定您发现的 caveat 是否与安装相关,请询问维护人员。一般来说,如果您的情况尚未在我们的全面 caveats Mini-DSL 中涵盖,则不太可能被接受。

caveats 作为字符串

caveats 是字符串时,它会在编译时进行评估。如果 caveats 位于 cask 末尾的惯常位置,则可以使用以下方法进行插值

方法 说明
标记 cask 标记
版本 cask 版本
homepage cask 主页
caskroom_path 此 cask 的包含目录:$(brew --caskroom)/<token>(仅适用于块形式)
staged_path 此 cask 的暂存位置,包括版本号:$(brew --caskroom)/<token>/<version>(仅适用于块形式)

示例

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

caveats 作为块

caveats 是 Ruby 块时,评估会推迟到安装时。在块中,您可以引用 @cask 实例变量,并调用 @cask 上可用的任何方法

caveats mini-DSL

caveats 块中提供了一个 mini-DSL。

可以调用以下方法来生成标准警告消息

方法 说明
path_environment_variable "path" 用户应确保 path 在其 PATH 环境变量中。
zsh_path_helper "path" zsh 用户必须采取其他步骤来确保 path 在其 PATH 环境变量中。
depends_on_java “版本” 用户应确保已安装指定版本的 Java。 version 可以是确切版本(例如 6),最小版本(例如 7+),或省略(任何版本均可)。
requires_rosetta 此 cask 需要 Rosetta 2 才能在 Apple Silicon 上运行。
logout 用户应注销并重新登录以完成安装。
reboot 用户应重新启动以完成安装。
files_in_usr_local 此 cask 将文件安装到 /usr/local,这可能会使 Homebrew 产生混淆。
kext 用户可能需要在系统设置 → 隐私与安全(或在早期 macOS 版本中为系统偏好设置 → 安全与隐私 → 通用)中启用其 kext。
unsigned_accessibility 由于此应用程序未签名,因此用户需要在每次更新时在系统设置 → 隐私与安全(或在早期 macOS 版本中为系统偏好设置 → 安全与隐私 → 隐私)中重新启用此应用程序。
license “网页” 用户可以在 web_page 找到软件的使用许可证。
free_license “网页” 用户可以在 web_page 获得软件的正式使用许可证。

示例

caveats do
  path_environment_variable "/usr/texbin"
end

节:deprecate! / disable!

deprecate!disable! 用于声明 cask 不再具有功能或不再受支持。包含 deprecate! 节的 cask 仍然可以安装,但在安装或升级时会打印警告消息。包含 disable! 节的 cask 无法安装或升级,并且会打印错误消息。

这两个节的语法相同

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: 参数

date: 参数控制弃用或禁用何时生效。具有未来日期的 deprecate! 节的 cask 在该日期之前不会被视为已弃用。具有未来日期的 disable! 节的 cask 在该日期之前会自动弃用,之后会被禁用。

because: 参数

because: 参数接受废弃或禁用 cask 的原因。信息消息将为 <cask> 已废弃,因为它 <reason>!,因此请将原因格式化为适合该句子的格式。例如,because: "已损坏" 将导致 <cask> 已废弃,因为它已损坏!

because: 参数还可以接受对应于预设原因的符号,例如

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

允许的符号的完整列表可以在 DeprecateDisable 模块 中找到。

节:conflicts_with

conflicts_with 用于声明阻止 cask 安装或正常工作的冲突。

conflicts_with cask

值应为另一个 cask 令牌。

示例:macFUSE cask 与 macfuse-dev 冲突。

conflicts_with cask: "macfuse-dev"

conflicts_with formula

注意: conflicts_with formula: 是一个存根,目前尚不可用。

值应为另一个配方名称。

示例:MacVimmacvim 配方冲突。

conflicts_with formula: "macvim"

节:depends_on

depends_on 用于声明 cask 的依赖项和要求。depends_on 在尝试 install 之前不会被咨询。

depends_on cask

值应为另一个 cask 令牌,当前 cask 需要该令牌。

示例:NTFSTool 依赖于 macFUSE。

depends_on cask: "macfuse"

depends_on formula

值应命名 cask 所需的 Homebrew 配方。

示例:某些发行版包含在存档格式中,例如 7z,而 Apple 的库存工具不支持这些格式。对于这些情况,可以通过声明对 unar 配方的依赖项,在安装时引入更强大的存档读取器

depends_on formula: "unar"

depends_on macos

需要确切的 macOS 版本

depends_on macos: 的值可以是符号或符号数组,列出确切的兼容 macOS 版本。macOS 版本的可用值在 MacOSVersion 类 中定义。

仅涵盖主要版本(macOS 11 之后的包含单个点或整数的 10.x 数字)。符号形式用于可读性。以下是枚举木桶确切 macOS 版本要求的所有有效方法

depends_on macos: :big_sur
depends_on macos: [
  :catalina,
  :big_sur,
]
设置最低 macOS 版本

depends_on macos: 还可以接受以比较运算符开头的字符串,例如 >=,后跟上述形式的 macOS 版本。以下是一个有效的表达式,表示“至少 macOS Big Sur (11.0)”

depends_on macos: ">= :big_sur"

比较表达式不能与任何其他形式的 depends_on macos: 结合使用。

depends_on arch

depends_on arch: 的值可以是符号或符号数组,列出木桶的硬件兼容性要求。如果多个 arch: 值中的任何一个与用户的硬件匹配,则在安装时满足要求。

可用的硬件符号为

符号 含义
:x86_64 64 位英特尔
:intel 64 位英特尔
:arm64 Apple 芯片

以下都是有效的表达式

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 参数

说明
公式 Homebrew 公式
木桶 木桶令牌
macos 定义 macOS 版本要求的符号、数组或字符串比较表达式
arch 定义硬件要求的符号或数组
java 存根 - 尚未启用

节:desc

desc 接受包含软件简短描述的单行 UTF-8 字符串。它用于帮助搜索和消除歧义,因此它必须简洁地描述软件的作用(或使用它可以完成什么)。

desc 不适用于应用程序标语!供应商的描述往往充满了诸如“现代”和“轻量级”之类的通用形容词。这些是毫无意义的营销宣传(你是否曾经看到应用程序自豪地将自己描述为过时且笨重?)必须删除。使用软件网站上的信息作为起点是可以的,但在几乎所有情况下都需要进行编辑。

注意事项

节:*flight

preflightpostflightuninstall_preflightuninstall_postflight 定义在安装或卸载之前或之后运行的操作。

块的评估总是被延迟

由这些节定义的 Ruby 块在安装时或卸载时才评估。在块中,您可以引用 @cask 实例变量,并调用 @cask 上可用的任何方法

*flight 微型 DSL

在这些块中提供了一个微型 DSL。

可以调用以下方法来执行标准任务

方法 availability 说明
set_ownership(paths) preflightpostflightuninstall_preflight 设置 paths 的用户和组所有权。(示例:docker-toolbox.rb)
set_permissions(paths, permissions_str) preflightpostflightuninstall_preflight paths 中的权限设置为 permissions_str。(示例:ngrok.rb)

set_ownership(paths) 将用户所有权默认为当前用户,将组所有权默认为 staff。可以通过传入额外选项来更改这些设置:set_ownership(paths, user: "user", group: "group")。(示例:hummingbird.rb)

节:installer

此节必须始终与 uninstall 一起使用。

installer 节包含一系列键值对,其第一个键必须为 manual:script:

installer manual

installer manual: 采用一个字符串值,描述一个 GUI 安装程序,用户必须在稍后运行该程序。路径可以是绝对路径,也可以相对于 cask。示例(来自 rubymotion.rb

installer manual: "RubyMotion Installer.app"

installer script

installer script: 引入一系列键值对,描述一个将自动完成安装的命令。它绝不应用于交互式安装。其形式类似于 uninstall script:

executable 要运行的安装脚本的路径
args 安装脚本的参数数组
input 要发送到脚本的 stdin 的输入行数组
must_succeed 如果允许脚本失败,则设置为 false
sudo 如果脚本需要 sudo,则设置为 true

路径可以是绝对路径,也可以相对于 cask。示例(来自 miniforge.rb

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

如果 installer script: 不需要任何键值,它可以直接指向安装脚本的路径

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

节:language

language 节可以匹配 ISO 639-1 语言代码、脚本代码 (ISO 15924) 和区域标识符 (ISO 3166-1 Alpha 2),或其组合。

美国英语应始终用作默认语言

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

请注意,以下内容并不相同

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

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

可以通过简单地调用 language 来访问匹配的 language 块的返回值。

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

示例:firefox.rbbattle-net.rb

安装

要在特定语言中安装 cask,可以将 --language= 选项传递给 brew install

brew install firefox --language=it

节:livecheck

livecheck 节用于从变更日志、发行说明、应用程序播客等自动获取 cask 的最新版本。

每个 livecheck 块必须包含一个 url,它可以是字符串或指向木桶中其他 URL 的符号(:url:homepage)。

有关如何编写 livecheck 块,请参阅 brew livecheck 文档

节:name

name 接受一个 UTF-8 字符串,用于定义软件的名称,包括大小写和标点符号。它用于帮助搜索和消除歧义。

标记(它被简化并简化为一组有限的字符)不同,name 节可以包括适当的大小写、空格和标点符号,以匹配软件的正式名称。为了消除歧义,建议拼出应用程序的名称,必要时包括供应商名称。一个很好的例子是 pycharm-ce 木桶,其名称拼写为 Jetbrains PyCharm Community Edition,即使它可能从未在任何地方被这样引用过。

有关软件的其他详细信息可以在 desc 中提供。

如果存在有用的备用名称,则可以多次重复 name 节。第一个实例应使用拉丁字母。例如,请参见 cave-story 木桶,其原始名称不使用拉丁字母。

节:pkg

此节必须始终与 uninstall 一起使用。

pkg 节的第一个参数应该是要安装的 .pkg 文件的相对路径。示例

pkg "Unity.pkg"

pkg 的后续参数是修改安装过程的关键值对。当前支持的键是 allow_untrusted:choices:

pkg allow_untrusted

pkg allow_untrusted: true 可用于通过将 -allowUntrusted 传递给 /usr/sbin/installer 来安装包含不受信任证书的 .pkg

此选项在官方 Homebrew Cask 水龙头中不被允许;它仅供在第三方水龙头或本地木桶中使用。

历史示例(来自 alinof-timer.rb

pkg "AlinofTimer.pkg", allow_untrusted: true

pkg choices

pkg choices: 可用于通过 -applyChoiceChangesXML 覆盖 .pkg 的默认安装选项。它使用 choiceChanges 属性列表的反序列化版本(请参阅 installer 手册页的 CHOICE CHANGES FILE 部分,方法是运行 man -P 'less --pattern "^CHOICE CHANGES FILE"' installer)。

运行此 macOS installer 命令

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

将输出 XML,您可以使用它来提取 choices: 值及其与 GUI 选项的等效值。

请参阅 针对 wireshark-chmodbpf 的此请求针对 wine-staging 的此请求,了解该过程的一些示例。

示例(来自 lando.rb

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

示例(来自 microsoft-office.rb

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

节:sha256

计算 SHA-256

通常通过 shasum 命令计算 sha256

shasum --algorithm 256 <file>

特殊值 :no_check

特殊值 sha256 :no_check 用于在由于上游配置而无法进行校验和时关闭 SHA 检查。

version :latest 需要 sha256 :no_check,并且这种配对很常见。但是,sha256 :no_check 不需要 version :latest

我们尽可能使用校验和。

节:suite

一些发行版提供一系列多个应用程序,或带有所需数据的应用程序,以便一起安装在 /Applications 的子目录中。

对于这些 cask,请使用 suite 节来定义包含应用程序套件的目录。示例(来自 racket.rb

suite "Racket v#{version}"

suite 的值永远不是 .app 捆绑包,而是一个普通目录。

节:uninstall

如果您无法设计一个有效的 uninstall 节,请随时提交您的 cask。维护人员可以帮助您编写 uninstall 节,只需询问即可!

uninstall pkgutil: 最简单、最有帮助

最简单、最有用的 uninstall 指令是 pkgutil:。它应该涵盖大多数用例。

对于使用 pkginstaller manual: 安装的 cask,需要 uninstall

对于大多数木桶,卸载操作会自动确定,并且不需要明确的 uninstall 节。但是,使用 pkginstaller manual: 节的木桶将不知道如何正确卸载,除非给出了 uninstall 节。

因此,虽然 cask DSL 并不强制要求,但如果每个 pkginstaller manual: 都具有相应的 uninstall,那么对用户来说会更好。

对于非 pkg 木桶,uninstall 节可用,并且对一些极端情况很有用。但是,下面的文档涉及使用 uninstallpkg 定义过程的典型情况。

有多种卸载技术

由于 pkg 安装程序可以执行任意操作,因此在每种情况下都需要不同的技术来卸载。您可能需要将以下一个或多个键/值对指定为 uninstall 的参数。

键的摘要

每个 uninstall 技术都按照上述顺序应用。忽略 uninstall 键在 cask 文件中出现的顺序。

为了帮助填写 uninstall 键的正确值,Homebrew Cask 存储库的 developer/bin 下有几个帮助脚本。每个脚本都使用 -help 选项来提供其他文档。

在已安装并运行软件包的系统上执行 uninstall 节是最简单的。若要操作已卸载的 .pkg 文件,请参阅以下 手动使用 .pkg 文件

uninstall pkgutil

这是最实用的卸载键。pkgutil: 通常足以完全卸载 pkg,并且强烈推荐使用它,而不是 delete:

可以使用 list_recent_pkg_ids 列出最近安装的软件包的 ID。

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

pkgutil: 还接受针对多个软件包 ID 的正则表达式匹配。正则表达式有点非标准。要针对当前安装的软件包测试 pkgutil: 正则表达式,请使用 list_pkg_ids_by_regexp

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

列出与软件包 ID 关联的文件

一旦你知道了已安装软件包的 ID(见上文),你就可以使用 macOS pkgutil 命令列出与该软件包 ID 关联的所有文件

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

列出关联的文件可以帮助你评估软件包是否包含任何 launchd 作业或内核扩展 (kext)。

uninstall launchctl

可以使用 list_loaded_launchjob_ids 列出当前加载的 launchd 作业的 ID。

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

可以使用 list_installed_launchjob_ids 列出所有已安装的 launchd 作业的 ID。

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

uninstall quit

可以使用 list_running_app_ids 列出当前正在运行的应用程序的包 ID。

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

可以使用 list_ids_in_app 列出磁盘上应用程序包内的包 ID。

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

uninstall signal

signal: 应该只在进程不响应 quit: 的罕见情况下才需要。

可以与 quit: 相同的方式获取 signal: 目标的包 ID。 signal: 的值是一个数组的数组,每个单元格包含两个元素:所需的 Unix 信号,后跟相应的包 ID。

Unix 信号可以以数字或字符串形式给出(有关更多详细信息,请参阅 kill(1) 手册页)。

signal: 数组的元素按顺序应用,仅当存在与包 ID 关联的现有进程时,并且在该进程终止时停止。可以重复包 ID 以向同一进程发送多个信号。

最好使用足以停止进程的最不严重的信号。KILL 信号尤其可能产生不良副作用。

一个示例,按严重程度升序排列的常用信号

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"],
]

请注意,当多个正在运行的进程与给定的 bundle ID 匹配时,将向所有匹配的进程发出信号。

quit: 指令不同,Unix 信号源自当前用户,而非超级用户。这被解释为一个安全特性,因为超级用户能够通过信号关闭系统。然而,这种不一致性也可能被视为一个 bug,并且应该在未来版本中以某种方式解决。

uninstall login_item

可以使用 list_login_items_for_app 列出与磁盘上的应用程序 bundle 关联的登录项

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

请注意,你可能需要至少打开一次应用程序,才能显示任何登录项。

uninstall kext

可以使用 list_loaded_kext_ids 列出当前加载的内核扩展的 ID

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

可以使用 list_id_in_kext 列出磁盘上 kext bundle 中的 ID

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

uninstall script

uninstall script: 引入一系列描述命令的键值对,该命令将自动完成卸载。示例(来自 virtualbox.rb

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

请务必注意,尽管上述示例中的 script: 尝试完全卸载 pkg,但它不应取代 pkgutil:,而应尽可能作为补充。

uninstall delete

delete: 仅应在其他 uninstall 方法不足时作为最后手段使用。

uninstall delete: 的参数应使用以下基本规则

要删除特定于用户的文件,请使用 zap

uninstall trash

trash: 参数遵循上面为 delete: 列出的相同规则。

手动使用 .pkg 文件

高级用户可能希望手动使用 .pkg 文件,而无需安装该软件包。

可以使用 list_payload_in_pkg.pkg 中提取可以安装的文件列表。

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

可以从 .pkg 文件中使用 list_apps_in_pkg 提取候选应用程序名称,以帮助确定 cask 的名称。

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

可以从 .pkg 文件中使用 list_ids_in_pkg 提取候选软件包 ID,这些 ID 在 pkgutil: 密钥中可能很有用。

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

以下是用于在软件包文件中查找 bundle ID 的完全手动方法

  1. 使用 pkgutil --expand /path/to/my.pkg /tmp/expanded.unpkg 解压 /path/to/my.pkg(替换为您的软件包名称)。
  2. 解压后的软件包是一个文件夹。捆绑包 ID 包含在名为 PackageInfo 的文件中。可以使用命令 find /tmp/expanded.unpkg -name PackageInfo 找到这些文件。
  3. PackageInfo 文件是 XML 文件,捆绑包 ID 位于 <pkg-info> 标记的 identifier 属性中,如下所示:<pkg-info ... identifier="com.oracle.jdk7u51" ... >,其中无关属性已剪切并用省略号替换。
  4. 软件包中的 Kext 也在 PackageInfo 文件中描述。如果存在任何内核扩展,则命令 find /tmp/expanded.unpkg -name PackageInfo -print0 | xargs -0 grep -i kext 应返回一个 <bundle id> 标记,其中 path 属性包含 .kext 扩展名,例如 <bundle id="com.wavtap.driver.WavTap" ... path="./WavTap.kext" ... />
  5. 一旦识别出捆绑包 ID,就可以删除解压后的软件包目录。

节:url

首选 HTTPS URL

如果可用,首选 HTTPS URL。只有在没有安全替代方案的情况下才应使用纯 HTTP URL。

其他 url 参数

当纯 URL 字符串不足以获取文件时,可以将附加信息提供给基于 curl 的下载器,形式为附加到 url 的键/值对

verified 重复 url 开头的字符串,用于 验证目的
using 符号 :post:homebrew_curl 是唯一合法的值
cookies 要在下载请求中设置的 cookie 哈希值(示例:oracle-jdk-javadoc.rb)
referer 在下载请求中用作引用的 URL 所在的字符串(示例:firealpaca.rb)
header 包含要为下载请求设置的标头的字符串或字符串数组(示例:pull-6545issue-15590)
user_agent 包含要为下载请求设置的用户代理的字符串。还可以将其设置为符号 :fake,它将使用类似于浏览器的通用用户代理字符串。当服务器不需要特定用户代理时,我们更喜欢 :fake
data 要在 POST 请求中设置的参数哈希值(示例:segger-jlink.rb)

当 URL 和主页域名不同时,添加 verified:

urlhomepage 的域名不同时,应使用 verified: 参数记录差异,重复 URL 中唯一标识应用程序或供应商的最小部分,不包括协议。(示例:shotcut.rb

必须添加此项,以便审核木桶的用户知道 Homebrew Cask 团队已验证 URL 是供应商提供的 URL,即使它看起来可能非官方。作为 Homebrew Cask 维护者,我们有责任在首次添加(或随后修改,版本控制除外)时验证 urlhomepage 信息。

该参数并不意味着您应该盲目信任来源,但我们只批准用户可以通过基本方式(例如检查官方主页或公共存储库)轻松验证其真实性的木桶。偶尔,可能会使用稍微复杂一点的技术,例如检查我们确认为官方的 livecheck URL。在无法进行此类快速验证的情况下(例如,当下载 URL 位于注册墙后面时),将 以更严格的方式进行处理

难以找到 URL

由于各种原因,Web 浏览器可能会隐藏直接 url 下载位置。Homebrew Cask 提供了一个 list_url_attributes_on_file 脚本,该脚本可以读取扩展文件属性,以提取 macOS 上浏览器下载的大多数文件的实际源 URL。该脚本通常会发出多个候选 URL;您可能必须测试每一个

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

Subversion URL

在极少数情况下,可能无法通过普通的 HTTP/S 获得分发。还支持 Subversion URL,可以通过将以下键/值对附加到 url 来指定

using 符号 :svn 是唯一合法的值
版本 用于标识要下载的 Subversion 版本的字符串
trust_cert 设置为 true 以自动信任服务器提供的证书(避免交互式提示)

Git URL

工件也可以通过 Git 存储库分发。以 .git 结尾的 URL 会自动假定为 Git 存储库,并且可以将以下键/值对追加到 url

using 符号 :git 是唯一合法的值
标签 用于标识要下载的 Git 标签的字符串
版本 用于标识要下载的 Git 版本的字符串
分支 用于标识要下载的 Git 分支的字符串
only_path 存储库中的路径,用于限制签出。如果只需要大型存储库中的单个目录,使用此选项可以显著加快下载速度。如果提供了,工件路径相对于此路径。(示例:font-geo.rb)

SourceForge/OSDN URL

SourceForge 和 OSDN(以前称为 SourceForge.JP)项目是分发二进制文件的常见方式,但它们提供了许多不同样式的 URL 来获取商品。

我们更喜欢这种格式的 URL

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

或者,如果它来自 OSDN,其中 <subdomain> 通常采用 dl<user>.dl 的形式

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

如果这些格式不可用,并且该应用程序是 macOS 专属的(否则命令行下载默认为 Windows 版本),我们更喜欢使用此格式

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

某些提供商会阻止命令行下载

某些托管提供商会主动阻止命令行 HTTP 客户端。此类 URL 无法在木桶中使用。

其他提供商可能会使用定期更改甚至每次访问都会更改的 URL(例如:FossHub)。虽然某些情况 可以变通,但它们往往发生在供应商积极尝试阻止自动下载时,因此我们更愿意不将这些木桶添加到主存储库中。

使用块来延迟代码执行

某些木桶(尤其是夜间构建)具有版本化的下载 URL,但更新如此频繁,以至于使用通常的过程来保持最新版本变得不切实际。对于这些木桶,我们希望动态确定 url

问题

理论上,可以在木桶定义中直接编写任意 Ruby 代码来获取和构造一个一次性 URL。

但是,这通常涉及到对登录网站的 HTTP 往返,这可能需要很长时间。由于 Homebrew Cask 加载和解析木桶的方式,不允许在木桶定义的主体中直接执行此类昂贵的操作。

编写块

类似于 preflightpostflightuninstall_preflightuninstall_postflight 块,url 节提供了一个可选的块语法

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

您还可以将 url do 块嵌套在 url do 块内,以遵循 URL 链。

该块仅在需要时才进行评估,例如在下载时或在审计 cask 时。在块内,您可以安全地执行可能需要较长时间才能执行的操作,例如 HTTP/S 请求。您还可以引用 @cask 实例变量,并调用 @cask 上可用的任何方法

该块将在下载之前立即调用;其结果值将被假定为 String(或包含参数的 StringHash 的一对),并随后用作下载 URL。

您可以将 url 节与直接参数或块一起使用,但不能同时使用两者。

使用块语法的示例: [email protected]

将其他 URL 参数与块语法混合使用

在极少数情况下,您可能需要设置 URL 参数,例如 cookiesreferer,同时还要使用块语法。

可以通过将一个双元素数组作为块结果来实现这一点。该数组的第一个元素必须是下载 URL;第二个元素必须是一个包含参数的 Hash

节:version

version 与应用程序自己的版本控制相关,但不必完全遵循它。通常会稍微更改它,以便可以在其他节中(通常在 url 中)对其进行 内插,从而创建一个仅需要 versionsha256 在更新时进行更改的 cask。在需要时,可以使用 Ruby 字符串方法 进一步进行此操作。

例如,而不是

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

我们可以使用

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

我们还可以利用正则表达式的强大功能。因此,而不是

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

我们可以使用

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

version 方法

但是,上面的示例可能难以阅读。由于许多这些更改很常见,因此我们提供了一些帮助程序来清楚地解释晦涩的案例

方法 input 输出
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

类似于 dots_to_hyphens,我们为 {dots,hyphens,underscores}_to_{dots,hyphens,underscores} 的所有逻辑排列提供方法。对于 no_dots,同样适用于 no_{dots,hyphens,underscores} 形式,并额外提供 no_dividers,它一次应用所有这些形式。

最后,有 csv,它返回一个逗号分隔值数组。 csvbefore_commaafter_comma 是额外的特殊功能,允许处理复杂的案例,并且应谨慎使用。每个 version 不应有多于两个 ,

version :latest

节:zap

zap 目的

zap 节规定述了与 cask 关联的文件的更彻底的卸载。zap 程序永远不会默认执行,只有当用户在 uninstall 中使用 --zap 时才会执行。

brew uninstall --zap firefox

zap 节可能会移除

zap 节不应该移除

向命令追加 --force 将允许你执行这些操作,即使 cask 已不再安装

brew uninstall --zap --force firefox

zap 语法

zap 节的形式遵循 uninstall。所有相同的指令都可用。trash: 键优于 delete:

示例:dropbox.rb

zap 创建

最简单的方法是使用 @nrlquaker 的 CreateZap,它可以自动生成节。在少数情况下,它可能无法拾取任何内容,可能需要手动创建。

可以使用以下方法来促进手动创建

如果没有发现其他文件,则可以包括以下注释,而不是 zap 节

# No zap stanza required

条件语句

处理不同的系统配置

Cask 可以根据当前 macOS 版本或 CPU 架构提供特定版本的工件,方法是定制 URL/SHA-256 哈希/版本,使用 on_<system> 语法(它使用 MacOS.versionHardware::CPU 替换条件语句),或同时使用这两种方法。

如果 Cask 的工件针对 Intel 和 Apple Silicon 架构提供单独下载,那么它们大概可以从仅略有不同的不同 URL 下载。要根据当前 CPU 架构调整 URL,请为 sha256arm:intel: 参数提供每个哈希,并使用特殊的 arch 节来定义各个 URL 的唯一组件,以便在 url 中进行替换。可以通过直接调用 on_arch_conditional 来定义其他替换。示例(来自 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

如果每个架构的版本号不同,请在 on_armon_intel 块中找到唯一的 version 和(如果已检查)sha256 节。示例(来自 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

要根据当前 macOS 版本调整已安装版本,请使用一系列 on_<system> 块,这些块涵盖受支持版本范围。每个块可以包含设置要下载的版本并自定义一个或多个版本的安装/卸载和 livecheck 行为的节。示例(来自 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

此类 on_<system> 块可以嵌套,并包含此处未列出的其他节。示例:calhash.rbopenzfs.rbr.rbwireshark.rb

在语言或地区之间切换

如果 Cask 提供多种语言版本,则可以使用 language 根据系统语言环境在语言或地区之间切换。

任意 Ruby 方法

在极少数情况下,如果 cask DSL 不够用,可以通过创建一个 Utils 命名空间,在 cask 中定义任意的 Ruby 变量和方法。示例

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

应谨慎使用此方法:两个或更多 cask 需要使用的任何方法都应转到 Homebrew/brew 中。还必须注意,此类方法必须非常高效。

变量和方法不应定义在 Utils 命名空间之外,因为它们可能会与 Homebrew Cask 内部发生冲突。

令牌参考

本部分介绍 generate_cask_token 脚本中实现的算法,并介绍大多数情况下不需要的详细规则和例外情况。

目的

软件供应商在命名方面常常不一致。通过强制执行严格的命名约定,我们的目标是

在转换为最小令牌时,软件名称和品牌的详细信息不可避免地会丢失。若要获取发行版的供应商全名,请在 cask 中使用 namename 接受不受限制的 UTF-8 字符串。

查找供应商发行版的简化名称

应用程序的简化名称

转换为 ASCII

基于 pkg 的安装程序的简化名称

非应用程序软件的简化名称

将简化名称转换为令牌

令牌是此项目中软件包的主要标识符。当对 cask 进行操作时,它是用户引用的唯一字符串。

将应用程序的简化名称(如上所述)转换为令牌

固定到特定版本的 Cask

固定到应用程序特定版本的 cask(即 carbon-copy-cloner@5)应使用与标准 cask 相同的令牌,后缀为 @<version-number。对于固定到版本 5 的 Carbon Copy Cloner(carbon-copy-cloner),令牌应为 carbon-copy-cloner@5

固定到开发渠道的 Cask

使用开发“渠道”的 cask,例如 beta 版,应使用与标准 cask 相同的令牌,后缀为 @<channel>。对于使用“beta”渠道的 Google Chrome(google-chrome),令牌应为 google-chrome@beta

Cask 文件名

Cask 存储在以令牌命名的 Ruby 文件中,文件扩展名为 .rb

Cask 标题

令牌也给出了每个 cask 的标题行。

Cask 令牌示例

这些说明了生成令牌的大多数规则

磁盘上的应用程序名称 简化的应用程序名称 Cask 令牌 文件名
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

对于版本化/开发渠道 cask

标准 Cask 令牌 派生 Cask 令牌 文件名
google-chrome Beta 渠道 google-chrome@beta [email protected]
vlc Nightly 渠道 vlc@nightly [email protected]
carbon-copy-cloner 固定到版本 5 carbon-copy-cloner@5 [email protected]

特定于 Tap 的 cask 令牌示例

Cask tap 具有特定于每个 tap 的命名约定。

特殊后缀

一些情况需要在令牌中添加前缀或后缀。

令牌重叠

当新 cask 的令牌与已经存在的 cask 的令牌发生冲突时,重叠的性质决定了令牌,可能适用于两个 cask。有关如何继续操作的信息,请参阅 分叉和名称冲突的应用程序

可能具有误导性的名称

如果与流行服务交互的非官方软件的令牌使其看起来很官方,并且供应商未经授权使用该名称,则 必须添加前缀 以消除歧义。

在前缀模棱两可并且会使应用程序看起来很官方的情况下,可以使用 -unofficial 后缀。

Fork me on GitHub