Same-repository Homebrew Cask release
Same-repository Homebrew Cask release 指产品仓库同时承担产品源码、稳定 Release 产物和 Homebrew Tap 的一部分职责,并把 GUI Cask 作为稳定 Release metadata 的受约束投影。
- 运维
- 工具
- 桌面
目录
Definition
Same-repository Homebrew Cask release 指产品仓库同时承担产品源码、稳定 Release 产物和 Homebrew Tap 的一部分职责,并把 GUI Cask 作为稳定 Release metadata 的受约束投影。Cask 的版本、下载地址、摘要和应用身份都应从同一个不可变 Release 及其校验材料推导;它不是另一个独立的版本源,也不是把任意 macOS 二进制重新包装后直接推到用户机器上的脚本(Casks/<cask-token>.rb、SHA256SUMS、BUILD-MANIFEST.json)。
这个模式还定义了三个必须同时成立的边界:Tap/Cask 与产品源码可以同仓库维护,但不要求第二个 Tap;GUI Cask 与 daemon、privileged helper 或 Formula 是不同的交付面;没有 Apple Developer ID 或 notarization 时,ad-hoc 签名和 Gatekeeper/quarantine 行为必须被如实呈现。它不授权删除 quarantine、自动申请 Full Disk Access、执行特权命令、绕过 branch protection,也不把某个项目的 bundle id、固定路径或版本号变成跨项目规则。macOS app 负责应用本身的构建和平台生命周期,本 Topic 负责 Cask 分发投影及其信任边界。
When to use
当仓库维护一个可发布的 macOS GUI app,并且希望用户通过 Homebrew 安装稳定版本,同时满足以下条件时使用:Cask 可以放在产品仓库的 Tap 根路径;产品 Release 已发布不可变的版本化安装包和校验材料;更新 Cask 不应依赖第二个仓库、PAT、GitHub App 或跨仓库 secret;自动化需要在 PR required checks 通过后才更新 Cask。典型证据包括 Casks/、Release workflow、checksum/manifest 文件和只读的 Cask audit workflow。
如果任务只讨论 app 的窗口、Keychain、签名配置或本地运行,应先使用 macOS app;如果只讨论 workflow 的并发或事件分类,应结合 GitHub Actions scheduling;如果只讨论一般 required checks,应结合 Quality gates。没有稳定 Release、没有 GUI app,或只是把一次性下载链接写进 README 时,不应命中本 Topic。需要跨仓库 Tap、组织级权限或特殊发布供应链时可以参考本 Topic 的边界,但那是 owner-controlled deviation,不是默认方案。
Habits / defaults
- 把 Cask 当作 Release projection。 在 Tap 根路径只维护一个 GUI Cask 定义,例如
Casks/<cask-token>.rb;稳定 Cask 只接受最终X.Y.Z,下载地址必须锚定同一 Release 的 immutable tag。渲染器应同时读取 Release 的校验和构建 manifest,拒绝 prerelease、缺少目标架构的产物,以及 checksum 与 manifest 不一致的候选。Cask 的版本、URL、SHA-256、bundle identity 和最低 macOS 版本都应由同一组输入验证,禁止手工改其中一项来“修绿”。 - 明确 Tap/Cask 与其他产物的仓库边界。 GUI Cask 只安装 GUI app;daemon Formula、命令行工具、snapshot/helper、Full Disk Access 配置和备份数据维持各自的 packaging/lifecycle。Formula 即使仍在同一个仓库,也不能被 GUI Cask 顺带安装、启动或配置;不要在另一个目录复制第二份 GUI Cask。
packaging/是一种常见的分离位置,但具体目录名应由目标仓库已有结构决定。 - 如实处理 ad-hoc 与 Gatekeeper。 没有 Apple Developer ID 或 notarization 时,可以发布 ad-hoc signed 产物,但必须把它当成未 notarized 的发行状态。Cask 保留 macOS quarantine,让用户先验证 Release 资产,再按 Finder/Gatekeeper 的系统提示批准首次启动;不要在
postflight中移除 quarantine、关闭 Gatekeeper、请求 FDA 或运行 privileged command。即使项目以后补齐 notarization,也应把 notarization 作为 Release 输入和验证结果,而不是靠安装脚本掩盖状态。 - 把写权限集中在同仓库的可信 workflow。 从可信
main或已发布稳定 Release 运行的更新 workflow 只用最小化GITHUB_TOKEN,其写入范围限于本仓库的 automation branch、PR、必要 label、check dispatch 和受保护的合并操作。Homebrew audit workflow 保持 read-onlycontents/pull-requests权限,不 checkout 或执行不可信 PR 内容。PAT、GitHub App、repository secret、跨仓库 Tap 和直接写默认分支都属于 owner-controlled deviation,采用前需单独记录信任模型。 - 创建 PR 前验证平台能力与 token scope。 若可信 workflow 依赖
gh pr create或 PR-create API,先由有权限的维护者核实 repository 的Settings > Actions > General > Workflow permissions已启用 Allow GitHub Actions to create and approve pull requests,并确认组织策略没有禁止该能力;这与 workflow/job 的permissions:是两层独立条件。创建 PR 的 job 必须显式拥有pull-requests: write;只有该 job 还要推送 automation branch 时才增加contents: write。GET /repos/{owner}/{repo}/actions/permissions/workflow的default_workflow_permissions和can_approve_pull_request_reviews不能证明创建 PR 的能力,不能把审批字段当成创建权限。设置未核实时先阻断 rollout 并请维护者确认,不以首次真实任务失败作探测,也不默默扩大权限或切换 PAT/GitHub App(GitHub Actions repository settings、workflow permissions、Create a pull request API、Actions permissions API)。 - 合并证据必须绑定 exact head。 自动化应在同仓库 automation branch 上创建或更新 PR,并把仓库、base branch、PR number、head SHA 和预期 check 名称一起校验。
pull_request_target使用 base branch 的 trusted workflow 定义,因此候选 PR 不能靠修改自身 workflow 修复 required check;一次成功的workflow_dispatch也不能替代该 PR head 上的 required check。只有当前 head 的所有 required checks 成功,并且 branch protection 仍有效时,才能进行 SHA-constrained merge。 - 为 docs-only 路径初始化可选状态。 Release completion 同时处理 product-release 和 documentation-only 分支时,所有可能在后续 shell guard 中读取的 preparation 变量都应在条件判断前初始化。
set -u下不能假设type:docs会产生 version preparation record;workflow contract test 必须覆盖产品发布和文档-only 两条路径,避免“文档变更没有发布意图却因未定义变量失败”。
复用前逐项确认:
- Cask 是稳定 Release 的单一、可重复生成的投影,且版本、URL、SHA-256、manifest 和 bundle 约束来自同一次发布。
- GUI Cask、daemon/Formula、helper、权限和数据的安装职责彼此分离。
- 无 Developer ID/notarization 时明确 ad-hoc 状态,保留 quarantine,并把首次 Gatekeeper 批准留给用户。
- 更新 workflow 的 write permissions 只覆盖同仓库所需对象,audit workflow 是只读的,没有默认 PAT、App 或跨仓库 secret。
- 若更新 workflow 会创建 PR,已分别核实 repository/org 的 Actions PR-creation setting 与 job 的
pull-requests: write;仅在 job 推送 branch 时才授予contents: write。 - required check、PR、base 和 head SHA 在同一候选上绑定;手动 dispatch 结果没有被冒充成 PR gate。
- product-release 与 docs-only 分支都经过 workflow contract test,optional preparation state 不会在
set -u下未定义。
Reference implementations
projects/televy-backup.md:代表一个同时包含 macOS GUI app、后台/CLI 产物和 release workflow 的产品仓库;它用于说明应用、daemon 和发布链路需要分层,不把该项目的具体命名当作 Topic 默认值。https://github.com/IvanLi-CN/televy-backup/blob/main/docs/solutions/macos/homebrew-cask-same-repository.md:代表本 Topic 的完整实例,覆盖同仓库 Cask、stable Release metadata、ad-hoc/Gatekeeper、GITHUB_TOKEN、exact-head checks 和 docs-only release path。阅读时只提炼边界和验证习惯,不复制其中的 bundle id、Tap 名称、固定路径或版本。skills/style-playbook/references/topics/macos-app/TOPIC.md、skills/style-playbook/references/topics/github-actions-scheduling/TOPIC.md和skills/style-playbook/references/topics/quality-gates/TOPIC.md:分别承接 app 生命周期、workflow 调度和一般 merge gate;本 Topic 只补充它们交叉处的 Homebrew Cask 发布合同。
Failure modes
- 把 Cask 指向
latest、branch、prerelease 或另一 Release 的 asset,会让安装内容、版本和摘要失去同一 Release 的 provenance。应拒绝候选并重新从稳定 Release metadata 渲染。 - 手工修改 version 或 SHA-256 以通过审计,会制造 release projection 的 split-brain。应修复 Release asset、checksum 或 manifest 的输入,而不是在 Cask 中覆盖事实。
- 在
Casks/和packaging/各放一份 GUI Cask,或让 GUI Cask 顺带安装 daemon/helper,会把两个生命周期混成一个不可审查的安装面。应保留一个 GUI Cask,并让其他产物继续走独立 Formula/packaging 流程。 - 在没有 Developer ID/notarization 时用
postflight删除 quarantine、执行xattr绕过 Gatekeeper、请求 FDA 或运行特权命令,会把用户可见的安全决策变成隐藏安装副作用。应保留 quarantine 并在文档中说明首次启动的 Gatekeeper 行为。 - 给同仓库 workflow 过度授予
contents: write、pull-requests: write,或为方便跨仓库写入引入 PAT/App/secret,会扩大受信任代码的影响面。应把写入集中在可信 workflow,并将跨仓库方案标成 owner-controlled deviation。 - 只在 job 中声明
pull-requests: write,却未启用或未核实 repository/org 的 Actions PR-creation setting,会让gh pr create在运行时被拒绝(例如createPullRequest)。这不是再扩大 workflow scope 就能修复的问题;应停下 rollout 并核实平台设置,不自动改权限、引入 PAT/App 或创建探测 PR。 - 看到手动 dispatch 变绿就合并,或只检查 branch 名不检查 head SHA,会把旧候选、另一 PR 或另一仓库的结果误当成当前 required check。应在合并前重新读取 PR、base、repository、head 和 check-run identity,并拒绝 SHA 漂移。
pull_request_target的修复只存在于候选分支时,required check 仍可能执行 base 版本的 workflow;应先检查可信 base workflow,再用当前 head 重新触发和追踪 required gate。- docs-only PR 没有 preparation record,却在
set -u下读取未初始化变量,会让“不发布产品”的文档变更错误失败。应把默认空值初始化放在 product-release 条件之前,并保留两条路径的 contract assertion。
Evidence index
projects/televy-backup.mdskills/style-playbook/references/topics/macos-app/TOPIC.mdskills/style-playbook/references/topics/github-actions-scheduling/TOPIC.mdskills/style-playbook/references/topics/quality-gates/TOPIC.mdhttps://github.com/IvanLi-CN/televy-backup/blob/main/docs/solutions/macos/homebrew-cask-same-repository.mdhttps://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authenticationhttps://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_targethttps://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repositoryhttps://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntaxhttps://docs.github.com/en/rest/pulls/pullshttps://docs.github.com/en/rest/actions/permissions