news 2026/9/21 18:46:25

vercel CLI 稳定版本发布全流程:changesets 驱动的 Release Runbook 深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vercel CLI 稳定版本发布全流程:changesets 驱动的 Release Runbook 深度解析
  • CLI
  • 后端
  • 云原生

【免费下载链接】vercel

Develop. Preview. Ship.

项目地址:https://gitcode.com/gh_mirrors/ve/vercel
点击查看免费下载

本指南以仓库根目录下的 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 的稳定版本发布遵循一条高度自动化的流水线,全链路可概括为四步:

  1. 将带有 changesets 的普通 PR 合并进main分支;
  2. 等待Release工作流自动打开/更新名为Version Packages的发布 PR;
  3. 由具备分支/规则集绕过权限的管理员对该 PR 执行强制合并(force merge),即使必需检查看起来卡住也不要等待;
  4. 合并动作再次触发main上的Release工作流,由changesets/action执行版本更新与 npm 发布。

整个流程的核心思想是:Version PackagesPR 只扮演"快速交接"(fast handoff)的角色,真正的发布动作发生在它合并回main之后的下一次Release运行中。因此,仓库刻意不为这个机器人生成的 PR 跑完整的 CI 检查。

二、发布前置条件

在触发一次稳定版发布之前,需要确认以下三项前提全部满足:

  • changesets 已合并:本次发布所包含的变更(changeset)已经合入main分支,且没有遗留未处理的 changeset;
  • Release工作流健康main上最近的Release工作流运行正常,尤其是determinerelease两个 job 没有持续失败;
  • 管理员可用:拥有分支/规则集绕过权限的管理员随时可对发布 PR 执行强制合并(该权限不在普通维护者手中,原文档指向的绕过用户列表位于仓库 Settings → Rules 对应规则集中,属仓库内部配置,读者可在自己的仓库中通过Settings → Rules查看等效配置)。

三、为什么Version PackagesPR 看起来"卡住"了

这是本仓库发布模型中最容易让新人困惑的一点,也是文档重点解释的设计意图:

  • Version PackagesPR 由 .github/workflows/release.yml 中的changesets/action使用GITHUB_TOKEN(机器人身份)创建;
  • 对于这个机器人生成的 PR,仓库故意不期望它运行 PR CI;
  • 尽管如此,规则集(Rulesets)仍可能要求SummarySummary (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.tomlpackages/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的行为是二选一的互斥逻辑:

  1. 存在 pending changesets→ 只创建/更新Version PackagesPR,不发布任何东西;
  2. 不存在 pending changesets(即发布 PR 被合并后的形态)→ 执行 publish 脚本,这是vercel唯一能到达 npm 的时机。

为了在下一次changesets/action运行前就知道这次 push 是否会真正发布,.github/workflows/release.yml 专门设置了一个determinejob,运行仓库自研脚本 utils/determine-release.mjs 并输出两个关键信号:

输出含义判定依据
will-publish本次 push 是否会真正执行ci:publishchangesets.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 modeassertNotPreMode会在发现.changeset/pre.json存在时直接抛错(见 utils/determine-release.mjs),因为 pre 模式下 pending changesets 的判定逻辑不同,宁可失败也不静默误判;
  • npm 检查带重试isPublishedOnNpmnpm 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:versionci:publish

ci:version:版本号落地

changeset version && node utils/sync-python-version.js && uv lock && pnpm install --no-frozen-lockfile

这条命令(见 package.json)依次完成:

  1. changeset version:根据 changesets 内容提升各包版本号并生成 changelog;
  2. 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 包保持同版本;
  3. uv lock:更新 Python 依赖锁文件;
  4. 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)的发布顺序体现了"先依赖后本体"的原则:

  1. utils/inject-native-optional-deps.mjs:为vercel注入原生二进制可选依赖;
  2. node utils/publish-runtimes.mjs在 npm 发布之前先发布非 npm 的运行时包——目前委托给python/publish.mjs发布 PyPI Python 包(见 utils/publish-runtimes.mjs),且任何运行时发布失败都会中止后续 npm 发布;
  3. bash utils/npm-publish.sh:执行真正的 npm 发布(稳定版走latestdist-tag);
  4. 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):

  1. 格式校验:输入必须匹配^[0-9]+\.[0-9]+\.[0-9]+$X.Y.Z格式,否则直接失败;
  2. 存在性校验:先执行npm view vercel@<版本> version确认该版本确实存在于 npm,不存在则打印最近 20 个版本并退出;
  3. 执行回滚npm dist-tag add vercel@<版本> latest使用NPM_TOKEN_ELEVATED凭据切换 dist-tag,最后再次打印npm dist-tag ls vercel供确认。

九、相关发布工作流全景

仓库中与发布相关的自动化不止稳定版一条线,它们共同构成了完整的发布矩阵:

工作流文件触发方式发布目标说明
.github/workflows/release.ymlpush 到mainnpm(vercel等包)稳定版主链路,本文核心
.github/workflows/canary.ymlpush 到mainnpm canary 快照每次main推送都会执行pnpm ci:version:canary+pnpm ci:publish:canary(见 package.json),发布canarydist-tag 快照,用于提前验证
.github/workflows/release-python-package.ymlworkflow_dispatchPyPI手动发布 Python 包,输入支持all或具体包名,并可--force强制发布;实际执行node python/publish.mjs
.github/workflows/release-crates.ymlpush 到main(限crates/vercel_runtime/**Cargo.tomlCargo.lock路径)或手动crates.io通过crates-io-auth-action获取临时 token 后执行cargo publish(作用于crates/vercel_runtime
.github/workflows/rollback-latest-tag.ymlworkflow_dispatchnpm dist-tag回滚latest,不重新发布
.github/workflows/release-binary.ymlworkflow_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),排查时优先看determinebinaryrelease三个 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.

项目地址:https://gitcode.com/gh_mirrors/ve/vercel
点击查看免费下载
上一篇:Vendure电商平台:Admin UI页面操作栏按钮扩展指南
下一篇:BV 开发者指南:Jetpack Compose 在TV应用中的最佳实践

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 18:46:16

改进减法优化器算法GSABO:融合黄金正弦与混沌映射

1. 项目概述在智能优化算法领域&#xff0c;2023年新提出的减法优化器算法(SABO)因其独特的数学基础和优化机制引起了广泛关注。作为一名长期从事算法优化研究的工程师&#xff0c;我在实际应用中发现原始SABO算法在解决高维非线性问题时存在收敛速度不稳定、易陷入局部最优等问…

作者头像 李华
网站建设 2026/9/21 18:44:11

企业级3D模型轻量化:从评估维度到工程实测的选型指南

前阵子有家做工业设备选型平台的客户&#xff0c;把一套装配模型扔给我&#xff1a;原始CAD导出STEP文件1.8GB&#xff0c;转到OBJ后1200多万个三角面&#xff0c;加载到浏览器里直接白屏。他们内部吵了一个星期——设计部门坚持模型一个倒角都不能少&#xff0c;前端要求首页3…

作者头像 李华
网站建设 2026/9/21 18:38:41

ABAP类型系统中的协变规则与安全实践

1. ABAP中的协变问题&#xff1a;隐藏在严格语法规则下的类型安全逻辑在ABAP开发领域&#xff0c;类型系统的设计哲学与Java等语言有着本质区别。作为一名长期从事SAP系统开发的工程师&#xff0c;我发现很多从Java转型到ABAP的同行最初都会低估类型系统差异带来的影响。ABAP确…

作者头像 李华
网站建设 2026/9/21 18:38:39

Java多线程编程实战:从基础到高并发系统设计

1. 为什么每个Java开发者都需要掌握多线程记得刚工作那会儿&#xff0c;我接手了一个简单的订单处理系统。在测试环境跑得好好的程序&#xff0c;一上线就频繁崩溃。排查了三天才发现&#xff0c;当并发用户超过50时&#xff0c;系统就会因为线程阻塞而雪崩。这个惨痛教训让我明…

作者头像 李华
网站建设 2026/9/21 18:34:07

Codex 跑 Trae+Docker+SSH 插件恢复脚本:Key 用 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华