Deno 版本发布全流程解析:从 release_doc_template 清单到仓库内发布自动化脚本
2026/9/7 7:44:24 网站建设 项目流程

Deno 版本发布全流程解析:从 release_doc_template 清单到仓库内发布自动化脚本

【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno

Deno 的每一次正式发布都不是靠人工“手动敲命令”完成的,而是围绕一份可执行的发布清单(release checklist)展开:冻结分支、提升版本号、发布 crates.io 依赖、触发 GitHub 发布草稿,再到更新官网、文档站、Docker 镜像与 PyPI 包。本篇以仓库中的发布清单模板 tools/release/release_doc_template.md 为主体,结合同目录下的自动化脚本与.github/workflows中的工作流定义,拆解这份清单背后“每一步实际发生了什么”,帮助读者理解 Deno 从代码冻结到二进制分发、再到紧急回滚的完整工程链路。

发布清单是如何生成的:从模板到 Gist

清单模板并不是孤立存在的一份文档。Deno 的发布入口是start_release工作流,其触发逻辑记录在 tools/cut_a_release.md 中:选择main分支与发布类型(patch / minor / major),运行工作流,等待 "Create Gist URL" 步骤输出一个 Gist 链接,然后按 Gist 中的清单操作。

生成这份 Gist 的脚本是 tools/release/00_start_release.ts,它做三件事:

  1. 读取当前版本:解析 cli/Cargo.toml 顶层的version字段得到当前 CLI 版本;
  2. 计算下一个版本:根据传入的--patch/--minor/--major(或--alpha/--beta/--rc)参数按 SemVer 规则推导下一个版本号;
  3. 填充模板并创建 Gist:读取 tools/release/release_doc_template.md(预发布则使用 tools/release/prerelease_doc_template.md),替换其中的占位符后创建私有 Gist。

模板中的占位符与替换规则在源码中一一对应:

