Skip to content

ci(release): 让发布完整性守卫真的能触发,并把镜像也纳入它守的不变量 (#4900) - #4901

Merged
os-zhuang merged 1 commit into
mainfrom
claude/ci-errors-i1t8fi
Aug 3, 2026
Merged

ci(release): 让发布完整性守卫真的能触发,并把镜像也纳入它守的不变量 (#4900)#4901
os-zhuang merged 1 commit into
mainfrom
claude/ci-errors-i1t8fi

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#4899 引入的那个守卫的两处缺陷 —— 紧接着的下一次发布(89d2a4e)就把它们全暴露了。

那一轮:npm 成功发布了全部 17.0.0-rc.2 并推了 tag,然后创建 @objectstack/spec 的 GitHub Release 时撞上 API 的 125k body 上限失败(该版本的 changelog 段落 342,911 字符,详见 #4900)。步骤标红 → published 没置真 → 运行时镜像被静默丢掉

缺陷 1:守卫根本触发不了

GitHub 会给不带状态函数if: 隐式包一层 success()。所以

if: steps.changesets.outputs.published != 'true'

实际是 success() && (...) —— changesets 步骤一失败它就被 skipped,而那正是它存在的两个理由之一。改为 !cancelled() && ...

缺陷 2:它守的契约错了

原来写的是「缺失就补发」。这覆盖不了「发布成功但在上报前挂了」:rc.2 当时已经在 npm 上,即使它执行了也是 no-op,镜像照样丢。

现在它守的是发布真正欠下的不变量 —— 本仓库的当前版本必须在 npm 上,且必须有对应的运行时镜像 —— 并把后半句通过 job output 上报,让 docker job 去建。

docker job 同样加 !cancelled()

默认隐式 success() 下,上游 job 一失败依赖 job 就被跳过 —— 于是镜像丢在了一个发生在包已经公开之后的故障上。真正的闸门是 published 输出,不是 release job 的退出码。run 仍然会红(GitHub Release 确实失败了),但镜像不再陪葬。

一个刻意的取舍

ghcr 探测失败按「镜像缺失」处理:多建一次镜像只是几分钟,而错误地跳过会留下一个「npm 上有包、没有镜像、且没有任何信号」的发布 —— 正是今天这个坑。

验证

四条路径都用打桩的 npm/pnpm/git/curl 实跑过:

场景 结果
npm 有 + 镜像有(正常路径) 完全 no-op,不写任何 output
npm 有 + 镜像缺(今天这次) 请求 docker,不重复发包
npm 缺(#4898 场景) 补发 → 再请求 docker
ghcr 探测失败 按缺失处理

ghcr 探测本身是对线上真实注册表验证的:能正常换到匿名 pull token(长度 68),并正确报告 17.0.0-rc.1 存在、17.0.0-rc.2 不存在。

YAML 解析通过,新步骤 shell 过 bash -n,两个 job output 表达式取值已核对。

关联

只动 release.yml,不涉及任何运行时代码。

🤖 Generated with Claude Code

https://claude.ai/code/session_01BbNVKv6KgPzuQ5p76nMgnf


Generated by Claude Code

…t guard the image too (#4900)

Two defects in the guard #4899 added, both exposed by the very next release
(89d2a4e). npm published all of 17.0.0-rc.2 and pushed its tags, then creating
the @objectstack/spec GitHub Release failed on the API's 125k body limit — the
spec changelog section for that version is 342,911 characters (#4900). The step
went red, `published` stayed false, and the runtime image was silently lost.

1. The guard could not fire. GitHub wraps an `if:` naming no status function in
   an implicit success(), so `if: steps.changesets.outputs.published != 'true'`
   was really `success() && …` — skipped for any changesets-step failure, which
   is one of the two cases it exists for. Now `!cancelled() && …`.

2. Its contract was wrong. "Publish whatever is missing" does not cover a
   release that published and then died before reporting it: rc.2 was already on
   npm, so a publish-only step would have no-opped and lost the image anyway. It
   now guards the invariant the release actually owes — this repo's version must
   be on npm AND must have a matching runtime image — and reports the second
   half through the job outputs so `docker` builds it.

`docker` gains the same `!cancelled()` treatment. A dependent job under the
default implicit success() is skipped for any upstream failure, so the image was
lost to a fault that happened after the packages were already public. The
`published` output is the real gate; the release job's exit status is not, and
the run stays red either way because the GitHub Release genuinely failed.

A failed ghcr probe counts as MISSING on purpose: a redundant rebuild costs a
few minutes, a wrongly-skipped one leaves a published npm version with no image
and nothing to say so.

Verified against stubbed npm/pnpm/git/curl — npm present + image present (full
no-op, no outputs), npm present + image missing (requests docker, does NOT
republish), npm missing (publishes, then requests docker), and ghcr unreachable
(treated as missing). The ghcr probe itself was checked against the live
registry: it resolves an anonymous pull token and correctly reports 17.0.0-rc.1
present and 17.0.0-rc.2 absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbNVKv6KgPzuQ5p76nMgnf
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 2:58pm

Request Review

@os-zhuang os-zhuang added ci/cd tooling skip-changeset PR has no user-facing published change; bypasses the changeset gate labels Aug 3, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 3, 2026
@os-zhuang os-zhuang added tooling and removed size/s tooling labels Aug 3, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 3, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 3, 2026 15:07
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit db82f2e Aug 3, 2026
35 of 36 checks passed
@os-zhuang
os-zhuang deleted the claude/ci-errors-i1t8fi branch August 3, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants