workerd 的 V8 版本升级全流程指南:从补丁 Rebase 到 Bazel 依赖同步
【免费下载链接】workerdThe JavaScript / Wasm runtime that powers Cloudflare Workers项目地址: https://gitcode.com/GitHub_Trending/wo/workerd
导读
workerd(Cloudflare Workers 的 JavaScript / Wasm 运行时)深度内嵌 Google V8 引擎,并在其上维护了一套用于支撑 Workers 平台特性的定制补丁。当 Chrome Beta 推进 V8 版本时,workerd 需要把整套补丁迁移到新版本之上,并同步更新 Bazel 构建配置中的版本号、完整性校验值与对齐依赖。本文以仓库中的官方升级文档 docs/v8-updates.md 为骨架,结合 ci/v8_update.py 等源码实现,完整讲解手动升级的 12 个步骤与半自动更新助手的使用方法。读完本文,你将掌握 workerd 的 V8 升级机制、补丁再生成与依赖对齐的底层原理,能够独立完成一次完整的 V8 版本升级。
为什么 workerd 要单独维护 V8
V8 是 workerd 的运行时核心,但 workerd 并不直接使用上游原版 V8。仓库通过 Bazel 模块 build/deps/v8.MODULE.bazel 拉取 V8 官方源码 tarball,并在其上叠加一组自定义补丁。该文件头部注释解释了关键约束:googlesource 生成的 tarball 不具备确定性,无法使用http_archive,因此 V8 本体通过 GitHub 官方镜像以 tarball 方式引入,而 zlib(Chromium fork)、icu(Chromium fork)、trace_event 等依赖仍必须走 git 协议。
该文件的核心配置块如下(当前仓库中的实际取值):
VERSION = "15.3.76.11":当前锁定使用的 V8 版本标签;INTEGRITY = "sha256-zhD6KsBTOQwTXzj1uuZ5gVbYJENz9+R38eAGOF0OCk0=":tarball 的 SHA-256 校验值(Bazel 偏好格式);PATCHES = [...]:依次列出 patches/v8 下按数字前缀命名的补丁文件(当前为0001至0041共 41 个);http_archive:从https://github.com/v8/v8/archive/refs/tags/<VERSION>.tar.gz下载,strip_prefix为v8-<VERSION>,并按PATCHES列表逐个应用-p1补丁;git_repository:拉取llvm-libc(Chromium googlesource 镜像)供 V8 使用;local_path_override:将perfetto_cfg、google_benchmark指向仓库内的build/perfetto、build/google-benchmark,并通过@workerd-v8间接引用 V8,以便内部非开源仓库可以覆写 V8 的构建方式。
这些补丁并非装饰品,而是 workerd 平台能力的一部分。例如 0001-Allow-manually-setting-ValueDeserializer-format-vers.patch 为 V8 的ValueDeserializer增加了SetWireFormatVersion()方法,允许 workerd 读取历史上缺失版本头的序列化数据时显式指定线格式版本;0006-Implement-Promise-Context-Tagging.patch、0013-Implement-cross-request-context-promise-resolve-hand.patch等则与 Workers 的 Promise 上下文语义相关。正因为补丁与业务强耦合,升级 V8 不是简单改一个版本号,而是一次完整的补丁迁移工程。
升级前的准备工作
升级流程的第一步是确定目标版本。官方文档建议访问 Chromium Dash(chromiumdash.appspot.com),查看 Chrome Beta 渠道使用的 V8 版本——workerd 以 Chrome Beta 的 V8 为跟踪目标,这与 ci/v8_nightly.py 中通过chromiumdash.appspot.com/fetch_releases?platform=Win32&channel=beta探测最新 Beta V8 版本号的逻辑一致(latest_beta_v8()函数)。
随后需要在本机安装 Google 的depot_tools(提供fetch、gclient等工具)。安装方式见 depot_tools 官方教程的 Setting up 一节,这里不再赘述。
获取 V8 源码时,官方文档特别强调:
mkdir v8 cd v8 fetch v8并提醒务必把 V8 副本放在 workerd 仓库之外,否则仓库内的 V8 源码树可能干扰 Bazel 的依赖解析与//...通配构建。接下来把本地 V8 同步到 workerd 当前锁定的旧版本(记为<old_version>):
cd <path_to_v8>/v8 git checkout <old_version> gclient sync其中<old_version>就是从build/deps/v8.MODULE.bazel中读出的VERSION值。gclient sync会把 V8 的 DEPS 文件声明的所有子依赖(icu、dragonbox、fast_float 等)一并检出到与<old_version>匹配的版本。
手动升级 V8 的 12 步流程
这是官方文档的核心章节,以下逐步展开。
第 1~3 步:确定目标版本并准备 V8 副本
即上一节所述:确认 Chrome Beta 的最新 V8 版本号、安装depot_tools、在仓库外fetch v8。
第 4 步:将本地 V8 同步到 workerd 当前版本
从build/deps/v8.MODULE.bazel读取当前VERSION作为<old_version>,然后git checkout <old_version>并gclient sync,确保工作区与 workerd 的基线完全一致。
第 5 步:创建补丁分支并应用 workerd 补丁
git checkout -b workerd-patches git am --keep-non-patch <path_to_workerd>/patches/v8/*--keep-non-patch是与第 7 步git format-patch的-k参数配套的:它保留上游 V8 提交自带的主题前缀(如[wasm]),这样第 7 步重新生成的补丁能沿用既有的文件名前缀与编号,便于PATCHES列表稳定维护。若git am中途失败,可先用git am --abort回退并排查具体补丁。
第 6 步:将 workerd 补丁 Rebase 到新版本
假设目标版本为<new_version>,执行:
git rebase --onto <new_version> <old_version>由于 workerd 补丁基于旧版 V8 编写,升级后上下文往往发生偏移,rebase 过程中通常需要少量手工修补。官方文档建议在此阶段就完成带补丁的本地 V8 构建与测试(参见 V8 官方 Testing 文档),尽早暴露补丁与新版本的兼容问题,而不是等回到 workerd 仓库再排查。这一步也是整个流程中风险最高的环节——补丁的语义(尤其是涉及 Promise 上下文、序列化格式、内存分配等平台行为的改动)必须由人来确认在新版本中依然成立。
第 7 步:重新生成补丁
rebase 成功后,用git format-patch从<new_version>基线导出补丁:
git format-patch --full-index --no-signature --no-stat --zero-commit <new_version>各参数含义:
--full-index:diff 中输出完整的 blob 哈希,保证补丁在任何克隆中都稳定可应用;-k:保留原始提交的 subject 前缀(对应第 5 步的--keep-non-patch);--no-signature:去掉git format-patch默认追加的签名信息;--no-stat:省略 diffstat 统计,缩小补丁体积;--zero-commit:将补丁头部的 commit 哈希置零,避免暴露提交哈希差异。
第 8 步:替换仓库中的补丁
删除 patches/v8 下原有的补丁文件,把第 7 步在 V8 目录生成的补丁复制进来。ci/v8_update.py的finish_update()在自动流程中正是先清空旧补丁,再用shutil.copy2复制新生成的补丁,以保留时间戳等元数据。
第 9 步:更新v8.MODULE.bazel中的 VERSION / PATCHES / INTEGRITY
在 build/deps/v8.MODULE.bazel 中:
- 将
VERSION改为<new_version>; - 根据新增/删除的补丁刷新
PATCHES列表; - 更新
INTEGRITY。官方文档给出两种获取方式:直接用新版本编译 workerd,从 Bazel 报告的完整性不匹配错误中读出 Bazel 偏好格式的校验值;
或先下载
https://github.com/v8/v8/archive/refs/tags/<new_version>.tar.gz,再执行:openssl dgst -sha256 -binary <tarball_filename> | openssl base64 -A得到的 base64 值前加上
sha256-前缀即为INTEGRITY。ci/v8_update.py中的_tarball_integrity()函数用 Python 的hashlib.sha256加base64实现了完全相同的计算逻辑("sha256-" + base64(...)),两者结果一致,可相互校验。
第 10 步:更新 V8 的依赖并重新生成deps.MODULE.bazel
V8 的部分第三方依赖以独立 Bazel 依赖的形式由 workerd 管理,记录在 build/deps/deps.jsonc 中。升级 V8 后需要把这些依赖的提交同步到新版本 V8 的DEPS文件所引用的提交(可在本地 V8 副本的<path_to_v8>/DEPS中找到)。
文档明确指出当前需要跟踪的依赖包括perfetto、com_googlesource_chromium_icu和simdutf,并给出了一个重要提醒:V8 是通过 Chromium 间接依赖perfetto与simdutf的,无法直接从 V8 的 DEPS 推断出 V8 对应的是 GitHub 上的哪个版本,因此对这两个依赖采取"直接升到 GitHub 最新版"的安全策略即可。
从 build/deps/deps.jsonc 的注释与 ci/v8_update.py 的V8_DEPENDENCIES映射可以确认更完整的依赖清单及其对齐方式:
| 依赖名(deps.jsonc 中的 name) | V8 内路径 | 是否与 V8 提交严格对齐 |
|---|---|---|
com_googlesource_chromium_icu | third_party/icu | 是(freeze_commit对齐) |
dragonbox | third_party/dragonbox/src | 是(freeze_commit对齐) |
fast_float | third_party/fast_float/src | 是(freeze_commit对齐) |
fp16 | third_party/fp16/src | 是(freeze_commit对齐) |
highway | third_party/highway/src | 是(freeze_commit对齐) |
perfetto | third_party/perfetto | 否(github_release,取最新版) |
simdutf | third_party/simdutf | 否(github_release,取最新版) |
其中 icu、dragonbox、fast_float、fp16、highway 在deps.jsonc中都带注释"我们想避免与 V8 产生版本偏差,所以使用完全一致的版本",并以freeze_commit锁定提交哈希。更新依赖后,运行依赖更新脚本重新生成deps.MODULE.bazel:
python3 build/deps/update-deps.py(脚本也支持指定单个依赖名进行定向更新,见下文助手章节。)
第 11 步:运行 workerd 测试套件
bazel test //...官方文档要求在提交前确认整个仓库的测试在升级后的 V8 下全部通过。若测试失败,需要回到补丁层排查,必要时调整补丁内容。
第 12 步:提交并推送评审
将build/deps/v8.MODULE.bazel、build/deps/deps.jsonc、patches/v8/*.patch以及可能涉及的源码改动一并提交,推送到远程分支进行代码评审。
半自动更新助手:ci/v8_update.py
手动 12 步中有大量机械性工作(创建分支、应用补丁、rebase、重新生成补丁、更新版本号与校验值),ci/v8_update.py 将这些步骤自动化,官方文档也特别提示该脚本"尚未被充分测试,若执行失败请回退到手动流程,并仔细审查其改动"。
检查是否有可用更新:check-update
python3 ci/v8_update.py check-update脚本通过latest_beta_v8()访问 Chromium Dash 获取 Chrome Beta 的 V8 版本,与 build/deps/v8.MODULE.bazel 中当前的VERSION比较:
- 有新版本时:打印
<current_version> -> <new_version>并以退出码1结束; - 已是最新时:不打印任何内容并以退出码
0结束; - 加
--machine-readable参数:仅打印新版本号(便于在脚本或 CI 中取用)。
执行更新:update <new_version>
python3 ci/v8_update.py update <new_version>从源码看,prepare_update()的机械流程与手动步骤一一对应:
- 删除并重建临时目录
/tmp/workerd-v8/v8(常量CHECKOUT); - 以
--depth=1浅克隆拉取old与target两个标签(该脚本默认远程为https://github.com/v8/v8.git); - 基于旧版本检出
workerd-patches分支; - 用
git am --keep-non-patch --3way --committer-date-is-author-date依次应用patches/v8/*.patch; - 执行
git rebase --onto <target> <old> workerd-patches。
若 rebase 顺利结束,脚本会立即调用finish_update()完成补丁再生成与配置更新;若 rebase 因冲突失败,脚本打印提示并返回失败,此时需要在临时检出中人工处理:
cd /tmp/workerd-v8/v8 git status # 解决冲突并把文件加入暂存区(git add) git rebase --continue重复此过程直到 rebase 完成。官方文档特别强调:补丁冲突必须人工审查,以确保 workerd 特有的行为(序列化、Promise 上下文、内存管理等)在新版本中仍然被保留,而不是机械地解决行级冲突。
收尾:finish <new_version>
python3 ci/v8_update.py finish <new_version>finish_update()在临时检出中执行git format-patch --full-index -k --no-signature --no-stat --zero-commit --output-directory ... <new_version>重新生成补丁,然后清空patches/v8并用copy2复制新补丁,最后更新:
- build/deps/v8.MODULE.bazel 中的
VERSION、INTEGRITY(由_tarball_integrity()实时下载 tarball 计算)与PATCHES列表; - build/deps/deps.jsonc 中 icu、dragonbox、fast_float、fp16、highway 的
freeze_commit(由_update_aligned_dependencies()从 V8 新版本的DEPS文件正则提取提交哈希写入)。
官方文档明确警告:rebase 未完成前不要运行finish,否则会用半成品状态重新生成补丁并覆写锁定配置。
手动补完依赖更新与验证
助手不会运行update-deps.py。文档要求手工对比新旧两个版本 V8 的DEPS文件,检查dragonbox、fast_float、fp16、highway、perfetto、simdutf的提交是否有变化,对每个发生变化的依赖定向执行依赖更新脚本:
python3 build/deps/update-deps.py <dependency>这一步会重新生成对应的deps.MODULE.bazel内容(build/deps/update-deps.py 是仓库所有 Bazel 依赖的自动生成器,输出文件头部带有 "AUTOGENERATED DO NOT EDIT" 警告)。注意脚本目录下update-deps.py中TARGET_FILTER正是从命令行参数读取依赖名,实现"只更新某一个依赖"的定向能力。
随后完成手动流程的第 11、12 步:运行bazel test //...验证全部测试,审查生成的改动后提交推送。
与 CI 夜间任务的关系
ci/v8_update.py并非孤立存在。仓库中的 ci/v8_nightly.py 将其作为底层库复用(导入prepare_update、finish_update、changed_dependencies、latest_beta_v8等),实现"探测 Chrome Beta 新 V8 → 自动 rebase 补丁 → 更新对齐依赖 → 构建//src/workerd/server:workerd→ 运行bazel test //..."的夜间流水线;ci/v8_nightly_shared.py 则提供共享的 git/subprocess/日志基础设施,并在补丁冲突或构建失败时调用 AI 助手(opencode)在有限轮次内尝试修复。理解这套 CI 可以帮你判断:当你手动执行ci/v8_update.py时,走的正是与 CI 相同的代码路径,只是没有自动化的 AI 修复环节。
常见问题与注意事项
- V8 副本必须放在仓库外:仓库内的 V8 源码树会干扰 Bazel 的依赖图与
//...构建,务必遵循文档建议。 --keep-non-patch与-k必须成对使用:前者在git am时保留 subject 前缀,后者在git format-patch时保留同一前缀,二者配合才能维持补丁文件名的稳定性。- rebase 冲突是常态而非例外:文档明确"通常会有少量补丁编辑工作",且
ci/v8_nightly.py中补丁冲突默认需要 AI 辅助或人工审查,切勿盲目--skip跳过错失补丁语义。 - INTEGRITY 必须与 tarball 严格对应:可交叉验证——
ci/v8_update.py的_tarball_integrity()与文档给出的openssl dgst -sha256 -binary ... | openssl base64 -A计算同一文件会得到相同结果。 - perfetto / simdutf 不要尝试从 V8 DEPS 反推 GitHub 版本:二者经由 Chromium 间接依赖,直接升级到最新版即可;icu、dragonbox、fast_float、fp16、highway 则必须与 V8 的
DEPS提交严格对齐,避免版本偏差导致编译或行为不一致。 - 助手脚本并非金标准:官方文档承认
ci/v8_update.py未经过充分测试,失败时回退到手动 12 步流程,并以bazel test //...的最终结果为准。
通过上述流程,workerd 得以始终跟进 Chrome Beta 的 V8 版本,同时保证 41 个平台定制补丁持续生效。无论你是想手动完成一次升级,还是希望借助ci/v8_update.py半自动推进,本文对应的文档 docs/v8-updates.md 与源码 ci/v8_update.py 都是可以直接照做的权威参考。
【免费下载链接】workerdThe JavaScript / Wasm runtime that powers Cloudflare Workers项目地址: https://gitcode.com/GitHub_Trending/wo/workerd
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考