首页
/ 如何发布 Deno 版本:start_release 工作流与 crates 发布流水线的源码级解析

如何发布 Deno 版本:start_release 工作流与 crates 发布流水线的源码级解析

2026-09-06 13:25:01作者:裴麒琰

本文以 Deno 仓库自带的 发布说明 为主线,完整讲清从触发 start_release GitHub Actions 工作流、获取 Gist 版发布清单,到版本 bump、crates 发布、打 tag、验证产物资产的全流程。读完本篇后,你能独立执行一次 Deno CLI 版本发布,并且理解 tools/release/ 目录下每个自动化脚本在背后做了什么。

发布流程总览:四步入口与自动化管线

tools/cut_a_release.md 给出的官方入口非常简短,核心只有四步:

  1. 打开 start_release 这个工作流(对应仓库中的 start_release 定义 与生成的 start_release.generated.yml);
  2. 选择 main 分支和发布类型(release kind),然后运行该工作流;
  3. 等待 “Create Gist URL” 这一步完成,并查看它的输出,得到 Gist 的 URL;
  4. Fork 这个 Gist,并按 Gist 中生成的清单逐条执行。

从源码结构看,整个发布过程是一条“工作流 + 脚本 + 模板”的组合管线:

start_release 工作流(人工触发)
  └─ Create Gist URL 步骤
       └─ tools/release/00_start_release.ts
            ├─ 从 cli/Cargo.toml 读取当前版本
            ├─ 计算下一个版本号(patch/minor/major/alpha/beta/rc)
            └─ 渲染清单模板 → 创建私有 Gist(release_版本号.md)

按 Gist 清单继续:
  Phase 1  version_bump 工作流
      └─ tools/release/01_bump_crate_versions.ts   改版本、更新 Releases.md
      └─ tools/release/02_create_pr.ts             开 draft PR
  Phase 2  cargo_publish 工作流
      └─ tools/release/03_publish_crates.ts        按依赖顺序 cargo publish
      └─ tools/release/04_post_publish.ts          创建 v$VERSION tag、回传 main
      └─ tools/release/05_create_release_notes.ts  生成 GitHub Release 说明
  之后     更新官网 / 文档 / Docker / PyPI / MDN,最后“解锁”仓库

第一步:运行 start_release 工作流

工作流输入与运行环境

.github/workflows/start_release.ts 是该工作流的生成脚本(生成的 YAML 文件头部标注 GENERATED BY ./start_release.ts -- DO NOT DIRECTLY EDIT,即 YAML 由脚本生成,不应直接编辑)。它定义了一个 workflow_dispatch 事件,唯一的输入参数是:

参数 类型 取值 说明
releaseKind choice(必填,默认 patch patch / minor / major 本次发布的版本号递增方式

工作流的 job 配置要点:

  • 运行在 ubuntu-24.04 上,超时 30 分钟;
  • 通过 denoland/setup-deno@v2 安装 deno-version: v2.x(注意:发布工具链本身就要求较新的 Deno 2.x 来运行 tools/release/ 下的 TypeScript 脚本);
  • 环境设置 RUST_BACKTRACE: fullRUSTC_FORCE_INCREMENTAL: 1,便于失败时排查。

“Create Gist URL” 步骤的实际执行内容

工作流最后一步(也就是原文档让你等待并查看输出的那一步)执行的命令是:

./tools/release/00_start_release.ts --${{github.event.inputs.releaseKind}}

这一步用 secrets.DENOBOT_GIST_PAT 作为 GITHUB_TOKEN,并透传 github.actor 作为 GH_WORKFLOW_ACTOR(后续脚本开 PR 时会 cc @触发者)。

第二步:Gist 是怎么生成的——00_start_release.ts 源码解析

tools/release/00_start_release.ts 是整个管线的版本计算中枢,主要做了三件事:

1. 读取当前版本并计算下一个版本

getCliVersion() 从仓库中的 cli/Cargo.toml 用正则 ^version\s*=\s*"([^"]+)"$ 提取 version 字段作为当前 CLI 版本。随后 getNextVersion()(见 00_start_release.ts 第 39–61 行)根据命令行参数决定递增策略:

  • --patch / --minor / --major:按 semver 常规递增;
  • --alpha / --beta / --rc(预发布):若当前版本不是预发布版(例如 2.7.0),先递增 minor(2.7.0 → 2.8.0-alpha.0);若当前版本是另一种预发布类型(例如 3.0.0-alpha.12 要发 beta),先去掉旧 prerelease 标识再递增(→ 3.0.0-beta.0);
  • 没有任何参数时抛出 Missing argument

2. 选择清单模板并替换变量

buildDenoReleaseInstructionsDoc()(见 00_start_release.ts 第 67–85 行)按是否为预发布选择模板:

模板中的占位符会被统一替换:

