自动更新与版本切换
区分更新路线、产物、元数据、下载、安装和失败恢复。
本页整合 Electron 归档分稿中的机制、接口和接线。归档使用 Electron 44 系列作为其版本讨论背景;本文接口依据官方文档及 breaking changes 核查,实际采用时仍需固定项目版本。包版本、维护状态和原稿实测均是对应日期的历史信息。本次未启动 Electron、构建安装包、签名、公证或验证更新。代码块按各自标注区分片段与组合示例。
6.1 两条技术路线
Electron 生态存在两条自动更新路线:内置 autoUpdater(main process 模块,基于 Squirrel 系框架)与 electron-updater(electron-builder 生态的独立 npm 包)。官方文档对内置模块的定位是「目前仅支持 macOS 和 Windows」,Linux 明确建议交给发行版包管理器;而 electron-updater 三平台可用,是 2026 年社区事实标准。
| 维度 | 内置 autoUpdater(Squirrel) | electron-updater(electron-builder) |
|---|---|---|
| 支持平台 | macOS、Windows | macOS、Windows、Linux(AppImage/DEB/RPM/Pacman) |
| Windows 更新目标 | Squirrel.Windows、MSIX | NSIS(Squirrel.Windows 已不支持,官方建议迁移 NSIS) |
| feed 元数据 | macOS 自定义 JSON / Windows RELEASES 文件 | latest.yml / latest-mac.yml / latest-linux.yml |
| 差量更新 | Squirrel.Windows 的 nupkg delta | blockmap(NSIS 安装器、macOS zip、AppImage) |
| staged rollouts | 无原生支持,需服务端分流 | stagingPercentage(客户端判定) |
| 下载进度事件 | 无 | download-progress |
| 签名校验 | macOS 签名校验(Squirrel.Mac 要求) | macOS + Windows Authenticode + sha512 校验(v27 起支持 Ed25519 manifest 签名) |
| 配置方式 | 代码里 setFeedURL | 构建期生成 app-update.yml 内嵌于 resources |
| 适配工具链 | Electron Forge / electron-winstaller | electron-builder |
6.2 全链路原理与流程
两种方案的链路一致:启动或定时触发 checkForUpdates() → 拉取 feed 元数据 → 版本比较 → 下载(优先差量)→ 完整性/签名校验 → 安装(下次启动生效或 quitAndInstall())。
6.2.1 内置 autoUpdater(Squirrel 系)
- 检查:macOS 上 Squirrel.Mac 请求 feed URL;有更新时服务器返回
200+ JSON(url字段必需,指向更新 zip;name/notes/pub_date可选),无更新返回204 No Content。Windows Squirrel.Windows 则在 feed 的/RELEASES子路径返回固定格式文件(SHA1、.nupkg文件名、大小),且即使无更新也要返回响应——版本比较由客户端完成。setFeedURL还支持serverType(json/default,仅 macOS)与file://协议(可绕过鉴权困难的服务器)。 - 下载与安装:
update-available后自动下载;update-downloaded触发后即使不调用quitAndInstall(),更新也会在下次启动时生效;quitAndInstall()会先关闭所有窗口再退出并安装(此时应监听before-quit-for-update做清理,而非before-quit)。 - Windows 双机制:Electron 自动探测——传统安装器走 Squirrel.Windows;以 MSIX 打包(
process.windowsStore)时走 MSIX 机制,支持直链.msix或 JSON feed,并可用allowAnyVersion允许降级。 - 托管服务:官方免费服务 update.electronjs.org(条件:公开 GitHub 仓库 + 发布到 GitHub Releases + macOS 代码签名),配套
update-electron-app一行接入,启动时检查且每 10 分钟轮询;也支持静态对象存储(serverless)模式——macOS 读releases.json(currentRelease+releases[],Forge 的 maker-zip 配macUpdateManifestBaseUrl),Windows 读RELEASES(maker-squirrel 配remoteReleases)。
6.2.2 electron-updater(electron-builder)
流程与内置版相同,但元数据改为 latest.yml 系列:构建时生成并上传(除 generic provider 需手动上传外),客户端读取内嵌的 app-update.yml(通常不要调用 setFeedURL)。关键选项及默认值(以官方源码为准):autoDownload = true、autoInstallOnAppQuit = true(v26;v27 起由 autoInstallEvent 取代:"onQuit"(默认)/"onNextLaunch"/"manual")、allowDowngrade = false(仅在 channel 不同、即 semver pre-release 段不同才生效)、allowPrerelease(仅 GitHub provider,默认跟随当前版本是否含 pre-release 段)。
channel 机制:channel 决定客户端读取哪份元数据文件——latest(默认)对应 latest.yml,beta 对应 latest-beta.yml(macOS/Linux 为 latest-mac.yml/latest-linux.yml 的 beta 变体)。detectUpdateChannel 可按当前版本号的 pre-release 段自动推断 channel(如 0.12.1-alpha.1 归入 alpha,GitHub provider 除外);generateUpdatesFilesForAllChannels 则让一次发布同时产出多个 channel 的元数据文件,用于把 beta 用户逐步「扶正」到 stable。
平台实现差异值得注意:
- macOS(MacUpdater):更新包必须是 zip 工件(找不到则抛
ERR_UPDATER_ZIP_FILE_NOT_FOUND);下载完成后通过本地代理服务器 + Basic auth 把文件交给 Squirrel.Mac,Squirrel 原生 staged——「退出后下次启动时应用更新」,quitAndInstall在 macOS 实际是委托原生 updater。已下载的 zip 会缓存为update.zip供下次差量。 - Windows(NsisUpdater):
quitAndInstall通过 spawn 安装器完成,参数为--updated,静默时加/S,isForceRunAfter对应--force-run,自定义目录/D=必须是最后一个参数;遇到EACCES时经elevate.exe重试提权。签名校验见 6.3。 - Linux(AppImageUpdater):仅在
APPIMAGE环境变量存在时启用(Snap 环境直接禁用);下载新 AppImage → 校验通过后才chmod 0o755→ 删除旧文件并mv替换运行中的 AppImage → 可选重启(或以APPIMAGE_EXIT_AFTER_INSTALL退出)。findFile显式排除rpm/deb/pacman工件——deb/rpm 没有应用内自更新,需依赖 apt/dnf 或商店渠道;AppImage「仅通过 manifest checksum 验证」。
6.2.3 macOS:签名、公证与 Sparkle 替代
- 签名是硬性要求:官方文档原话「Your application must be signed for automatic updates on macOS. This is a requirement of Squirrel.Mac.」;electron-builder 文档同样强调「macOS application must be signed in order for auto updating to work」。
- 公证(notarization):官方更新文档未将公证列为更新链路要求(只提签名);社区教程普遍要求「signed and notarized」(公证是 macOS 上通过 Gatekeeper 分发的通行要求,建议开启)。更换 Developer ID 证书后老版本客户端能否继续更新的具体行为,官方文档未说明,待核实。
- Sparkle 替代:原生 Mac 应用的事实标准更新框架 Sparkle(Sparkle 2,macOS 10.13+)提供 EdDSA 签名、delta 更新与沙盒支持。Electron 官方无 Sparkle 桥,社区项目(如
Innei/electron-sparkle-updater的 N-API 桥 + appcast 工具链)声称可绕开「Squirrel.Mac 需要付费 Developer ID、Sparkle 可用 ad-hoc 签名」的痛点——此说法来自第三方 README,待核实,采用前需自行验证。
6.2.4 Windows:Squirrel.Windows vs NSIS
Squirrel.Windows 已事实上冻结:最后 release 2.0.1(2020-09-27),仓库 README 公开招募维护者,423 个 open issue;electron-builder 已不再支持它作为更新目标,官方迁移建议是 NSIS。Squirrel.Windows 遗留的两个经典问题(官方文档记载):安装后首启带 --squirrel-firstrun 参数且 Squirrel 持有文件锁导致检查失败——应跳过该次检查或延迟约 10 秒(electron#7155);以及必须调用 app.setAppUserModelId(格式如 com.squirrel.slack.Slack)否则任务栏固定异常。NSIS + electron-updater 是 2026 年 Windows 默认选型,天然支持 channel(latest/latest-beta.yml 等)与 blockmap 差量。
6.3 差量更新(blockmap)与下载校验
blockmap 原理:electron-builder 构建时为可更新工件(NSIS 安装器、macOS zip、AppImage)生成 .blockmap(文件分块的哈希索引)。更新时客户端取新旧两份 blockmap,只下载发生变化的分块;旧 blockmap 无缓存、或 manifest 指定 blockMapUrl 而无 override 时,以及任何异常情况下都会自动回退全量下载;disableDifferentialDownload 可关闭,previousBlockmapBaseUrlOverride 可指定旧 blockmap 地址(GitHub 场景常用)。electron-builder 26.17.0 还新增了 NSIS store-asar 差量模式。Squirrel.Windows 的差量则走自己的 nupkg delta,与 blockmap 无关。
校验链(层层递进):
latest.yml中的sha512对下载文件做哈希校验,失败抛ERR_CHECKSUM_MISMATCH;- Windows 上用 PowerShell
Get-AuthenticodeSignature校验安装器 Authenticode 签名,并与app-update.yml中publisherName做 Subject CN 匹配,失败抛ERR_UPDATER_INVALID_SIGNATURE;v26 中publisherName缺失时跳过校验仅告警(fail-open),v28 起将改为失败(fail-closed);缓存的安装器在下次启动安装前会重新验签; - v27(alpha)引入 Ed25519 签名的更新 manifest(CI 经
ELECTRON_BUILDER_UPDATE_SIGN_KEY配置,支持双签实现 key rotation),客户端配置公钥后 fail-closed;同时 SHA-256-only 元数据已弃用,v28 将强制 SHA-512。
安全视角:Doyensec 2026-02 研究指出主流方案未覆盖四类威胁:降级/回滚攻击(MITM 或被入侵的服务器下发旧版本)、完整性攻击(篡改二进制/元数据)、TOCTOU 竞态(校验与安装之间被替换本地文件)、未测试版本攻击(alpha/beta 与生产共用签名 key/通道);该机构 2020 年就披露过 electron-updater 签名校验绕过导致 RCE 的案例。其参考实现 SafeUpdater 采用 Ed25519 签名 + SHA-512 + 「签名内容绑定版本号」来封死降级路径——这预示 electron-builder v27 的 Ed25519 manifest 正是对同类威胁的响应。
6.4 更新策略:静默 vs 征询、灰度、回滚与强制最低版本
- 静默 vs 征询:
autoDownload=true+ 下次启动生效是最省打扰的组合;征询式则把autoDownload=false,在 UI 中展示版本说明后手动downloadUpdate()/quitAndInstall()。v27 的autoInstallEvent: "onNextLaunch"会在下次启动时对照最新 update info 重新校验缓存安装器再安装,规避了系统会话中途关机导致的安装损坏(历史 issue #7807)。 - 灰度(staged rollouts):在
latest.yml/latest-mac.yml中手写stagingPercentage: 10即向 10% 用户放量,可随时间调大。客户端判定算法(源码实证):首次生成 UUID v5 随机 ID 持久化于 userData 下.updaterId,UUID.parse(id).readUInt32BE(12) / 0xffffffff得到 [0,1) 值,小于stagingPercentage/100则命中;字段缺失视为全量;由于.updaterId持久不变而判定值恒定,阈值从 10 逐步调到 50、100 的过程就是同一批用户被单调放量,不会来回抖动;检查请求会携带x-user-staging-id头,服务端可据此自行分流。大型应用实践:VS Code 自 1.107(2025-11)起将 Insiders 构建在 4 小时窗口、Stable 在 24 小时窗口内渐进放量,用户可用 Check for Updates 立即获取。 - 回滚:官方文档明确——没有降级通道。要撤回已灰度的坏版本,必须发布一个更高版本号;重新发布同号版本只会让已中招的用户停在坏版本上。
allowDowngrade只解决「beta 用户回流 stable」这类跨 channel 场景。 - 强制最低版本:内置 autoUpdater 与 electron-updater 均没有内建 min-version/kill-switch API。通行的两种做法:一是服务端按客户端版本分流(自定义 feed 服务天然能做,内置 Squirrel feed 本就由服务器决定返回 200 还是 204);二是客户端启动时额外拉取一份远程配置(最低版本/维护开关)强制升级或禁用。
- 检查频率:update.electronjs.org 的默认是启动 + 每 10 分钟;官方教程的 60 秒示例偏激进,生产建议数小时级并加随机抖动,同时注意官方警告「调用
checkForUpdates()两次会把更新下载两遍」。
6.5 Feed 服务选型对比
| 方案 | 类型 | 活跃度(2026-10 经 GitHub API 核实) | 要点 |
|---|---|---|---|
| update.electronjs.org | 官方托管 | Electron 团队维护 | 免费免运维;要求公开 GitHub 仓库 + Releases,macOS 必须签名 |
| GitHub Releases provider | 托管 | electron-builder 维护 | 私有仓库需 GH_TOKEN,限额 5000 请求/小时、每次检查约 3 个请求,官方称仅适合「特殊场景」 |
| S3/OSS/Spaces/R2 + generic | 静态托管 | 自管 | 纯静态 + CDN,成本最低;latest.yml 必须配 no-cache/短缓存,否则永远查不到新版本 |
| electron-release-server | 自建 | 最后提交 2024-04,半停滞 | 带管理面板与鉴权,不依赖 GitHub |
| Nucleus(Atlassian) | 自建 | 最后提交 2023-12,release 停在 2018(v0.8.3) | 多应用、多 channel 的元数据服务 |
| Nuts(GitBook) | 自建 | 最后提交 2023-10 | GitHub Releases 后端 + 磁盘缓存,支持私有仓库 |
| faynoSync 等新一代项目 | 自建 | 活跃(2026-10 仍在提交) | 支持 Electron/Tauri 多生态的轻量更新服务,社区规模小 |
结论:官方教程仍列出的 Hazel/Nuts/electron-release-server/Nucleus 多数已失修,2026 年的主流是「GitHub Releases 或对象存储 + generic provider」与大厂自研更新服务(如 VS Code 的自有 update service)两端;选自建方案前务必核对仓库活跃度。
6.6 常见坑与反模式
- 重复调用
checkForUpdates()——官方明示会重复下载;务必做去重/节流。 - Squirrel.Windows 首启文件锁(
--squirrel-firstrun)与漏设app.setAppUserModelId。 - macOS 未签名就上线更新——Squirrel.Mac 直接拒绝;「本地能跑、上线全挂」多源于此。
- Windows 无签名 + v26 默认 fail-open:
publisherName缺失时校验被跳过,等于裸奔;发布签名安装器并在配置中写明publisherName。 latest.yml被 CDN 长缓存——灰度与紧急止血全部失效;设置Cache-Control: no-cache。- 想「回滚」到旧版本——做不到,只能发更高版本号;把「发更高版本」写进发布 runbook。
- 给 electron-updater 调
setFeedURL——会覆盖构建期生成的app-update.yml,除鉴权头等特殊需求外不要碰。 - 开发环境测更新:用
dev-app-update.yml+forceDevUpdateConfig=true;Linux 下设APPIMAGE指向产物;推荐本地 MinIO 模拟 generic 服务。 - 失败排查缺日志:设置
autoUpdater.logger(推荐 electron-log),关注error事件;常见错误码:ERR_CHECKSUM_MISMATCH(文件损坏或元数据与上传物不一致)、ERR_UPDATER_INVALID_SIGNATURE(签名/证书主体不匹配)、ERR_UPDATER_ZIP_FILE_NOT_FOUND(macOS 缺 zip 工件)、差量失败会自动回退全量(日志可见 fallback)。electron-updater 没有内建失败自动重试,依赖下一轮检查。 - HTTP feed、把私有 GitHub token 打进客户端、alpha/beta 与生产共用签名通道——均为 Doyensec 点名的攻击面。
6.7 关键代码与配置示例
electron-updater(征询式 + 灰度友好):
const { autoUpdater } = require('electron-updater')
const log = require('electron-log')
function setupUpdater() {
autoUpdater.logger = log
autoUpdater.autoDownload = true // 默认 true
autoUpdater.autoInstallOnAppQuit = true // v26;v27 起用 autoInstallEvent
autoUpdater.on('download-progress', (p) => log.info(`进度 ${p.percent}%`))
autoUpdater.on('update-downloaded', async ({ version }) => {
const { response } = await dialog.showMessageBox({
type: 'info', buttons: ['重启', '稍后'],
message: `新版本 ${version} 已就绪,重启后生效`
})
if (response === 0) autoUpdater.quitAndInstall()
})
autoUpdater.on('error', (e) => log.error(e))
autoUpdater.checkForUpdates() // 启动即查
setInterval(() => autoUpdater.checkForUpdates(), 4 * 60 * 60 * 1000)
}内置 autoUpdater(官方教程模式,自建 feed 按平台分流):
const { app, autoUpdater } = require('electron')
if (app.isPackaged) {
const url = `https://updates.example.com/update/${process.platform}/${app.getVersion()}`
autoUpdater.setFeedURL({ url })
setInterval(() => autoUpdater.checkForUpdates(), 60 * 60 * 1000)
}latest.yml 灰度配置(构建后手动追加,逐步调大):
version: 1.1.0
path: MyApp Setup 1.1.0.exe
sha512: Dj51I0q8aPQ3ioaz9LMqGYujAYRbDNblAQbodDRXAMxm...
stagingPercentage: 10 # 先放 10%,观察后调至 50 → 100electron-builder 发布配置(generic + 对象存储):
publish:
provider: generic
url: https://cdn.example.com/myapp/ # 存放 latest.yml / exe / blockmap
channel: latest # beta 渠道 → latest-beta.yml参考来源
- Electron 官方文档 · autoUpdater API:https://www.electronjs.org/docs/latest/api/auto-updater
- Electron 官方教程 · Updating Applications:https://electronjs.org/docs/latest/tutorial/updates
- electron-builder · Auto Update(v26 稳定版):https://www.electron.build/v26/docs/features/auto-update
- electron-builder · Auto Update(next/v27):https://www.electron.build/docs/features/auto-update
- electron-builder · v27 Breaking Changes:https://www.electron.build/docs/migration/v27-breaking-changes
- electron-updater 源码 AppUpdater.ts(staged rollout/差量/校验):https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/AppUpdater.ts
- electron-updater 源码 MacUpdater.ts / NsisUpdater.ts / AppImageUpdater.ts:https://github.com/electron-userland/electron-builder/tree/master/packages/electron-updater/src
- electron-builder Releases(26.17.0 / 27.0.0-alpha.9):https://github.com/electron-userland/electron-builder/releases
- Electron 版本发布页(44.5.1,2026-09-30):https://releases.electronjs.org/
- VS Code 1.107 Release Notes · Builds rollout(Insiders 4h / Stable 24h 灰度):https://code.visualstudio.com/updates/v1_107
- Doyensec · Building a Secure Electron Auto-Updater(2026-02-16):https://blog.doyensec.com/2026/02/16/electron-safe-updater.html
- Squirrel.Windows(维护状态):https://github.com/Squirrel/Squirrel.Windows
- Squirrel.Mac:https://github.com/Squirrel/Squirrel.Mac
- electron-release-server:https://github.com/ArekSredzki/electron-release-server
- Nucleus:https://github.com/atlassian/nucleus
- Nuts:https://github.com/GitbookIO/nuts
- Sparkle 官网:https://sparkle-project.org
- Innei/electron-sparkle-updater(Sparkle 的 Electron N-API 桥,第三方):https://github.com/Innei/electron-sparkle-updater
最后更新于