Skip to content

ci: 推 v* 标签自动发版,Release 正文固定成四段 - #4

Merged
modusensus merged 1 commit into
mainfrom
feat/release-workflow
Nov 12, 2026
Merged

modusensus merged 1 commit into
mainfrom
feat/release-workflow

Conversation

@modusensus

Copy link
Copy Markdown
Member

为什么改

0.9.0 那次发版是手工走的:本地 npm publish 卡在 2FA(EOTP),tag 手打,Release 正文靠 --generate-notes 拼出一堆 Merge pull request #N。这条路没法重复用,每次都要人盯着。

现在推 v* 标签就跑完整条链路。

做了什么

.github/workflows/release.yml

三段 job,任何一段红了后面都不跑:

job 做什么 为什么这么排
test Node 20/22 双档跑全套单测 装法与 test.yml 完全一致(--omit=peer),否则测不到 rules-remote 的降级形状
publish 校 tag 版本 → 查 registry → npm publish --provenance → 回查 先校版本再发:npm 上的版本号发出去改不了
release 列提交 → 拼正文 → gh release create 等 publish 成功才建 Release,发不出去就不该先挂一个出来

几个具体决定:

  • tag 版本必须等于 package.json 版本,对不上直接红。不用「自动以 tag 为准改文件」那种做法——发出去的东西要和仓库里的一致。
  • registry 上已有该版本就跳过 publish,但 Release 照建。补 Release 时不用手忙脚乱。
  • --provenance 需要 id-token: write,已给。NPM_TOKEN 为空时直接 ::error:: 退出,不回落 OIDC——这个仓库没配 OIDC,静默回落只会让人以为发了。
  • 发完回查 30 秒(seq 1 10 × sleep 3)。npm publish 返回 0 不等于 registry 能查到,这一步是防「显示成功但装不到」。

.github/scripts/release-body.mjs

Release 正文固定四段:描述 / 本次变更 / 安装 / 链接。

  • 提交从「上一个 tag..本 tag」取,过滤 merge 提交与 chore: 发版 X.Y.Z;
  • 有 release-notes/v<版本>.md 就用手写的:首行 # 标题 摘成 Release 标题,其余进「本次变更」,这时不再列提交;
  • 安装段给 npm install dsh-cura@<版本> + 重启 DSH + 登记 bundles 的提示,免得装完没反应。

补发老标签时那个 tag 的树里可能还没有这个脚本(脚本是后加的),workflow 会从默认分支 git show 一份,取不到才退化成自动说明。

测试

新增 tests/test-release-body.mjs(7 组),已接进 tests/all.mjs:8 套全绿 24212ms。

组 7 是 workflow 的静态接线断言——这类断言容易写成「查错误文案存在」,那样漏得厉害。M4 变异就是证据:把 if [ "$tag" != "$ver" ] 改成恒真,原断言照样绿。现在同时断言判定表达式本身,改完才红。

五条变异全部 EXIT=1,还原后全绿:

# 变异 结果
M1 删掉提交过滤 3 条红
M2 安装命令去掉 @version 2 条红
M3 改用 --generate-notes 1 条红
M4 tag 校验恒真 先没红 → 补断言后红
M5 release 改 needs: [test] 1 条红

本地也模拟跑过 release job:v0.8.0..v0.9.0 12 条提交 → 过滤后 9 条,生成 706 字节正文。

顺手修的

  • ref: ${{ env.RELEASE_TAG }} → ref: ${{ github.event.inputs.tag || github.ref_name }}(3 处)。env 上下文在 step 的 with: 里不可用,原来那样写 checkout 会拿到空 ref。
  • README 单测从「六套」改「八套」(漏了 care 与 release-body),新增「发版」一节。

没做的

  • 没动版本号。这版是 CI 改动,等你想发下一个功能版时一起 bump。
  • 没给 npm 加 trusted publishing(OIDC)。当前走 NPM_TOKEN,想换成无密钥的话得去 npm 网站配 trusted publisher,那是仓库外的一步。

之前发 0.9.0 是手工走的:本地 npm publish 卡在 2FA,tag 和 Release 也是
手打的,正文靠 --generate-notes 拼出一堆 merge 提交。这条路没法重复用。

现在推 v* 标签触发 .github/workflows/release.yml,分三段:

- test:Node 20/22 双档跑全套单测,装法与 test.yml 完全一致(--omit=peer
  是为了保住 rules-remote 的降级路径,装上 peer 就测不到真实形状)
- publish:先校验 tag 与 package.json 版本一致(对不上直接红——npm 上的
  版本号发出去就改不了),再查 registry 是否已有该版本(有则跳过),
  然后 npm publish --provenance,发完回查 30 秒确认真的能查到
- release:等 publish 成功才建 Release,发不出去就不该先挂一个出来

Release 正文由 .github/scripts/release-body.mjs 生成,固定四段:
描述 / 本次变更 / 安装 / 链接。没有手写说明时列上一个 tag 到本 tag 的
提交,过滤 merge 与「chore: 发版」;想写人话就放 release-notes/v<版本>.md,
首行 # 标题会被摘成 Release 标题。

补发老标签时那个 tag 的树里可能还没有这个脚本,workflow 会从默认分支取
一份,取不到才退化成自动说明。

顺手修了 README:单测从六套改八套(补上 care 与 release-body),并新增
发版一节写清触发方式与正文格式。
@modusensus
modusensus merged commit a51eba5 into main Nov 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant