- CLI
- 后端
- 云原生
【免费下载链接】vercel
Develop. Preview. Ship.
本指南以仓库根目录下的 RELEASE.md(CLI Release Runbook)为骨架,完整讲解 vercel 单仓库中vercelCLI 稳定版本从 changesets 合并、Version Packages自动 PR、管理员强制合并到最终发布 npm 的全链路,并深入到 .github/workflows/release.yml 与 utils/determine-release.mjs 等源码实现,解释"发布 PR 看起来卡住是预期行为"这一核心设计,以及发布后验证、latest回滚和相关发布工作流。读完本文,你将掌握在类似 changesets 单仓库中安全、可复用地设计和运维稳定版发布流水线的完整方法。
一、发布流程总览(TL;DR)
vercelCLI 的稳定版本发布遵循一条高度自动化的流水线,全链路可概括为四步:
- 将带有 changesets 的普通 PR 合并进
main分支; - 等待
Release工作流自动打开/更新名为Version Packages的发布 PR; - 由具备分支/规则集绕过权限的管理员对该 PR 执行强制合并(force merge),即使必需检查看起来卡住也不要等待;
- 合并动作再次触发
main上的Release工作流,由changesets/action执行版本更新与 npm 发布。
整个流程的核心思想是:Version PackagesPR 只扮演"快速交接"(fast handoff)的角色,真正的发布动作发生在它合并回main之后的下一次Release运行中。因此,仓库刻意不为这个机器人生成的 PR 跑完整的 CI 检查。
二、发布前置条件
在触发一次稳定版发布之前,需要确认以下三项前提全部满足:
- changesets 已合并:本次发布所包含的变更(changeset)已经合入
main分支,且没有遗留未处理的 changeset; Release工作流健康:main上最近的Release工作流运行正常,尤其是determine与release两个 job 没有持续失败;- 管理员可用:拥有分支/规则集绕过权限的管理员随时可对发布 PR 执行强制合并(该权限不在普通维护者手中,原文档指向的绕过用户列表位于仓库 Settings → Rules 对应规则集中,属仓库内部配置,读者可在自己的仓库中通过
Settings → Rules查看等效配置)。
三、为什么Version PackagesPR 看起来"卡住"了
这是本仓库发布模型中最容易让新人困惑的一点,也是文档重点解释的设计意图:
Version PackagesPR 由 .github/workflows/release.yml 中的changesets/action使用GITHUB_TOKEN(机器人身份)创建;- 对于这个机器人生成的 PR,仓库故意不期望它运行 PR CI;
- 尽管如此,规则集(Rulesets)仍可能要求
Summary、Summary (lint)、Summary (python-packages)等检查通过; - 这些检查在这个 PR 上会无限期地停留在 expected/pending 状态。
这是预期且刻意为之的行为——因为在 release.yml 中,这些Summary*检查 job(如Summary (release),见 .github/workflows/release.yml)绑定的是main分支的 push 触发,而不是 PR 触发。发布 PR 合并回main的那一刻,真正的发布工作流会立即被触发,因此团队不为这个 PR 等待 CI,而是直接走管理员强制合并路径。
四、稳定版发布逐步操作流程
步骤 1:等待发布 PR
changesets 落地main后,GitHub Actions 的Release工作流会运行并创建或更新标题为Version Packages的 PR。该工作流的触发条件是:
on: push: branches: - main即每次向main推送都会触发,它依据当前是否存在 pending changeset 决定是"打开/更新发布 PR"还是"直接发布"(详见下文源码解析)。
步骤 2:校验 PR 内容
合并前,管理员应审查该 PR 的 diff,确认其只包含发布机制生成的预期变更,典型包括:
- 各包
package.json中的 version 字段更新; - 各包的 changelog(变更日志)更新,例如 packages/cli/CHANGELOG.md、packages/backends/CHANGELOG.md 等;
- 版本同步引发的附带文件更新(如 Python 包的
pyproject.toml与packages/python/src/package-versions.ts,见下文ci:version说明)。
如果 PR 中出现了预期之外的文件变更,必须停下来调查后再合并,防止把非发布内容混入版本号提交。
步骤 3:使用管理员绕过权限强制合并
管理员应使用 bypass/force-merge 权限合并Version PackagesPR,操作要点:
- 不要等待那些根本没在运行的必需检查(它们永远不会通过,等待只会阻塞发布);
- 这是本仓库发布 PR 的既定路径,普通 PR 的常规合并规范不适用于此 PR。
步骤 4:确认main上的发布运行
合并 PR 会推送代码到main,再次触发Release工作流。这一次运行中,changesets/action会依次执行两个脚本(定义于 package.json):
pnpm ci:version pnpm ci:publish只要存在可发布的变更,就会发布包(包括vercelCLI 本身)并创建对应的 git tag。
五、源码级解析:Release工作流如何决定"发布还是只开 PR"
changesets/action的行为是二选一的互斥逻辑:
- 存在 pending changesets→ 只创建/更新
Version PackagesPR,不发布任何东西; - 不存在 pending changesets(即发布 PR 被合并后的形态)→ 执行 publish 脚本,这是
vercel唯一能到达 npm 的时机。
为了在下一次changesets/action运行前就知道这次 push 是否会真正发布,.github/workflows/release.yml 专门设置了一个determinejob,运行仓库自研脚本 utils/determine-release.mjs 并输出两个关键信号:
| 输出 | 含义 | 判定依据 |
|---|---|---|
will-publish | 本次 push 是否会真正执行ci:publish | changesets.length === 0(无 pending changesets) |
should-release-binary | 本次发布是否包含vercel,从而需要先发布原生二进制包 | 无 pending changesets 且vercel@<cli版本>尚未存在于 npm |
这个脚本的巧妙之处在于:
- 复用官方解析逻辑:它直接加载
@changesets/cli依赖中的@changesets/read来读取 changeset 状态(见 utils/determine-release.mjs),保证与changesets/action自身的行为不会产生漂移; - 硬性拒绝 pre mode:
assertNotPreMode会在发现.changeset/pre.json存在时直接抛错(见 utils/determine-release.mjs),因为 pre 模式下 pending changesets 的判定逻辑不同,宁可失败也不静默误判; - npm 检查带重试:
isPublishedOnNpm对npm view vercel@<version> version做最多 3 次重试,只有遇到明确的 E404 才判定"未发布",其余网络类错误一律按"未发布"处理(见 utils/determine-release.mjs)——因为误判"已发布"会让vercel在没有原生依赖的情况下发布,而误判"未发布"最多只是多构建一次二进制,代价更小。
determinejob 的输出进一步驱动了工作流的编排(见 .github/workflows/release.yml):
releasejob 只有在determine成功且binaryjob 成功或跳过时才会运行;binaryjob(复用 .github/workflows/release-binary.yml 可复用工作流)负责在发布vercel前先构建并发布@vercel/vc-native-*原生包,通过if: false当前处于暂时禁用状态,并在环境变量VERCEL_SKIP_NATIVE_DEPS=1时让 npm 发布跳过原生 optionalDependencies 的缺失校验(见 .github/workflows/release.yml)。
六、发布执行的幕后:ci:version与ci:publish
ci:version:版本号落地
changeset version && node utils/sync-python-version.js && uv lock && pnpm install --no-frozen-lockfile这条命令(见 package.json)依次完成:
changeset version:根据 changesets 内容提升各包版本号并生成 changelog;node utils/sync-python-version.js:将 Python 运行时包的版本从package.json同步到pyproject.toml,并重新生成packages/python/src/package-versions.ts版本导出文件(见 utils/sync-python-version.js),保证@vercel/python-runtime与@vercel/python-workers两个 Python 包与 npm 包保持同版本;uv lock:更新 Python 依赖锁文件;pnpm install --no-frozen-lockfile:让 workspace 内互相引用的版本依赖关系重新对齐。
ci:publish:真正的发布动作
node utils/inject-native-optional-deps.mjs && node utils/publish-runtimes.mjs && bash utils/npm-publish.sh && changeset tag这条命令(见 package.json)的发布顺序体现了"先依赖后本体"的原则:
utils/inject-native-optional-deps.mjs:为vercel注入原生二进制可选依赖;node utils/publish-runtimes.mjs:在 npm 发布之前先发布非 npm 的运行时包——目前委托给python/publish.mjs发布 PyPI Python 包(见 utils/publish-runtimes.mjs),且任何运行时发布失败都会中止后续 npm 发布;bash utils/npm-publish.sh:执行真正的 npm 发布(稳定版走latestdist-tag);changeset tag:为发布创建 git tags。
发布时工作流还设置了NPM_CONFIG_PROVENANCE: 'true'启用 npm 来源证明(provenance),并使用commitMode: 'github-api'让发布提交与 tag 可以用$GITHUB_TOKEN签名(见 .github/workflows/release.yml)。
七、发布后验证
Release工作流成功后,建议按以下命令验证发布结果:
# 查看 npm 上的最新版本号 npm view vercel version # 查看所有 dist-tag(latest / canary 等) npm dist-tag ls vercel此外可以(可选)核对 changesets 创建的 git tags 是否存在且指向正确的提交。验证时需要注意:如果发布包含原生包,还需确认@vercel/vc-native-*各平台包已可在 npm 上安装(release-binary.yml 中甚至有最多 12 次、每次间隔 10 秒的全局安装轮询验证逻辑,见 .github/workflows/release-binary.yml)。
八、回滚与热修复:Rollback Latest Tag工作流
如果latestdist-tag 指向了错误的版本,无需重新发布,只需调整 npm dist-tag。操作方式:
- 工作流:
Rollback Latest Tag(.github/workflows/rollback-latest-tag.yml),通过workflow_dispatch手动触发; - 输入:期望的稳定版本号,例如
39.2.4。
该工作流内部做了三层保护(见 .github/workflows/rollback-latest-tag.yml):
- 格式校验:输入必须匹配
^[0-9]+\.[0-9]+\.[0-9]+$的X.Y.Z格式,否则直接失败; - 存在性校验:先执行
npm view vercel@<版本> version确认该版本确实存在于 npm,不存在则打印最近 20 个版本并退出; - 执行回滚:
npm dist-tag add vercel@<版本> latest使用NPM_TOKEN_ELEVATED凭据切换 dist-tag,最后再次打印npm dist-tag ls vercel供确认。
九、相关发布工作流全景
仓库中与发布相关的自动化不止稳定版一条线,它们共同构成了完整的发布矩阵:
| 工作流文件 | 触发方式 | 发布目标 | 说明 |
|---|---|---|---|
| .github/workflows/release.yml | push 到main | npm(vercel等包) | 稳定版主链路,本文核心 |
| .github/workflows/canary.yml | push 到main | npm canary 快照 | 每次main推送都会执行pnpm ci:version:canary+pnpm ci:publish:canary(见 package.json),发布canarydist-tag 快照,用于提前验证 |
| .github/workflows/release-python-package.yml | workflow_dispatch | PyPI | 手动发布 Python 包,输入支持all或具体包名,并可--force强制发布;实际执行node python/publish.mjs |
| .github/workflows/release-crates.yml | push 到main(限crates/vercel_runtime/**、Cargo.toml、Cargo.lock路径)或手动 | crates.io | 通过crates-io-auth-action获取临时 token 后执行cargo publish(作用于crates/vercel_runtime) |
| .github/workflows/rollback-latest-tag.yml | workflow_dispatch | npm dist-tag | 回滚latest,不重新发布 |
| .github/workflows/release-binary.yml | workflow_call被 release.yml 调用 | npm 原生包 | 构建 macOS/Linux/Windows 五平台二进制(darwin-arm64/x64、linux-arm64/x64、win-x64),含 Apple 签名/公证与 smoke test,当前在 release.yml 中临时禁用 |
十、常见故障模式与排查
原文档总结了三类最常见的发布故障及其处理思路,结合源码可进一步细化:
1.Version PackagesPR 没有出现
- 排查:查看
main上最新一次Release工作流运行,确认在changesets/action步骤之前是否有 job 失败。常见诱因包括determinejob 中的脚本报错(例如误创建了.changeset/pre.json触发assertNotPreMode硬失败)、依赖安装失败或构建失败(release.yml 在调用changesets/action前会执行pnpm build,见 .github/workflows/release.yml)。
2.Version PackagesPR 检查卡住
- 结论:这是该流程的预期行为,不是故障。规则集要求的
Summary系列检查在该 PR 上永远不会通过,无需等待,直接走管理员强制合并即可。
3. 合并后main上的发布运行失败
- 处理:先修复
main上的问题,然后重新运行Release工作流;或者先合入修复代码,让下一次 push 触发的Release完成发布。注意releasejob 的失败会连带Summary (release)job 失败(见 .github/workflows/release.yml),排查时优先看determine、binary、release三个 job 各自的结果。
结语
vercelCLI 的稳定版发布流水线是一个"changesets 状态机 + 管理员强制合并 + main 二次触发"的典型实现:它把"生成版本号"与"真正发布"拆成两次mainpush 接力完成,用determine-release.mjs在发布前精确预判动作,用ci:version/ci:publish串起 npm、PyPI、crates.io 与原生二进制的多语言发布矩阵,最后用Rollback Latest Tag提供不重发的回滚兜底。理解这套模型后,你既可以在本仓库中顺畅地推动一次 CLI 发版,也可以把同样的"自动开 PR + 管理员强制合并 + 二次触发发布"模式复用到任何基于 changesets 的 pnpm 单仓库中。
- CLI
- 后端
- 云原生
【免费下载链接】vercel
Develop. Preview. Ship.
相关推荐
Mastra 版本发布工作流:@internal/changeset-cli 自定义 Changesets 工具深度解析
Mastra 版本发布工作流:@internal/changeset cli 自定义 Changesets 工具深度解析 导读 @internal/change
人工智能Agent 框架AI AgentRAG后端OpenZeppelin Contracts 全自动发布流程解析:Changesets、release-vX.Y 分支与 release-cycle 工作流
OpenZeppelin Contracts 全自动发布流程解析:Changesets、release vX.Y 分支与 release cycle 工作流 O
区块链Web3GreptimeDB 版本发布 Runbook:从 Tag、GitHub Release 到文档 PR 的完整发布流程
GreptimeDB 版本发布 Runbook:从 Tag、GitHub Release 到文档 PR 的完整发布流程 本文是 GreptimeDB(开源可观测
时序数据库数据库可观测性
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考