brew -- 创建自己的 Cask
摘要
-
brew 用
formula从源码(或 bottle)安装软件,用cask安装已经打好的成品。成品常见是.app/.pkg,也可以只是一个命令行二进制,不一定非要是 GUI。 -
本文介绍如何在 macos 下创建自己的 Cask,和 brew -- 创建自己的 Formula 是一对:那篇讲 Formula,这篇讲 Cask。
-
本文基于 MacOS 15.x,brew 版本为 Homebrew 7.0.4。
-
关于 brew 的安装及使用可以参考 MacOS软件包管理器--brew
Formula 和 Cask 的区别
-
选择哪种包装,看的是怎么拿到软件,不是「命令行必须 Formula、GUI 必须 Cask」:
- 能从源码构建(开源命令行工具、库)→ Formula
- 上游已经提供预编译包 → Cask(可以是
.app/.pkg,也可以是单个命令文件)
| 对比项 | Formula | Cask |
|---|---|---|
| 典型对象 | 从源码构建的命令行工具、库 | 预编译成品:GUI 或命令行二进制都可以 |
| 下载物 | 源码 tarball,本地编译或 bottle | .dmg / .zip / .tar.gz / .pkg 等容器 |
| 安装产物 | Cellar 里的文件,再软链到 $(brew --prefix)/bin |
app → /Applications;pkg → 系统 installer;binary → $(brew --prefix)/bin |
| DSL | class Xxx < Formula |
cask "token" do ... end |
| 文件位置 | Formula/xxx.rb |
Casks/xxx.rb |
| 安装命令 | brew install xxx |
brew install --cask xxx |
| 卸载 | brew uninstall xxx |
brew uninstall --cask xxx,可用 --zap 清残留 |
-
.dmg/.zip/.tar.gz只是下载容器,真正装进系统的是 artifact:app、pkg、binary等。Reasonix 就是 Cask 只装一个命令文件的例子,见文末。 -
自己写的、需要从源码安装的命令行脚本仍应走 Formula。已经有各平台预编译二进制时,用 Cask 的
binary更合适。
从一个简单示例开始
-
这里用一个最小的 macOS 应用
HelloCask.app做演示:用 AppleScript 生成.app,打成 zip,上传到 GitHub Release,再写 Cask 让别人brew install --cask安装。 -
和 Formula 的关键差别:Formula 通常下载源码包(
archive/refs/tags/v1.0.0.tar.gz);Cask 下载的是已经打好的应用包。
制作 HelloCask.app 并发布
-
创建一个 Github 仓库,用于存放发布产物,仓库名称:
hanqunfeng/hello_cask
1 | # 用 AppleScript 生成一个最简单的 .app,双击会弹出对话框 |
-
把仓库推到 GitHub,然后在 GitHub 上创建一个 Release:
- Tag:
v1.0.0 - 附件上传:
HelloCask-1.0.0.zip
- Tag:
-
下载地址会是:
1 | https://github.com/hanqunfeng/hello_cask/releases/download/v1.0.0/HelloCask-1.0.0.zip |
不要用
archive/refs/tags/v1.0.0.tar.gz这种源码归档当地址。Cask 不会帮你编译.app,它只负责下载、校验、把里面的.app拷到/Applications。
创建 tap 仓库
-
Cask 和 Formula 可以放在同一个 tap 里(
Formula/和Casks/并列),也可以单独建一个。这里单独建,结构和上一篇保持一致。
1 | brew tap-new hanqunfeng/hello_cask |
-
brew tap-new默认只创建Formula/目录,Cask 需要自己补:
1 | mkdir -p "$(brew --repo hanqunfeng/hello_cask)/Casks" |
仓库在 GitHub 上的名字必须是
homebrew-hello_cask。brew tap hanqunfeng/hello_cask会自动补上homebrew-前缀。
手写一个 Cask 文件
-
真正有约束的是 token 三处一致:
- 文件名:
Casks/hello-cask.rb - header:
cask "hello-cask" do - 安装命令:
brew install --cask hello-cask
- 文件名:
-
name "HelloCask"是给人看的显示名,可以有大小写和空格,不必等于 token。 -
brew generate-cask-token不是安装所必需的步骤。它只是按官方规则给一个建议名,方便向homebrew/cask投稿、避免重名。个人 tap 里你自己定hello-cask完全可以,不必先跑这个命令。 -
如果要用它,注意它只做「小写 + 空格/下划线变
-」,不会拆驼峰:
1 | brew generate-cask-token "HelloCask" |
-
后面示例采用
hello-cask。选定之后,文件名和 header 不要混用hellocask。
1 | cd "$(brew --repo hanqunfeng/hello_cask)/Casks" |
写入内容(把 sha256 换成你自己算出来的值):
1 | cask "hello-cask" do |
-
desc不要以A/An/The开头,也不要写macOS,否则brew style会报错。 -
只有
.app的 Cask 必须写depends_on :macos。 -
提交前在本地跑一次,能修的会自动改:
1 | brew style --fix "$(brew --repo hanqunfeng/hello_cask)/Casks/hello-cask.rb" |
【推荐】也可以用
brew create --cask URL --tap hanqunfeng/hello_cask --set-name hello-cask生成模板。它会下载 zip、自动填sha256,再打开编辑器,根据需要修改生成的模板文件。
提交 Cask 文件到 Github 仓库
-
创建一个 Github 仓库:
hanqunfeng/homebrew-hello_cask
1 | cd "$(brew --repo hanqunfeng/hello_cask)" |
-
如果推送
.github/workflows/*.yml失败,提示缺少workflow权限,给 PAT 加上repo和workflow后重推。具体处理和 brew -- 创建自己的 Formula 里一样:
1 | git remote set-url origin https://ghp_xxxxx@github.com/hanqunfeng/homebrew-hello_cask.git |
GitHub Actions 报错(brew test-bot)
-
brew tap-new会生成.github/workflows/tests.yml。推到main后会在 Ubuntu 和 macOS 上跑brew test-bot --only-tap-syntax,核心是brew style检查脚本格式。 -
matrix 默认 fail-fast:Ubuntu 一失败,后面的
macos-26、macos-15-intel会显示 canceled,那不是 macOS 自己坏了。 -
常见
brew style错误(HelloCask 实测会踩):
| 规则 | 问题 | 改法 |
|---|---|---|
Cask/Desc |
desc 以 A/An/The 开头 |
删掉冠词 |
Cask/Desc |
desc 里写了 macOS |
不要提平台名 |
Homebrew/OSDependsOn |
只有 .app 却没声明系统 |
加 depends_on :macos |
Layout/EmptyLines |
多空了一行 | 按 stanza 分组,组与组之间只空一行 |
Layout/TrailingEmptyLines |
文件末尾缺少换行 | 文件必须以换行符结束,--fix 会自动补上 |
Cask/ArrayAlphabetization |
zap trash: 里只有一个元素还用了 [] |
改成字符串,或没有残留就删掉 zap |
-
本地改完再推:
1 | cd "$(brew --repo hanqunfeng/hello_cask)" |
安装 Cask
1 | # 添加 tap |
-
安装完成后:
.app在/Applications/HelloCask.app- 下载缓存和版本目录在
$(brew --caskroom)/hello-cask/1.0.0,Intel Mac 一般是/usr/local/Caskroom/hello-cask/1.0.0
测试、卸载
1 | # 查看信息 |
Cask 文件模板
-
Cask 不是 Formula 那种继承类,而是一个声明式 DSL:把“去哪下载、叫什么、装什么、怎么卸”写清楚,Homebrew 自己决定执行顺序。
1 | cask "hello-cask" do |
-
文件名、token、header 三者必须一致:
Casks/hello-cask.rb↔cask "hello-cask" do
必填字段
1 | version "1.0.0" |
-
url里用#{version}插值,以后升版本通常只改version和sha256。 -
version :latest必须搭配sha256 :no_check,个人 tap 能打出版本号就不要用:latest。 -
另外至少要有一个 artifact(真正安装的东西)。最常见的是
app(GUI),也可以是binary(命令文件),不必非有.app。
app:安装 .app
1 | # zip/dmg 解压后,根目录就是 HelloCask.app |
pkg:安装 .pkg
-
.pkg走系统 installer,Cask 必须同时写uninstall,否则用户卸不干净。
1 | pkg "HelloCask.pkg" |
-
uninstall常用键:
| 键 | 作用 |
|---|---|
pkgutil: |
按 package id 卸载,.pkg 首选 |
quit: |
按 bundle id 发送退出事件(相当于 Cmd+Q) |
launchctl: |
卸载 launchd 服务 |
delete: / trash: |
按路径删除,trash: 进废纸篓,更安全 |
script: |
跑官方卸载脚本 |
1 | uninstall quit: "com.example.HelloCask", |
binary:把可执行文件链到 prefix/bin
-
Cask 的安装产物不必须是
.app/.dmg/.pkg。binary可以把压缩包里的命令文件软链到$(brew --prefix)/bin,装完就能在终端直接跑。Reasonix 整份脚本只有这一个 artifact,没有.app。 -
两种常见用法:
1 | # 1. 压缩包里就是一个命令文件(Reasonix 这种) |
zap:彻底清理
-
普通
brew uninstall --cask只移除 artifact(例如/Applications里的.app)。用户数据、偏好设置、缓存要写在zap里,只有加--zap才会执行。 -
不要凭 bundle id 猜路径。HelloCask 这种
osacompile对话框应用,打开后通常也不会生成~/Library/Saved Application State/...。路径不存在时,zap里写了也只是空操作。 -
真实 GUI 应用常见残留大致在这些位置,以本机实际存在的为准:
1 | zap trash: [ |
-
正确做法是:先安装并打开一次应用,再自动扫描:
1 | brew generate-zap hello-cask |
-
扫不到额外文件时,官方习惯写一行注释:
# No zap stanza required。HelloCask 演示就属于这种情况。
depends_on:依赖和系统约束
1 | depends_on macos: :sonoma # 最低 macOS |
按架构提供不同下载地址
-
很多应用 Intel / Apple Silicon 各一个包:
1 | cask "hello-cask" do |
token 命名要点
-
个人 tap:文件名去掉
.rb必须等于cask "..."里的字符串,用户安装时也敲这个名字。这是硬约束。brew generate-cask-token只是帮你起名,可以不用。 -
若向官方
homebrew/cask投稿,才建议用它生成 token,规则是:取.app名字 → 去掉.app→ 小写 → 空格变-。它不会拆驼峰:HelloCask→hellocask,Hello Cask→hello-cask。 -
和已有 formula / cask 重名时,加厂商前缀或
-app后缀,例如 formula 叫unison,cask 叫unison-app。 -
固定大版本用
corretto@11;测试通道用google-chrome@beta。@latest表示上游的发布通道,和version :latest不是一回事。
第三方 tap 的本地修改会生效
-
Homebrew 4.0 之后,官方
homebrew/cask默认走 JSON API,改本地.rb不会参与安装,需要HOMEBREW_NO_INSTALL_FROM_API=1。 -
自己的 tap 不受这个限制:安装时本来就会 clone 到
Taps目录,改完Casks/hello-cask.rb再brew reinstall --cask hello-cask就会用新脚本。
1 | brew edit --cask hanqunfeng/hello_cask/hello-cask |
以 Reasonix 为例,说明脚本配置项
-
这是一个只有命令文件、没有
.app的 Cask:按系统/架构下载对应tar.gz,用binary "reasonix"链到bin。说明 Cask 完全可以用来分发预编译 CLI,本机只会走其中一条on_*分支。
1 | # token:文件名、header、安装名必须一致,用户执行 brew install --cask reasonix |
后记
-
更完整的 Cask DSL 见官方文档:Cask Cookbook、Acceptable Casks
-
学习现有写法:
brew edit --cask firefox(官方 cask 会先 clonehomebrew/cask) -
创建 Formula 见 brew -- 创建自己的 Formula