ci(release): npm-first 发布顺序,桌面签名资产异步补挂 - #646
Conversation
背景:此前 Release workflow 的 npm 发布强依赖 macOS 签名(`release` job `needs: desktop`,且「Require signed macOS assets」对 stable 硬闸),而 macOS 签名挂在 macos-signing 环境的人工审批门后。结果:签名慢/待批时,代码 明明就绪,`npm i -g botmux` 也拿不到新版——正是 v3.7.0 发版时的体感。 改动(Option B,申晗拍板):解耦 npm 与桌面签名,让 npm 先发、桌面异步补。 - `release` job 去掉 `needs: desktop` 与 always() 门控,改为 push 即跑;npm publish + 创建 GitHub Release 不再等 macOS 签名。 - 三道既有安全闸**原样保留、且仍在 npm publish 之前执行**:防降级 latest、 正式版必须包含最新 master、dist-tag 路由(canary/beta/rc/next 走旁路)。 - 新增显式「deepcoldy 才能发 latest」闸,替代原先经 `needs: desktop` 传递的 隐式作者权限(desktop 只对 deepcoldy 跑 → 旧「Require signed macOS assets」 步骤据此拦非 deepcoldy 的 stable tag)。解耦后必须显式重申,否则任何人推 stable tag 都能发 latest。 - 新增 `attach-desktop-assets` job(`needs: [desktop, release]`):签名完成 + Release 建好后,把 .dmg/.zip --clobber 补挂到该 Release 并校验(与 workflow_dispatch 的 refresh-desktop-assets 同法,只是走 tag-push 路径)。 - 合并原「with assets / without assets」两个 Release 创建分支为一个无条件创建 (不带 assets),assets 一律异步补。 权衡:正式版会有一个短时间窗——npm 已上、GitHub Release 已建、但 .dmg/.zip 还没挂(几分钟内同一次 run 补上)。换取 npm 不再被签名审批门阻塞。 验证:YAML 解析通过;人工核对步序确认三道 guard 全在 npm publish 之前、 作者权限闸对 latest 生效。因属发布基础设施改动,交 codex 复审安全性后再合。 Co-Authored-By: Riff <noreply@riff.dev>
|
To use Codex here, create a Codex account and connect to github. |
codex 独立复审:✅ 代码层面通过(无 blocker)固定基线:base 1. 发布 guard 与 latest 权限:通过
2.
|
codex 复审指出:macOS 人工审批可等待最多 30 天,若拒绝/签名失败/运行取消, Release 会保留但桌面资产不会自动补挂。这是 Option B 的既定运维窗口(不做 自动重试——签名需人工审批,重试只会再次阻塞)。补充: - attach-desktop-assets job 注释写清该失败模式 + 恢复方式(workflow_dispatch 用 release_tag=vX.Y.Z 重跑,重建+签名后 refresh-desktop-assets --clobber 补挂, 幂等;fresh artifact 故 7 天保留期无关)。 - release job 新增一步在 run log 里显式打印「npm/Release 已上、桌面资产异步补、 失败如何恢复」,让运维一眼看到窗口与补救步骤。 Co-Authored-By: Riff <noreply@riff.dev>
codex 增量复审
|
codex 复审 9005f9d 抓到恢复路径 blocker:desktop job 的 actions/checkout 没 指定 ref,workflow_dispatch 时默认检出所选 ref(通常 master)而非 release_tag。 「Sync version」只改版本号、不改代码,于是恢复 re-run 会把「当前 master 代码 + 旧版本号」签名后 --clobber 挂到旧 Release,桌面资产与 npm 包/git tag 不同源 (官方契约:workflow_dispatch 的 GITHUB_SHA=所选 ref 末端,非 tag)。 修复: - desktop checkout 显式钉 ref:workflow_dispatch → refs/tags/${release_tag}; push → github.ref(本就是 tag)。两条路径都从「被发布的确切 commit」构建。 - 新增「Verify HEAD is the tagged commit」步骤:校验 HEAD==tag commit,mis-resolve 时 fail-closed(绝不把错源签进 Release)。 - 改掉自相矛盾的注释(原「几分钟/同一次 run」与新增 30 天审批失败说明冲突): Create Release 注释改为「正常几分钟内补,但签名挂人工审批门可等 30 天/被拒/ 失败/取消,那时 Release 保持 npm-only 直到手动恢复 re-run」。 注:这其实是 refresh-desktop-assets 的预存隐患,但本 PR 把它当恢复路径依赖, 故一并修。push 正常发版路径不受影响(本就检出 tag)。 验证:YAML 解析通过;desktop job 步序完整(checkout 钉 ref→verify HEAD→ setup-node→...→sign→upload);push 路径 ref 解析仍= github.ref。 Co-Authored-By: Riff <noreply@riff.dev>
codex 增量复审
|
…CTOU 不同源 codex 复审 58cd65f 抓到 push 路径 TOCTOU blocker:显式 `ref: github.ref` 不等价于 checkout 默认行为。checkout@v6 无 ref 时用 context.sha 钉住事件 commit;显式传 github.ref(一个 ref 名)会让 checkout 在**运行时重新解析** 该 tag。而 desktop 挂人工审批门(可等 30 天),且仓库 ruleset 明确不保护 canary/beta/rc 的 tag update——若 tag 在 release 已按事件 SHA 发布 npm(commit A)之后、desktop 获批之前被移动到 B,desktop 会构建 B;且原 Verify 取「当前 tag」也是 B,B==B 通过,最终 npm(A)与桌面资产(B)不同源。codex 已 execution 复现(release=A、tag 移 B、Verify 放行 B)。 修复: - push 分支 checkout 改用 `github.sha`(事件触发时 tag 指向的**不可变 commit**), 不用 `github.ref`(ref 名会被重新解析)。SHA 不会移动,无论 tag 后续如何变, desktop 都构建 A。 - workflow_dispatch 分支仍用 `refs/tags/${input}`(dispatch 没有 tag 的事件 SHA)。 - Verify 步骤语义随之变实:HEAD=github.sha=A;若 tag 被移到 B,当前 tag^{commit}=B ≠ HEAD=A → fail-closed,拒绝把与 tag 现状不符的资产挂上去。 - release job 不受影响:它本就无 ref(默认 = 事件 SHA A),npm 从 A 发布。 依据 checkout@v6 input-helper:显式 SHA 归入 commit 路径、不再按 ref 重解析。 验证:YAML 解析通过;push→github.sha、dispatch→refs/tags/input 两路径正确; release job 仍 = 事件 SHA;annotated/lightweight/缺失 tag 的 Verify 行为不变。 Co-Authored-By: Riff <noreply@riff.dev>
codex 最终增量复审
|
背景
此前 Release workflow 的 npm 发布强依赖 macOS 签名:
releasejobneeds: desktop,且有一道「Require signed macOS assets for stable releases」硬闸。而 macOS 签名挂在macos-signing环境的人工审批门后。结果:签名慢 / 待批时,代码明明就绪,npm i -g botmux也拿不到新版——正是 v3.7.0 发版时的体感。申晗拍板走 Option B:解耦 npm 与桌面签名,npm 先发、桌面签名资产异步补挂。
改动
releasejob 去掉needs: desktop(及always()门控),改为 push 即跑:npm publish + 创建 GitHub Release 不再等 macOS 签名。npm publish之前执行:merge-base --is-ancestor,防从落后分支发 latest)needs: desktop隐式传递的(desktop 只对 deepcoldy 跑 → 旧「Require signed macOS assets」步骤据此拦非 deepcoldy 的 stable tag)。解耦后这条隐式链断了,故显式重申,否则任何人推 stable tag 都能发 latest。canary/beta/rc/next 仍对所有人开放(灰度用)。attach-desktop-assetsjob(needs: [desktop, release]):签名完成 + Release 建好后,把.dmg/.zip--clobber补挂到该 Release 并下载校验(与 workflow_dispatch 的refresh-desktop-assets同法,只是走 tag-push 路径)。权衡
正式版会有一个时间窗:npm 已上、GitHub Release 已建、但
.dmg/.zip还没挂。正常情况下签名在同一次 run 内几分钟补齐;但 macOS 签名挂人工审批门(可等最多 30 天,或被拒/失败/取消),那时 Release 会保持 npm-only,直到手动workflow_dispatch(release_tag=vX.Y.Z)恢复 re-run 补挂。换取 npm 不再被签名审批门阻塞。影响面
.github/workflows/release.yml;不碰产品代码。验证
releasejob 步序:Resolve dist-tag → 作者权限闸 → 防降级闸 → 必须含 master 闸 → build → npm publish → changelog → 创建 Release。三道 guard + 权限闸全部在 npm publish 之前。attach-desktop-assets的 needs/if 是否正确、有无「Release 建了但 assets 永久缺失」的失败模式。🤖 下次发版生效;本次 v3.7.0 已按旧流程发成功,不受影响。
复审后追加(codex review)
9005f9dd:显式化签名失败运维窗口——attach-desktop-assets注释写清失败模式+恢复方式,releasejob 加一步 run-log 打印「npm/Release 已上、资产异步补、失败如何恢复」。58cd65f3(blocker 修复):desktopjob 的actions/checkout原先没钉ref,workflow_dispatch恢复路径会检出默认 ref(master)而非release_tag→ 会把「当前 master 代码+旧版本号」签名挂到旧 Release,资产与 npm/tag 不同源。加「Verify HEAD is the tagged commit」fail-closed 校验。908206b0(TOCTOU blocker 修复,当前 head):58cd65f3的 push 分支用了ref: github.ref(ref 名),checkout 会在运行时重解析 tag。desktop 挂 30 天审批门 + 仓库 ruleset 不保护 canary/beta/rc 的 tag update → tag 在release已按事件 SHA 发 npm(commit A)后、desktop 获批前被移到 B → desktop 会构建 B,且原 Verify 取「当前 tag」=B → B==B 放行 → npm(A) 与桌面资产(B) 不同源。改为:push 分支 checkout 用github.sha(不可变事件 commit,不会移动),workflow_dispatch分支仍refs/tags/${release_tag};Verify 步骤据此真正生效(HEAD=A,tag 若移到 B → 当前tag^{commit}=B≠A → fail-closed)。releasejob 不受影响(本就无 ref = 默认事件 SHA)。