模板占位符替换来源
$VERSION计算出的下一个完整版本号(如2.30.0
$BRANCH_NAMEv$major.$minor(如v2.30),即发布期间冻结的分支
$MINOR_VERSION前两位版本号(major.minor
$PAST_VERSION当前cli/Cargo.toml中的旧版本

也就是说,开发者在执行清单时看到的v$VERSION$BRANCH_NAME等变量都已被脚本渲染为真实值。

Pre-flight:冻结分支与准备工作

清单的第一阶段(Pre-flight)要求在整个发布期间$BRANCH_NAME分支保持冻结、不接受任何新提交,并完成以下准备:

  • 准备好以下仓库的 fork 与本地克隆:denoland/deno(CLI 主仓库)、denoland/dotcom(官网)、denoland/deno_docker(Docker 镜像)、denoland/deno-docs(文档站);
  • 检查基准测试面板,确认近期没有性能回归;
  • 在公司内部#cli频道发出锁定公告(:lock:+@here+ “DO NOT LAND ANY PRs”),并附 Gist 清单链接。

“冻结分支”这条规则的工程含义是:发布过程中的版本号 bump、Releases.md更新等提交都直接落在该分支上,如果此时有 PR 合入,会污染发布提交的历史并干扰后续forward_v$VERSION回合(cherry-pick 回 main)的冲突判断。

Phase 1:Bumping versions —— 版本号提升到底改了什么

清单第一阶段要求运行version_bump工作流(选main分支,选patchminor),等待其自动开出 PR,审查后合并,并特别强调:不要手动创建 release tag,tag 会在后续流程中自动生成。

该工作流的生成本体是 .github/workflows/version_bump.ts(生成产物为version_bump.generated.yml),其调用的核心脚本是 tools/release/01_bump_crate_versions.ts。从源码看,一次版本号提升实际包含 6 个动作:

1. 提升 CLI 版本与核心 crate

脚本通过 tools/release/deno_workspace.ts 中的DenoWorkspace类定位三类关键 crate:

  • deno(CLI 本体,即 cli/Cargo.toml);
  • denort(runtime crate,对应 runtime/ 目录);
  • deno_lib(lib crate,对应 cli/lib/ 目录)。

执行--patch/--minor/--major参数时,脚本先对 CLI crate 调用increment,然后denort强制设置为与 CLI 相同的版本,并将新版本写入deno_libversion.txt(即 cli/lib/version.txt),保证三者版本严格对齐。

2. 提升所有依赖 crate 的 minor 版本

getCliDependencyCrates()会枚举 CLI 的仓库内下游依赖(并排除test_servertest_macrotest_util三个纯测试 crate),对每个 crate 执行 minor 提升。

有一个值得注意的例外:renamedCrates集合中包含deno_v8。因为根 Cargo.toml 中它以v8 = { package = "deno_v8", ... }这种“依赖键名 ≠ crate 名”的方式声明,而发布自动化是靠“行首 crate 名”匹配依赖条目的,匹配不到就会报错。因此脚本用incrementRenamedCrateMinor()专门处理:用正则分别改写根Cargo.toml中的package = "deno_v8"依赖条目版本与其自身 manifest 的版本号(对应 libs/deno_v8/)。

3. 更新 lockfile 并验证二进制版本

cargoUpdate("--workspace")刷新 Cargo.lock;随后assertDenoBinaryVersion()会实际执行cargo run -p deno -- -v,把输出与期望版本比对,不一致则直接Deno.exit(1)——这是对“版本号真的编译进了二进制”的硬校验。

4. 提升 CI 缓存版本

bumpCiCacheVersion()会把 .github/workflows/ci.ts 中的const cacheVersion = N;自增 1,然后重跑该生成脚本刷新ci.generated.yml。其目的是让每个新版本强制使用全新的 cargo 构建缓存目录,避免跨版本缓存污染。

5. 自动更新 Releases.md

updateReleasesMd()会获取上次 tag 到当前的git log(自动区分 minor 与 patch 发布的历史区间),写入仓库根的 Releases.md;若本次是 minor/major 版本提升(releaseHasBlogPost()判定 major 或 minor 位发生变化),还会在条目前加上官方博客链接文案。若自动更新失败,脚本会打印手动兜底命令:git log --oneline VERSION_FROM..VERSION_TO

6. 失败兜底路径

清单中<details>里的 Failure Steps 说明了工作流失败时的人工路径:checkout 发布分支,手动运行./tools/release/01_bump_crate_versions.ts,确认 crate 版本与Releases.md正确后自行开 PR 继续流程——这与脚本本身是“可本地重跑”的设计(文件头 shebang 为deno run -A --lock=tools/deno.lock.json)相吻合。

PR 的创建由 tools/release/02_create_pr.ts 完成:它创建release_X_Y_Z分支(点号替换为下划线)、提交并推送,然后以草稿 PR形式打开,PR 描述中内置了“crate 版本是否正确、Releases.md是否相关且移除了 revert”两个审查项,并附上拉取分支的git fetch upstream ... && git checkout -b ...命令。

Phase 2:Publish —— crates.io 发布与 GitHub 发布草稿

清单第二阶段要求运行cargo_publish工作流(在 Phase 1 相同的分支上),其核心脚本是 tools/release/03_publish_crates.ts:

  • getCratesPublishOrder()对依赖 crate 做拓扑排序,按依赖顺序逐个cargo publish
  • 依赖 crate 使用--no-verify参数发布。源码注释解释了原因:这些 crate 单独构建时无法完成deno_v8引擎特性选择(引擎选择在denocrate 顶层的v8/quickjsfeatures 决定),cargo 独立 tarball 校验会触发compile_error!;而denocrate 本身仍走完整校验(await cliCrate.publish());
  • 整个流程包在try/finally中,结束时发出系统蜂鸣(\x07)提示操作者成功或失败。

发布完成后,清单要求验证两点(标记,代表硬性检查项):

  • GitHub 上v$VERSION发布草稿包含46 个 assets
  • 对象存储(dl.deno.land 对应的 R2 桶)中该版本目录下有48 个 zip 文件

tag 的创建实际上发生在后续的 tools/release/04_post_publish.ts:它拉取远端 tag 列表,若v$VERSION不存在则创建并推送——这正是 Phase 1 清单中“不要手动打 tag”的原因:tag 由 CI 在 crates 发布成功后自动创建,而 tag 又会触发第二轮 CI 生成 GitHub 发布草稿。

该脚本还实现了补丁版本回合:若当前分支不是main(即 patch release),会基于origin/main创建forward_v$VERSION分支,cherry-pick 发布提交;若产生冲突则提交带冲突的工作树并在 PR 描述中标注 “THIS PR HAS GIT CONFLICTS THAT MUST BE RESOLVED”,保证版本 bump 提交最终回到 main 分支,避免 main 上的下一个版本 bump 与历史版本不一致。

发布草稿的说明文本则由 tools/release/05_create_release_notes.ts 生成:它取 Releases.md 中最新一条版本的文本,写入target/release/release-notes.md供 GitHub 发布草稿使用。

下游生态更新:官网、文档站、Docker 与 PyPI

清单的后半部分覆盖 CLI 之外的分发渠道,每个渠道的机制略有不同:

deno.com 与 docs.deno.com

两者都是“跑一个工作流 → 自动开 PR → 人工审查合并”的三步走:官网的update_version.ymldenoland/dotcom仓库)与文档站的update_versions.ymldenoland/deno-docs仓库)。它们把站点展示的“最新版本号”指向新 tag,通常还会被setup-deno等 CI 动作消费。

deno_docker

运行deno_docker仓库的version_bump工作流,审查并合并其开的 PR,然后在镜像仓库上创建不带v前缀$VERSIONtag(注意与主仓库v$VERSION的命名差异),tag 触发镜像构建发布的 CI,验证其成功即可。

deno_pypi(pip 安装的 deno)

deno_pypi仓库需要跑两个工作流:先version-bump(开 PR,审查合并),再release工作流触发发布,最终验证新版本已出现在 PyPI 的deno项目页。

MDN browser-compat-data

如果本次发布新增或启用了 JavaScript / Web API,需要同步更新mdn/browser-compat-data。清单给出的保守策略是:拿不准就跳过(并联系对应维护者确认),体现了“清单允许在证据不足时选择安全路径”的设计。

deno upgrade 升级横幅

清单提供了一个可选的发布后公告机制:在deno upgrade执行时向用户打印一段纯文本提示。操作方式是创建一个banner.txt(内容必须为 plaintext),上传到对象存储中release/v$VERSION/banner.txt路径。适用场景是希望提醒用户“需要执行某个命令才能享受新功能”或“存在破坏性变更”的版本。

收尾与 Downgrade:回滚预案

清单的 “All done!” 阶段要求回到内部频道发出解锁公告(:unlock:+ “You can land PRs now” + 版本发布完成通知),标志$BRANCH_NAME分支解冻。

模板最后给出了明确的Downgrade 预案(In case something went wrong):

  1. 把 dl.deno.land 的release-latest.txt改回上一个稳定版本号——这是用户侧deno upgrade与自动安装脚本读取“最新稳定版”的依据,改回它即可让流量回到旧版本;
  2. revert 官网(dotcom 仓库)中更新版本号的 PR,防止setup-deno等 GitHub Action 拉取到问题版本。

这两步只动“版本指针”而不动已发布二进制,因此是整个流程中代价最低的止血手段。

发布自动化的目录地图

将模板与仓库脚本对照,tools/release/目录下的脚本按执行顺序可理解为一条完整流水线:

阶段脚本职责
启动00_start_release.ts计算下一版本,填充 release_doc_template.md / prerelease_doc_template.md,创建发布清单 Gist
版本 bump01_bump_crate_versions.ts提升deno/denort/deno_lib及依赖 crate 版本、更新 lockfile、CI 缓存版本、Releases.md,并编译验证
开 PR02_create_pr.ts创建release_X_Y_Z分支并打开草稿 PR
发布03_publish_crates.ts拓扑排序后逐个cargo publish
发布后04_post_publish.ts创建v$VERSIONtag;patch 版本时 cherry-pick 回合到 main
发布说明05_create_release_notes.ts从 Releases.md 生成 GitHub 发布草稿说明
公共库deno_workspace.ts、deps.ts封装仓库/crate 定位逻辑;deps.ts转发自jsr:@deno/rust-automation提供 Git、PR、SemVer 等基础能力

此外,promote_to_release.ts 与 promote_to_release_windows.ts 负责预发布(alpha/beta/rc)候选版本向正式发布的晋级,upload_version_file.ts 与 purge_cdn_cache.ts 则负责分发侧的版本文件上传与 CDN 缓存清理;.github/workflows/下的start_release.tsversion_bump.tscargo_publish.tspost_publish.tspromote_to_release.ts等脚本则是这些步骤对应的 CI 入口(各带一份.generated.yml产物)。

小结

这份清单模板表面上是“打勾式”的操作手册,实际上它把 Deno 发布流程中的顺序依赖(先 bump 后 publish,先 publish 后打 tag,先 tag 后发布草稿)、硬性校验点(46 个 assets、48 个 zip、cargo run -v版本比对)和失败/回滚路径(Failure Steps、Downgrade)都显式化了。结合 tools/release/ 中的脚本源码可以看到:模板中每一步人工动作背后都有一个可重跑的自动化脚本兜底,人工介入被收敛到“审查 PR、验证产物、发布草稿”这几个真正需要判断的环节——这也是一个大型运行时项目能在多版本、多分发渠道(二进制、crates.io、Docker、PyPI)下保持发布一致性的工程基础。

【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询