关于 electron-node-upgrade
这项 skill 指导 Electron 维护者升级 Node.js 依赖,涵盖补丁同步、构建和测试。它按阶段说明冲突处理及验证要求。
主要功能
- 01三阶段升级工作流
- 02补丁冲突处理指引
- 03构建与启动验证
- 04Node.js 测试套件指引
- 05补丁导出与提交规则
适用场景
- 01在 Electron 中升级 Node.js 版本
- 02解决 Node.js 补丁冲突
- 03适配 Node.js API 变更
- 04升级后检查构建和测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ electron/ electron --skill electron-node-upgrade安装后在 Claude Code 中输入/electron-node-upgrade确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/patch-analysis.mdphase-one-commit-guidelines.mdphase-three-commit-guidelines.mdphase-two-commit-guidelines.md详细介绍
Electron Node.js 升级:第一阶段
摘要
反复运行 e sync --3,并在出现补丁冲突时修复,直到命令成功。然后导出补丁,并以原子方式提交更改。
成功标准
满足以下条件时,第一阶段即告完成:
e sync --3以代码 0 退出(没有补丁失败)- 按照提交指南提交所有更改
在满足这些条件之前,不要停止。
重要 除非百分之百确定某个补丁已不再需要,否则不要删除或跳过它。对于大版本升级,修补已弃用 V8 API 的补丁或回移上游更改的补丁通常可以删除,因为新版 Node.js 可能已包含这些更改,但删除前请务必确认。复杂冲突或难以解决的问题,应在穷尽其他所有选项后再提交给用户。不要仅仅因为无法解决就删除补丁。
重要 切勿使用 git am --skip,然后通过创建新提交手动重建补丁。这样会破坏原补丁的作者信息、提交消息以及在补丁序列中的位置。如果 git am --continue 报告“No changes”,请调查原因——这些更改可能已被之前冲突解决时的三方合并吸收。应将这种情况提交给用户处理,而不是跳过并重建补丁。
背景
roller/node/main 分支由自动化创建,用于更新 DEPS 中 Electron 的 Node.js 依赖版本。尚未进行任何工作来处理新旧版本之间的破坏性更改。
Node.js 版本更新有两种类型:
- 升级(补丁版/次版本):由
electron-roller[bot]自动执行,提交标题为chore: bump node to v{version}。简单的补丁索引更新由patchup[bot]自动处理。这类更新通常可以顺利应用,但有时需要手动修复补丁。 - 大版本升级(例如 v22 → v24):手动进行的大型 PR,提交标题为
chore: upgrade Node.js to v{X}.{Y}.{Z}。这类升级通常需要删除过时补丁、调整许多其他补丁,并更新package.json中的@types/node。
关键目录:
- 当前目录:Electron 仓库(始终在此处运行
e命令) ../third_party/electron_node:Node.js 仓库(补丁应用于此处)patches/node/:Node.js 补丁文件docs/development/patches.md:补丁系统文档
预检
在每次升级会话开始时运行以下检查:
- 清除 rerere 缓存(如果已启用):在 Electron 仓库和
../third_party/electron_node仓库中都运行git rerere clear。之前尝试时记录的旧解决方案可能会悄悄应用错误的合并结果。 - 确保已安装 pre-commit 钩子:检查
.git/hooks/pre-commit是否存在。如果不存在,运行yarn husky进行安装。该钩子会运行lint-staged,为 C++ 文件处理 clang-format。 - 查找其他分支上相同的升级:获取
roller/node/main,以及任何针对相同 Node.js 版本的同级roller/node/{N}-x-y分支。如果这些分支已包含修复,请原样复用(补丁名称、内容、提交标题、Ref:行),不要重新推导。请参阅references/phase-one-commit-guidelines.md中的“Reuse Fixes From Sibling Roller Branches”。
工作流
- 运行
e sync --3(--3标志启用三方合并,始终必需) - 如果成功 → 跳至第 5 步
- 如果补丁失败:
- 根据错误输出确定目标仓库和补丁
- 分析失败原因(请参阅 references/patch-analysis.md)
- 在
../third_party/electron_node工作目录中修复冲突 - 在
../third_party/electron_node中运行git am --continue - 重复以上步骤,直到该仓库的所有补丁都应用成功
- 重要:
git am --continue成功后,必须运行e patches node导出修复 - 返回第 1 步
e sync --3成功后,运行e patches all- **现在阅读
references/phase-one-commit-guidelines.md,**然后严格按照其中的说明提交更改。
命令参考
| 命令 | 用途 |
|---|---|
e sync --3 | 克隆依赖项并通过三方合并应用补丁 |
git am --continue | 解决冲突后继续(在 node 仓库中运行) |
e patches node | 将 node 仓库中的提交导出为补丁文件 |
e patches all | 从所有目标导出全部补丁 |
e patches node --commit-updates | 导出补丁并自动提交简单更改 |
e patches --list-targets | 列出目标和配置路径 |
补丁系统心智模型
patches/node/*.patch → [e sync --3] → ../third_party/electron_node commits
← [e patches] ←
何时编辑补丁
| 情况 | 操作 |
|---|---|
正在处理 git am 冲突 | 在 node 仓库中修复,然后运行 git am --continue |
| 在冲突之外修改补丁 | 直接编辑 .patch 文件 |
| 创建新补丁(少见,应避免) | 在 node 仓库中提交,然后运行 e patches node |
99% 的情况下都应修复现有补丁,而不是创建新补丁。
补丁修复规则
- 保留作者信息:在 TODO 注释中保留原始作者(取自补丁的
From:字段) - 绝不更改 TODO 负责人:
TODO(name)必须保留原始名称 - 更新描述:如果上游更改了 API 或宏,请更新补丁提交说明以反映当前状态
- 绝不跳过并重新创建补丁:如果
git am --continue提示“No changes — did you forget to use 'git add'?”,不要运行git am --skip并创建替代提交。该补丁的更改已通过先前的三方合并冲突解决纳入代码。这意味着较早的冲突解决纳入了过多更改。请向用户说明情况并寻求指导——正确的修复方式可能需要更仔细地重新处理先前的冲突解决,以确保每个补丁的更改彼此分离。
Electron Node.js 升级:第二阶段
概述
反复运行 e build -k 999 -- --quiet,出现构建问题时逐一修复,直到构建成功。然后运行 e start --version,验证 Electron 能否启动,并以原子方式提交更改。
第一阶段完成后立即运行第二阶段。
成功标准
满足以下条件即表示第二阶段完成:
e build -k 999 -- --quiet以代码 0 退出(没有构建失败)- 已运行
e start --version,检查 Electron 能否启动 - 已按照提交指南提交所有更改
在满足这些条件之前不要停止。不要删除代码或功能,也绝不要为了走捷径而注释掉代码。要让所有现有代码、逻辑和意图都正常运行。
背景
自动化创建了 roller/node/main 分支,用于更新 DEPS 中 Electron 的 Node.js 依赖版本。没有进行任何工作来处理新旧版本之间的破坏性变更。Node.js API(尤其是内部 V8 集成、OpenSSL/BoringSSL 兼容性以及构建系统文件)在不同版本之间经常发生变化。无论遇到何种情况,都必须更新 Electron 中的代码以适配 Node.js 的变更;应尽量避免修改 Node.js 代码来修复 Electron 的构建。
关键目录:
- 当前目录:Electron 仓库(始终在此处运行
e命令) ../third_party/electron_node:Node.js 仓库(不要修改其中的代码来修复构建问题,只需阅读以了解背景)
工作流
- 运行
e build -k 999 -- --quiet(--quiet标志会隐藏每个目标的状态行,只显示错误和最终结果) - 如果成功 → 跳到第 6 步
- 如果构建失败:
- 根据编译错误消息确定 "electron" 中的底层文件
- 分析失败原因
- 调整 Electron 代码以适配 Node.js 中的更改,修复构建问题
- 运行
e build -t {target_that_failed}.o,仅构建我们要修复的失败目标- 可从构建日志中的失败行识别 target_that_failed。例如,
FAILED: 2e506007-8d5d-4f38-bdd1-b5cd77999a77 "./obj/electron/shell/browser/api/electron_api_utility_process.o" CXX obj/electron/shell/browser/api/electron_api_utility_process.o中的目标名称是obj/electron/shell/browser/api/electron_api_utility_process.o
- 可从构建日志中的失败行识别 target_that_failed。例如,
- 现在阅读
references/phase-two-commit-guidelines.md,然后严格按照其中的说明提交更改。 - 返回第 1 步
- 重要:每次提交后(尤其是补丁提交后),立即在 electron 仓库中运行
git status- 查找其他仅修改了索引/区块标题的
.patch文件 - 这些是受你修复影响的依赖补丁。立即运行以下命令提交它们:
git commit -am "chore: update patches (trivial only)"
- 查找其他仅修改了索引/区块标题的
- 返回第 1 步
- 当
e build成功后,运行e start --version - 在
../third_party/electron_node中运行git status,检查 Node.js 仓库是否有待处理的更改- 如果有更改,请按照下文“A. 补丁修复”中的说明,将这些修改正确提交到相应的补丁文件中
命令参考
| 命令 | 用途 |
|---|---|
e build -k 999 -- --quiet | 构建 Electron,遇到错误时继续,并隐藏状态行 |
e build -t {target}.o | 仅构建一个指定目标以验证修复 |
e start --version | 验证 Electron 在构建成功后能否启动 |
两种构建修复类型
A. 补丁修复(针对经过补丁修改的 Node.js 文件)
当错误发生在 Electron 会打补丁的文件中时(使用 grep -l "filename" patches/node/*.patch 检查):
- 在 Node.js 源代码树中编辑文件(
../third_party/electron_node/...) - 创建一个修复提交,目标为原始补丁提交:
cd ../third_party/electron_node git add <modified-file> git commit --fixup=<original-patch-commit-hash> GIT_SEQUENCE_EDITOR=: git rebase --autosquash --autostash -i <commit>^ - 导出更新后的补丁:
e patches node - 按照
references/phase-one-commit-guidelines.md中的说明提交更新后的补丁文件。
查找要修复的原始补丁提交:git log --oneline | grep -i "keyword from patch name"
变基的基准提交是应用补丁前的 Node.js 提交。通过检查 refs/patches/upstream-head 引用来查找。
B. Electron 代码修复(针对 shell/、electron/ 等目录中的文件)
当错误发生在 Electron 自有源代码中时:
- 直接在 electron 仓库中编辑文件
- 直接提交(无需导出补丁)
Electron Node.js 升级:第三阶段
概述
通过 script/node-spec-runner.js 运行 Node.js 测试套件,修复失败的测试,并持续提交修复,直到所有测试通过。某些测试已永久禁用(列在 script/node-disabled-tests.json 中),不应运行。
第二阶段完成后,立即运行第三阶段。
成功标准
满足以下条件时,第三阶段即告完成:
node script/node-spec-runner.js --default运行后失败数为 0- 所有更改均已按照提交指南提交
在满足这些条件之前,不要停止。
上下文
Electron 使用自定义运行器(script/node-spec-runner.js)运行 Node.js 上游测试套件的一个子集。测试通过 ELECTRON_RUN_AS_NODE=true 使用 Electron 构建的二进制文件执行。由于 Electron 使用 BoringSSL(而不是 OpenSSL)和 Chromium 的 V8(可能与 Node.js 捆绑的 V8 不同),许多测试需要调整。
关键文件:
script/node-spec-runner.js— 测试运行器脚本script/node-disabled-tests.json— 永久禁用的测试(不要尝试修复这些测试)../third_party/electron_node/test/— Node.js 测试文件(补丁应用于此处)patches/node/fix_crypto_tests_to_run_with_bssl.patch— BoringSSL 加密测试适配patches/node/test_formally_mark_some_tests_as_flaky.patch— 不稳定测试列表
工作流
- 在 electron 仓库中运行
node script/node-spec-runner.js --default - 如果所有测试均通过 → 第三阶段完成
- 如果测试失败:
- 根据输出找出失败的测试文件
- 分析每个失败项(请参见下方的“常见失败模式”)
- 在
../third_party/electron_node/test/...中修复测试 - 重新运行特定的失败测试以验证:
node script/node-spec-runner.js {test-path}- 测试路径相对于 node 的
test/目录,例如test/parallel/test-crypto-key-objects-raw.js - 运行特定测试时不要使用
--default— 它会添加完整测试套件的标志 - 不要直接使用
ELECTRON_RUN_AS_NODE运行测试 — 运行器会处理环境设置(例如,临时将package.json从 ESM 切换为 CommonJS)
- 测试路径相对于 node 的
- 使用 fixup 工作流和提交指南提交修复
- 返回步骤 1
命令参考
| 命令 | 用途 |
|---|---|
node script/node-spec-runner.js --default | 运行完整的 Node.js 测试套件 |
node script/node-spec-runner.js test/parallel/test-foo.js | 运行单个测试 |
NODE_REGENERATE_SNAPSHOTS=1 node script/node-spec-runner.js test/test-runner/test-foo.mjs | 为基于快照的测试重新生成快照 |
常见失败模式
BoringSSL 不兼容问题
Electron 使用 Chromium 的 BoringSSL 构建 Node.js,而不是使用 Node.js 随附的 OpenSSL。从 v24.18.0 的版本更新起,这一设置通过 GN 参数启用,而不再依赖大量源码补丁:
# build/args/all.gn
# Build Node.js against Chromium's BoringSSL instead of node's bundled OpenSSL.
node_openssl_path = "//third_party/boringssl"
上游 Node.js 现已原生支持使用 BoringSSL 构建(参见 nodejs/node 提交 677ca7e76c9 和 61b20f60a39),因此 Electron 过去的大部分 BoringSSL 变通方案都已移除。在 v24.18.0 的版本更新期间:
fix_crypto_tests_to_run_with_bssl.patch从约 1100 行缩减为仅跳过两个测试文件。fix_handle_boringssl_and_openssl_incompatibilities.patch删除了约 176 行。剩余内容是在deps/ncrypto/ncrypto.cc、src/crypto/crypto_{common,context,hash}.cc、src/env.h、src/node_metadata.h和src/node_options.h中的 C++ 层兼容处理。
请逐步删除这些变通方案,而不要继续增加。只有在某项功能确实仍不受 BoringSSL 支持时,才添加保护条件;上游测试越来越多地在设置了 process.features.openssl_is_boringssl 时自动跳过,因此无需修改 Electron。
仍不受支持的功能的首选保护方式——在文件顶部跳过整个测试文件,并将其添加到 fix_crypto_tests_to_run_with_bssl.patch 中:
if (process.features.openssl_is_boringssl) {
common.skip('Skipping unsupported ML-DSA key tests');
}
对于无法通过行内代码妥善添加保护条件的测试,请改为将整个文件添加到 script/node-disabled-tests.json 中。
Chromium 的 BoringSSL 仍不支持的功能(截至 v24.18.0):
| 功能 | 当前处理方式 |
|---|---|
| RSA-PSS 密钥生成(弃用路径) | 在 test-crypto-keygen-deprecation 中通过文件级 common.skip 跳过 |
| ML-DSA 密钥 | 在 test-crypto-pqc-key-objects-ml-dsa 中通过文件级 common.skip 跳过 |
| ML-KEM 密钥 | 在 node-disabled-tests.json 中禁用(test-crypto-pqc-key-objects-ml-kem) |
| FIPS 模式 | 已禁用(test-crypto-fips) |
| 安全堆 | 已禁用(test-crypto-secure-heap) |
| 无状态 DH | 已禁用(test-crypto-dh-stateless) |
| 其他密钥生成 / WebCrypto 密钥生成 | 已禁用(test-crypto-keygen、test-webcrypto-keygen、wpt/test-webcrypto) |
行为差异(更新断言,而非跳过)——有些错误的形式发生了变化:
- 从不受支持的 OKP(Ed448)JWK 创建私钥时,现在会抛出
Invalid JWK OKP key(此前为Invalid JWK data);参见 nodejs/node#62499。Electron 的spec/node.spec.ts断言已放宽为/Invalid JWK/。
确实需要为测试添加保护条件时,如果可以直接探测功能,请优先使用精确的功能检查(例如 ciphers.includes('aes-128-ccm')),而不是笼统地检查 process.features.openssl_is_boringssl。
快照测试不匹配
有些测试使用 assert.strictEqual 将输出与已提交的 .snapshot 文件进行比较——这不是通配符比较。当 Chromium 的 V8 产生不同输出时(例如 V8 增强导致堆栈跟踪不同),必须重新生成快照:
NODE_REGENERATE_SNAPSHOTS=1 node script/node-spec-runner.js test/test-runner/test-foo.mjs
然后检查差异,确认更改符合预期,并将更新后的快照提交到相应补丁中。
V8 行为差异
Chromium 的 V8 可能比 Node.js 随附的 V8 更新。这可能导致:
- 堆栈跟踪格式不同(例如 thenable 异步堆栈帧)
- 错误消息不同
- Chromium V8 中提供了 stock Node.js V8 不具备的功能(反之亦然)
两种测试修复类型
A. 修复补丁(测试失败时最常见)
大多数测试修复都应添加到 patches/node/ 中的现有补丁。请使用 fixup 工作流:
- 编辑
../third_party/electron_node/test/...中的测试文件 - 找到相关的补丁提交:
git log --oneline | grep -i "keyword"- Crypto/BoringSSL 测试 →
fix crypto tests to run with bssl - 快照测试 → 对应的快照补丁(例如
test: accomodate V8 thenable) - 不稳定测试 →
test: formally mark some tests as flaky
- Crypto/BoringSSL 测试 →
- 创建 fixup 提交:
cd ../third_party/electron_node git add test/path/to/test.js git commit --fixup=<patch-commit-hash> GIT_SEQUENCE_EDITOR=: git rebase --autosquash --autostash -i <commit>^ - 导出:
e patches node - 现在阅读
references/phase-three-commit-guidelines.md,然后提交更新后的补丁文件。
B. 新建补丁(少见)
只有在修复内容不适用于任何现有补丁时,才创建新补丁。../third_party/electron_node 中的新补丁提交必须包含说明,解释补丁存在的原因以及何时可以移除——lint 检查会强制执行这一要求。
添加到禁用测试列表
只有在万不得已时,才将测试添加到 script/node-disabled-tests.json——也就是测试在架构层面与 Electron 根本不兼容时(而不只是可以通过添加保护来处理的 BoringSSL 差异)。此处禁用的测试会被完全跳过,永远不会运行。
关键:提交前阅读
- 在进行任何 Phase One 提交前:阅读
references/phase-one-commit-guidelines.md - 在进行任何 Phase Two 提交前:阅读
references/phase-two-commit-guidelines.md - 在进行任何 Phase Three 提交前:阅读
references/phase-three-commit-guidelines.md
高变更量补丁
在 Node.js 升级期间,这些补丁通常需要投入最多工作:
fix_handle_boringssl_and_openssl_incompatibilities.patch— Electron 使用 BoringSSL(通过 Chromium),而 Node.js 需要 OpenSSL。这个补丁过去规模庞大且复杂;Node.js 原生支持 BoringSSL 后(通过build/args/all.gn中的node_openssl_path = "//third_party/boringssl"启用),补丁已大幅缩减。它仍会为一些 C++ 层面的差异提供适配,而上游 OpenSSL/ncrypto API 的变更也可能导致其失效。fix_crypto_tests_to_run_with_bssl.patch— 配套补丁,用于适配 Node.js 的 Crypto 测试,使其能够在 BoringSSL 下运行。由于上游测试现在会在 BoringSSL 下自行跳过,该补丁也已大幅缩减——目前主要跳过少数仍不受支持的测试文件。请参阅上文“BoringSSL incompatibilities”部分。support_v8_sandboxed_pointers.patch— 当 V8 API 变更时,支持 V8 沙箱指针需要谨慎适配。build_add_gn_build_files.patch— GN 构建文件补丁规模较大,会涉及许多构建目标。上游构建系统的变更经常会与其冲突。
主版本升级
Node.js 主版本迁移(例如 v22 → v24)远比补丁版本升级复杂:
- 预计会删除补丁。 Electron 使用 Chromium 的 V8,其版本通常领先于 Node.js 内置的 V8。许多补丁用于弥合这一差距——为较新的 V8 API 提供适配,而 Chromium 的 V8 中有这些 API,Node.js 较旧的 V8 中却没有。当 Node.js 升级到较新的主版本时,其 V8 会追上 Chromium 的版本,因此可以删除这些桥接补丁。v22 → v24 升级中,就因此删除了 17 个补丁。
- 在
package.json中更新@types/node,使其与新的主版本匹配。 - 升级后出现回归是预料之中的。 即使升级已完成,针对边缘情况(ESM 路径处理、证书加载、平台特定问题)的后续修复 PR 也很常见。
技能目录结构
此技能在 references/ 中还包含其他参考文件:
- patch-analysis.md - 如何分析补丁失败
- phase-one-commit-guidelines.md - 第一阶段的提交格式
- phase-two-commit-guidelines.md - 第二阶段的提交格式
- phase-three-commit-guidelines.md - 第三阶段的提交格式
在工作流步骤中引用这些文件时,请阅读它们。