Ant Design 发版工作流实战:从 Release PR 到 npm publish 的完整操作手册
Ant Design 的版本发布并非简单的“改个版本号 + npm publish”:它的发布准备是一个仅更新 changelog 与 package.json 的普通 PR,而真正的 npm 包发布被刻意推迟到 PR 合并之后,并依赖 prepublishOnly / postpublish 钩子自动完成 CI 检查、产物下载、Git tag 推送与下游通知。本文基于仓库中的发版 skill 文档(.agents/skills/version-release/SKILL.md)与对应的发布脚本源码,梳理出一条可直接照做的发版操作路径:读完你可以独立完成 release PR 的分支判断、发布文件更新、提交前校验,以及在 PR 合并后安全地执行 npm publish。
发版流程的定位与边界
Ant Design 的发版工作流明确分为两个阶段,且职责边界清晰:
- 准备 release PR:在 changelog 已准备好的前提下,更新
package.json并提交一个指向master的发布准备 PR; - 正式发布:在 release PR 合并之后,执行
npm publish。
需要特别注意的边界是:changelog 内容的收集、筛选与改写不在此流程的职责范围内。发版 skill 文档中明确要求:如果 changelog 尚未准备好,应先切换到 changelog-collect 相关流程,而不是在发版过程中临时生成 changelog 文案。这保证了发版操作者只处理“发布准备”这一窄而确定的任务。
分支选择规则
发版的第一原则是分清代码来源分支与 PR 目标分支:
- 新特性代码来自
feature分支(通过 auto merge 合入master),但 release PR 始终向master提交; - 无论是 patch 还是 minor 发布,release PR 的 base 分支都是
master; - 维护分支发版(如 5.x、4.x 的长期支持)则沿用已有稳定分支,例如
5.x-stable、4.x-stable。
这一约定在下游 workflow 中也有体现:tag 触发的发布通知工作流 release-dingtalk.yml 中,branch 配置正是 master, 5.x-stable, 4.x-stable,tag 过滤为 6*, 5*, 4*,与“主干 + 稳定维护分支”的发版模型完全对应。
发布类型:Patch 与 Minor 的判断标准
除非用户明确要求其它流程,常规发版只处理两类:
| 类型 | 代码来源 | 版本变化 | 使用场景 |
|---|---|---|---|
| Patch | master 上的 bugfix |
x.y.z -> x.y.(z+1) |
Bug 修复、稳定性发布 |
| Minor | feature 分支合入的新特性 |
x.y.z -> x.(y+1).0 |
特性批量发布 |
判断口径:如果改动里明显包含未发布的新特性,优先判断为 minor;否则默认按 patch 处理。 以当前仓库 package.json 为例,version 字段为 6.6.2,若本次合并的仅是 bugfix,目标版本就是 6.6.3;若包含 feature 分支合入的新特性,则应升到 6.7.0。
Release PR 准备流程
第 1 步:确认目标分支与当前版本
开始前至少要检查以下四项信息,对应命令为:
git branch -vv
git rev-parse --abbrev-ref HEAD
cat package.json
git tag --list | grep -v -E '(experimental|alpha|resource)' | sort -V | tail -20
逐条确认:
- 当前要发布的是哪条分支;
package.json里的当前版本号(例如当前的6.6.2);- 最近一次有效 release tag(排除
experimental、alpha、resource等预发布/资源类 tag); - 下一个版本应该升 patch 还是 minor。
第 2 步:确认 changelog 已经准备好
发版流程默认把 changelog 视为前置输入,而不是当前步骤的输出。开始 release PR 前需确认:
CHANGELOG.en-US.md已经有目标版本条目;CHANGELOG.zh-CN.md已经有对应的中文条目;- changelog 文案已经过筛选与格式整理。
如果这些内容还不存在,先走 changelog-collect 流程,不要在发版 skill 中临时生成。
第 3 步:更新发布文件
发布准备通常只应检查并更新这三个文件:
CHANGELOG.en-US.mdCHANGELOG.zh-CN.mdpackage.json
对这三个文件有硬性要求:
- 中英文 changelog 必须已经存在且内容对齐;
- 新增 section 标题必须与目标版本完全一致,例如
## 6.6.3; - 必须有日期行,且日期应接近当前发布日期,格式为反引号包裹的
2026-03-31形式; package.json中的版本号必须与 changelog 中的版本一致。
除非明确要求,否则不要额外引入别的 release 元数据文件——发版 PR 保持最小 diff。
这一“版本号与 changelog 必须匹配”的规则在发布前会被脚本强制校验。scripts/check-version-md.ts 的逻辑(见 scripts/check-version-md.ts#L32-L63):
- 先校验
package.json的version是否符合x.y.z语义化格式,预发布版本(带-alpha./-beta./-rc.等后缀)会直接跳过检查; - 在
CHANGELOG.en-US.md中查找与当前版本完全匹配的## {version}section,找不到即exit(1)报错 “No changelog found for the version to be released”; - 找到后,取该 section 的第一行作为日期行,要求它是反引号包裹的日期,且距离当前时间 ±2 天以内(
date.isBetween(dayjs().add(-2, 'day'), dayjs().add(2, 'day'))),否则报 “The date wrongly written”。
这解释了为什么 changelog 的日期行不能随便写旧日期——它会被脚本当成发布时效性校验的一部分。
第 4 步:提交前校验
至少运行:
npm run lint:changelog
必要时再运行:
npm run version
两条命令的实际行为可从 package.json 的 scripts 字段确认:
lint:changelog对应tsx scripts/generate-component-changelog.ts。从 scripts/generate-component-changelog.ts 源码看,它会解析中英文 changelog,把每个条目按组件名归类(如Input、Grid、Message等),并额外匹配Global:、MISC:等杂项关键字;如果某一行既不属于任何组件、也不属于 misc 关键字,脚本会打印🚨 Miss Component: ...并抛出Component changelog miss match!错误(见 scripts/generate-component-changelog.ts#L228-L249)。这意味着 changelog 文案中组件名称的书写方式直接决定 lint 能否通过。version对应tsx scripts/generate-version.ts,它只做一件事:把package.json的版本号写入 components/version/version.ts(生成export default 'x.y.z';)。它只在需要刷新本地生成的版本文件、做校验时才运行——不要把无关的生成文件顺手带进 release PR,除非仓库本来就预期它们会被更新。
发布前还可以直接运行 tsx scripts/check-version-md.ts 来做上文提到的版本-changelog 一致性校验。
第 5 步:提交与 PR 规范
release 准备 PR 的标题通常形如:
docs: add changelog for 6.3.5docs: release 6.3.5
硬性规范有四点:
- 如果 PR 标题里包含
release或changelog,就必须带上版本号——这一点由 CI 强制。.github/workflows/verify-package-version.yml 在所有 PR 的opened / edited / reopened / synchronize / ready_for_review事件上触发,且仅当标题包含changelog或release时生效(if: contains(...)条件,见 verify-package-version.yml#L20),使用actions-cool/verify-package-version校验title-include-content: docs与title-include-version: true,不通过会直接在 PR 上评论; - PR 标题保持英文;
- 创建 PR 时必须使用仓库官方模板 PULL_REQUEST_TEMPLATE.md,其中 “📝 Change Log” 一节要求按 Keep a Changelog 的口径,分别填写中英文两栏 changelog 文案;
- 生成 PR body 时保留模板原有 section,简要说明本次发布范围即可——changelog 类 PR 的正文可以简洁,因为详细内容已经写在 changelog 文件里。
正式发布流程:PR 合并之后
这部分只在 release PR 已经合并、且明确要求“现在发布”时才执行。发布前必须逐项确认:
- 当前分支已经切到合并后的目标分支,通常是
master; - 工作区是干净的;
- 远端分支已同步到最新;
package.json中的版本号已经是目标发布版本;CHANGELOG.en-US.md中存在该版本的 changelog 条目;CHANGELOG.zh-CN.md中存在对应的中文条目。
执行发布命令
npm publish
在 package.json 中,prepublishOnly 指向 tsx ./scripts/pre-publish.ts,postpublish 指向 tsx scripts/post-publish.ts(见 package.json#L71 与 package.json#L76),因此一条 npm publish 背后实际串起了完整的自动化链。
prepublishOnly 阶段:pre-publish.ts 做了什么
从 scripts/pre-publish.ts 源码可以确认发布前的完整调用链:
- 仓库状态检查(
checkRepo(),实现在 scripts/check-repo.ts):checkVersion(scripts/check-repo.ts#L18-L44):查询registry.npmmirror.com与registry.npmjs.org两个注册表,若package.json中的版本已存在,立即报错 “Current version already exists. Forget update package.json?”——这是防止重复发版的最后一道闸;checkBranch:正式版本必须位于master分支(-alpha./-beta./-rc./-experimental.版本跳过该检查);checkCommit:git 工作区必须干净,有未提交文件直接退出;checkRemote:远端必须是ant-design/ant-design,防止在 fork 仓库上误发布;checkToken:要求设置GITHUB_ACCESS_TOKEN环境变量。
- 同步远端:对当前分支执行
git pull+git push,确保本地与远程一致(scripts/pre-publish.ts#L109-L115)。 - 远程 CI 状态检查:通过 Octokit 查询目标 SHA 的 combined status,
failure/pending/ 非success一律process.exit(1)终止发布(scripts/pre-publish.ts#L130-L171)。除非显式设置SKIP_CI_CHECK环境变量,否则这一步不可绕过(脚本头部注释给出用法:SKIP_CI_CHECK=1 npm run prepublishOnly)。 - 清理与下载构建产物:先执行
clean,然后并发地从两个来源拉取与目标 SHA 对应的构建产物(scripts/pre-publish.ts#L177-L258):- GitHub:定位该 SHA 上成功的
✅ testworkflow run,下载名为build artifacts的 artifact 为artifacts.zip; - OSS:按
https://antd-visual-diff.oss-accelerate.aliyuncs.com/{sha}/oss-artifacts.zip的地址下载。 两者通过Promise.any竞速,任一成功即可;全部失败则提示“请确认你当前 {sha} 位于 master 分支中”并退出。
- GitHub:定位该 SHA 上成功的
- 产物自检:解压后依次运行
test:dekko(包结构检查)与test:package-diff(包内容 diff 检查),通过后才发送“产物已经准备好了,快回来输入 npm 校验码了”的系统通知。
postpublish 阶段:tag 推送与 conch 版本
npm publish 成功后,postpublish 钩子执行 scripts/post-publish.ts:
- 推送 Git tag:
git tag --no-sign {version}并推送到远端(scripts/post-publish.ts#L35-L49)。--no-sign是刻意的——即使本地配置了tag.gpgSign,也保持轻量 tag,避免卡在签名编辑器上。这正是发版文档强调“不要在 publish 前手动创建或推送版本 tag”的原因:tag 完全由postpublish统一处理; - 选择并更新 conch 版本:脚本从 npm 注册表拉取最近 30 个正式发布版本,结合 BUG_VERSIONS.json 过滤掉已知有缺陷的版本(标记 🚨),按“发布满 15 天、且与相邻版本间隔超过 3 天视为稳定”的启发式算法给出默认推荐,然后交互式地执行
npm dist-tag add antd@{conchVersion} conch-v6(scripts/post-publish.ts#L163-L167)。
tag 触发下游工作流
tag 推送后,tag 创建事件(on: create 且 ref_type == 'tag')触发 release-dingtalk.yml,由 actions-cool/release-helper 基于 CHANGELOG.en-US.md、CHANGELOG.zh-CN.md 自动生成 GitHub Release,并向钉钉群推送发布通知。至此完成“npm publish → tag → GitHub Release + 群通知”的闭环。
不要做的事
以下几条是发版流程中的明确禁区:
- 不要使用
npm run pub:package.json#L75 中该脚本被显式定义为echo 'Please use \npm publish` instead.',仓库已经用脚本本身提示应使用npm publish`; - 不要在 publish 前手动创建或推送版本 tag:tag 由
postpublish钩子统一推送,手动创建会破坏 tag 命名与推送时机的一致性; - changelog 或版本号缺失时不要直接发布:
check-repo.ts会拦截重复版本,check-version-md.ts会拦截版本与 changelog 不一致的情况,绕过校验直接发布只会把问题带到线上。
发版操作清单(Checklist)
按 skill 文档中“Agent 执行动作”一节整理的端到端清单:
- 检查当前分支、版本号和最近 tags(
git branch -vv、git tag --list); - 根据目标分支与发布目标判断 patch 还是 minor;
- 确认 changelog 已由
changelog-collect或用户提前准备好(中英文条目对齐、section 标题与日期行合规); - 更新 package.json 中的
version字段; - 运行
npm run lint:changelog和tsx scripts/check-version-md.ts校验; - 用带版本号的
docs:标题提交,按 PULL_REQUEST_TEMPLATE.md 以正确的 base 分支(master或x.x-stable)创建 PR; - 只有在 PR 合并后、且明确要求时,才执行
npm publish——如果任务只是“准备 release PR”,创建 PR 后就应停止,不要继续执行发布。
适用前提与限制
- 本文描述的是当前仓库(
antd6.x,package.json版本6.6.2)的实际发布机制;维护分支(如5.x-stable、4.x-stable)发版时分支规则略有不同,但“release PR +npm publish+ postpublish 推 tag”的主干流程一致; npm publish需要GITHUB_ACCESS_TOKEN环境变量与可访问 ant-design 官方仓库的权限,SKIP_CI_CHECK仅用于确需跳过远程 CI 检查的特殊场景;- 构建产物必须已由远端 CI 生成(GitHub artifact 或 OSS),本地
prepublishOnly不会自行构建——这也是“产物必须来自合并后的masterSHA”这一前提的来源。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00