占位符 替换值 含义
$BRANCH_NAME v<major>.<minor> 发布冻结的分支名(如 v2.7
$VERSION 计算出的完整新版本号 2.7.1
$MINOR_VERSION <major>.<minor> 维护分支名
$PAST_VERSION 当前 CLI 版本 上一个已发布版本

3. 创建私有 Gist 并打印 URL

非 dry-run 时,脚本通过 Octokit 执行 POST /gists 创建一个非公开 Gist,描述为 Deno CLI v$VERSION release checklist,文件名为 release_<版本号>.md,内容即渲染后的清单。最后在日志中打印 Gist 的 html_url,并提示:

Please fork the gist and follow the checklist.

这也解释了原文档第 4 步“按 Gist 里的说明操作”的由来——清单第一行就是 “Fork this gist and follow the instructions there”。本地调试时可以用 --dry-run 参数(00_start_release.ts 第 16–17 行)直接把渲染后的清单打印到 stdout,而不创建 Gist。

第三步:按 Gist 清单执行正式版发布

以下以正式版清单 release_doc_template.md 为准完整梳理各阶段。

Pre-flight:发布前检查与分支冻结

清单要求在整个发布过程中,$BRANCH_NAME(即 v<major>.<minor> 分支)必须冻结、禁止合入任何提交,直到发布结束。检查项包括:

  • 确保持有 denoland/denodenoland/dotcom(官网)、denoland/deno_dockerdenoland/deno-docs 等仓库的 fork 与本地克隆;
  • 检查 deno.land 的基准测试页面,确认近期没有性能回退;
  • 在公司 #cli 频道发出“上锁”公告(模板给出的示例文案):
:lock:

@here

Deno v$VERSION is now getting released.

denoland/deno is now locked.

*DO NOT LAND ANY PRs*

Release checklist: <LINK TO THIS FORKED GIST GOES HERE>

Phase 1:版本 bump(version_bump 工作流)

在 CLI 仓库的 Actions 中运行 version_bump 工作流(仓库内对应 version_bump 生成脚本version_bump.generated.yml):

  1. 点击 “Run workflow”,选择 main 分支;
  2. 选择发布类型 patchminor(正式清单;预发布清单则选 alpha/beta/rc);
  3. 等待工作流完成,它会自动打开一个 Pull Request。审阅、必要时修改后合并;
  4. 清单特别强调:⛔ 不要手动创建 release tag——打 tag 是后续 CI 自动完成的。

工作流背后:01_bump_crate_versions.ts 实际改了哪些文件

失败兜底手册中给出的手动命令就是 tools/release/01_bump_crate_versions.ts,它完整暴露了 version bump 阶段的全部改动:

  • 三个核心 crate 同步版本第 51–62 行):递增 cli(即 deno)crate 的 patch/minor/major 版本,然后把同一版本写入 deno_runtime crate 以及 deno lib crate 的 cli/lib/version.txt
  • 所有 CLI 依赖的内部 crate 递增 minor第 64–71 行),但 deno_v8 是特例——它在根 Cargo.toml 中以 v8 = { package = "deno_v8", ... } 的改名字段声明,通用逻辑按 crate 名匹配不到它,源码里用一个 renamedCrates 集合单独处理,手工正则改写根 Cargo.toml 与其自身 manifest 的 version(第 90–121 行);
  • 强制更新锁文件cargo update --workspace
  • 二进制版本自检assertDenoBinaryVersion() 会真实执行 cargo run -p deno -- -v 并比对输出与预期版本号,不一致直接退出码 1(第 214–222 行);
  • 自动更新 Releases.md:从上游 tag 之间取 git log(patch 版本与 minor 版本取 log 的范围不同,minor 版本会剔除上一个 minor 已有的提交),major/minor 版本还会在条目头部加上博客链接(第 135–190 行);
  • 递增 CI 缓存版本号:读取 .github/workflows/ci.ts 中的 const cacheVersion = N; 并 +1,保证每次发布后 CI 缓存失效重建(第 192–212 行)。

bump 完成后由 tools/release/02_create_pr.ts 收尾:创建名为 release_<major>_<minor>_<patch>(版本号中的 . 换成 _)的分支、提交并推送,然后通过 GitHub API 打开一个 draft PR,PR 正文自带检查项:

Bumped versions for <version>

Please ensure:
- [ ] Crate versions are bumped correctly
- [ ] Releases.md is updated correctly (think relevancy and remove reverts)

Phase 2:发布(cargo_publish 工作流)

cargo_publish 工作流 上对 Phase 1 所用的同一分支运行并等待完成。清单给出的失败兜底步骤(先重试,因为该工作流设计为可重入;仍失败则手动执行 03_publish_crates.ts,或补打 v$VERSION tag)对应脚本逻辑:

  • 按依赖顺序发布03_publish_crates.ts 先用 getCratesPublishOrder() 对 CLI 的依赖 crate 拓扑排序,逐个执行 cargo publish --no-verify;源码注释解释了 --no-verify 的原因:这些 crate 单独构建时依赖 deno_core 默认 features 关闭,deno_v8 门面选不到 engine 会命中 compile_error!,只有顶层 deno crate(其 v8/quickjs features 选择 engine)才会走完整校验;最后发布 deno crate 本体(第 14–30 行);
  • 打 tag 并回传 main04_post_publish.ts 创建并推送 v<version> tag(若已存在则跳过),并在 patch 发布场景下把发布 commit cherry-pick 回 main 开一个 draft PR,保证 main 上的版本号与已发布版本一致(第 33–114 行);
  • 生成 Release 说明05_create_release_notes.tsReleases.md 提取最新版本条目,写到 target/release/release-notes.md,供 GitHub draft release 使用。

tag 推送会触发第二次 CI 运行,自动在 GitHub 上创建 draft release,并把构建产物上传到 dl.deno.land

产物数量验证:46 个资产与 48 个 zip

清单要求人工核对两个硬性指标(这是原文档中具体、可验证的验收点):

  • GitHub release draft 上 v$VERSION46 个 assets
  • dl.deno.landrelease/v$VERSION 目录下有 48 个 zip 文件

核对通过后,在 GitHub 上正式发布(publish)该 release。

生态仓库与文档的后续更新

发布本体完成后,清单还列出一连串“外围”步骤,每个都是独立仓库的 workflow + 自动 PR:

  1. 官网(deno.com):运行 dotcom 仓库的 update_version.yml 工作流自动开 PR,合并之;
  2. 文档站(docs.deno.com):运行 deno-docs 仓库的 update_versions.yml 工作流自动开 PR,合并之;
  3. Docker 镜像:运行 deno_docker 的 version_bump 工作流,合并其 PR;然后创建不带 v 前缀$VERSION tag(注意与 CLI 的 v$VERSION tag 不同),触发镜像发布 CI,确认成功;
  4. PyPI:先跑 deno_pypi 的 version-bump.yml 并合并 PR,再跑 release.yml 触发发布 CI,确认新版本可安装;
  5. MDN:若本次版本新增或启用了 JavaScript / Web API,检查 MDN browser-compat-data 是否已同步;
  6. deno upgrade 横幅(可选):制作纯文本的 banner.txt,上传到 dl.deno.landrelease/v$VERSION/ 目录下,用户执行 deno upgrade 时即可看到(用于提示破坏性变更或必须执行的新命令)。

收尾:解锁仓库与回滚预案

完成后在 #cli 频道发布“解锁”公告(release_doc_template.md 第 144–156 行给出的模板文案,含 “denoland/deno is now unlocked / You can land PRs now / Deno v$VERSION has been released”)。

如果发布中途出错,清单给出两条回滚路径:

  1. dl.deno.land/release-latest.txt 改回上一个版本(该文件决定“latest”指向哪个版本);
  2. Revert dotcom 仓库的自动 PR,防止 setup-deno 这个 CI 安装器把未发布的版本当作默认版本拉取。

预发布(alpha / beta / rc)与正式版的差异

预发布走的是同一套管线,但模板换成 prerelease_doc_template.md,关键差异有:

  • version_bump 的类型选择是 alpha / beta / rc,手动兜底命令为 ./tools/release/01_bump_crate_versions.ts --alpha(或 --beta / --rc);
  • 没有 cargo_publish 阶段,取而代之的是 create_prerelease_tag 工作流(仓库内对应 create_prerelease_tag 生成脚本create_prerelease_tag.generated.yml):直接创建并推送 tag,触发 CI 构建产物;
  • 产物数量指标不同:GitHub draft 上 28 个 assetsdl.deno.land30 个 zip
  • release 必须以 pre-release 形式发布;
  • 没有官网 / 文档 / PyPI / MDN / upgrade 横幅等步骤,只更新 Docker 镜像,最后同样发“解锁”公告。

关键版本来源与自检点汇总

事实 来源(仓库内路径)
当前 CLI 版本 cli/Cargo.tomlversion 字段(00_start_release.ts 第 87–100 行 从这里读取)
运行时/库版本落点 cli/lib/version.txt(由 01_bump_crate_versions.ts 第 62 行 写入)
二进制版本正确性 cargo run -p deno -- -v 输出比对(01_bump_crate_versions.ts 第 214–222 行
发布说明内容 Releases.md 最新版本条目 → target/release/release-notes.md05_create_release_notes.ts
正式版产物验收 46 个 GitHub assets + 48 个 zip(release_doc_template.md 第 83–87 行
预发布产物验收 28 个 GitHub assets + 30 个 zip(prerelease_doc_template.md 第 75–79 行
回滚开关 dl.deno.land/release-latest.txt + dotcom 自动 PR 的 revert(release_doc_template.md 第 158–164 行

需要强调的是适用前提:以上流程针对的是官方 denoland/deno 仓库的维护者发布操作,依赖仓库 secrets(如 Gist PAT)、GitHub Actions 权限、crates.io 发布权限以及 dl.deno.land 存储桶访问权;普通使用者只需等待 release 发布后通过官方渠道升级即可。若需要本地演练模板渲染,可使用 00_start_release.ts--dry-run 查看将写入 Gist 的完整清单内容。

登录后查看全文
热门项目推荐
相关项目推荐