您已经发布了 Electron 构建,发布页面已经上线,第一份支持票已经到达了,咖啡机还没完成。一个用户说应用程序找不到更新。另一个用户下载了它,但无法安装。第三个用户仍在运行一个旧的二进制文件,具有破坏性验证流程,而您的日志显示几乎没有有用的信息。
这就是 Electron 应用程序自动更新的不舒服现实。 Electron 应用程序自动更新. 更新器 API 只是其中一个组件。生产发布还依赖于平台签名、传输策略、清单、托管、生命周期事件、可观察性、滚动控制和回滚路径。将其中任何一个视为可选项,并且一个常规补丁就可能成为一夜之间的事件。
目录
- 上午 2 点的更新事件:引发了本指南
- 选择合适的 Electron 更新器路径
- 在主进程中实现自动更新流程
- CI/CD 配置自动签名发布和清单
- 发布策略、渠道和回滚策略
- 将自动更新视为安全控制
- 生产更新的运行手册和检查表
那场 2 点的更新事件
发布通过 CI 检查并看起来正常。周五晚上,一名开发人员推送了一个未签名的 Electron 构建,发布作业上传了足够的资产,使发布看起来完整。应用程序在测试中启动,但没有人执行从已安装的生产构建中更新的路径。
2:08 a.m.时,PagerDuty警报通知了负责人。新认证流程对部分fleet失败,更新的用户无法完成登录。其他用户仍在使用之前的版本,因为更新器无法验证或安装artifact。一些客户发布失败,而其他fleet则运行不同的版本,没有明确的说明。
调查遵循五个检查:
- 检查发布源 二进制文件存在,但预期元数据未清晰地确定哪些客户应接收它。清单是发布管道与安装客户之间的契约,而不是可选上传细节。
- 检查签名 发布未正确签名,因此受影响平台上的验证失败。签名必须阻止缺失或无效的发布。
- 比较客户日志 更新错误从未达到中央遥测。应用程序吞噬了事件并继续运行,留下了团队无法可靠的证据。
- 检查发布控制 没有内部通道或阶段性群组。所有合格客户都使用相同的源,因此失败没有包含点。
- 寻找回滚 团队没有测试过的程序来重新发布之前的版本或将客户导向破坏的发布。
Electron的官方文档明确了平台的限制。 Linux没有内置的自动更新支持,macOS更新请求必须满足 App Transport Security要求。文档还指出签名是可靠的macOS更新和发布验证的必要条件。 Electron autoUpdater文档 定义了API约束,而发布系统必须强制执行周围的操作控制。
后续教训: 无法解释发生了什么的更新器是一次远程安装尝试,缺乏遥测。
成本超过了工程时间。客户对桌面客户端失去了信心,支持团队必须解释不一致的行为,团队在下一个工作日重建了应该在事件发生之前就存在的发布过程。
应对自动更新的方式是 操作系统。签名是一个发布门户,清单定义客户端契约,发布渠道限制暴露,回滚是一个经过测试的路径而不是紧急创造。
选择合适的Electron更新路径
2点钟,错误的更新选择变成一个运营问题。原生二进制更新必须处理签名,清单,安装器,回滚。仅限渲染的JavaScript或CSS更改遵循不同的路径。托管约束也很重要:一个小GitHub托管项目不需要同样的发布控制和企业分发服务。
对于使用electron-builder进行签名artifact发布的项目 electron-updater 通常是实用的默认值。它的生态系统涵盖了发布目标,发布清单,artifact下载,和下一次启动的安装。它支持多种托管模型,但您的团队仍然负责签名,feed可用性,渠道策略,发布控制,和监控。 Capgo的Electron更新集成 是评估混合交付模型的Web层捆绑包和原生发布时的相关内容。
update-electron-app 适合那些想要在GitHub发布周围的小型集成。它在启动时检查,然后在定期间隔内检查,保持设置简单但留下了更少的空间来选择渠道,分阶段流量,和自定义回滚规则。该包对于小型发布流程是合理的,假设GitHub发布和可用性与您的运营要求相符。
| 选项 | 托管控制 | 签名支持 | 频道 & 阶段性发布 | 维护负担 |
|---|---|---|---|---|
| electron-updater | S3, GitHub, 通用 HTTPS 和其他发布目标 | 集成到打包的发布签名 | 强大的基础,通常在 feed 周围有自定义策略 | 中等 |
| update-electron-app | 主要是简单的 GitHub 发布流程 | 使用 Electron 的底层签名模型 | 除非您添加周围的服务,否则有限 | 低 |
| Squirrel.Windows 或 Squirrel.Mac | 平台化分发流程 | 依赖于平台签名要求 | 可能,但通常需要额外的发布基础设施 | 中等于遗留应用 |
| 自定义服务 | 对清单、授权、队列和 feeds 有完全控制 | 您负责验证设计和密钥处理 | 最大灵活性 | 高 |
| Capgo 实时更新 | 为 web 层包管理的托管交付 | 使用其更新器和交付模型 | 针对受众和基于频道的交付 | 将原生二进制更新与操作模式分开 |
当发布授权、租户目标、审计记录或受管部署规则的实施成本被证明是必要的时,一个自定义服务,如Hazel、Nuts或内部订阅,适合。这种权衡是持续的拥有权。您的团队必须定义清单语义、保护签名密钥、保持客户兼容性并测试下载失败、拒绝发布和回滚行为。
Live更新可以不重建原生壳船而只发送渲染器更改。它们并不会取代二进制更新,当Electron、原生模块、权限或安装程序行为发生变化时。 除非您需要自定义发布逻辑,否则使用electron-updater。 如果需要自定义逻辑,请围绕已建立的清单和工件约定而不是重新创建下载和差异更新行为。可靠的路径是您的团队可以在压力下观察、阶段和反转的路径。
在主进程中实现自动更新流程
主进程应该拥有更新检查和安装。渲染器可以显示状态,但它不应该决定是否信任可执行更新或何时退出应用程序。
首先配置发布目标
一个最小的electron-builder配置可能如下所示:
{
"build": {
"appId": "com.example.desktop",
"publish": [
{
"provider": "s3",
"bucket": "example-electron-releases",
"channel": "stable"
}
],
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
}
保持 beta 和稳定 feed 分开。一个频道是一个发布策略,而不是 UI 中的标签。每个频道应该解析到正确的签名 artifact 和 manifest。
定时检查并暴露生命周期事件
在启动期间 checkForUpdates() 仅在启动期间调用是生产中的常见错误。用户可以将应用程序保持打开数天,所以主进程需要一个受控的间隔和一个重试策略,尊重离线操作。
const { app, BrowserWindow, ipcMain } = require('electron');
const { autoUpdater } = require('electron-updater');
let mainWindow;
let isQuitting = false;
let retryDelay = 60 * 1000;
function sendUpdateStatus(status, payload = {}) {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('update-status', { status, ...payload });
}
}
function scheduleUpdateCheck() {
setTimeout(async () => {
try {
await autoUpdater.checkForUpdates();
retryDelay = 60 * 1000;
} catch (error) {
sendUpdateStatus('error', { message: error.message });
retryDelay = Math.min(retryDelay * 2, 30 * 60 * 1000);
}
scheduleUpdateCheck();
}, retryDelay);
}
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
webPreferences: {
preload: require('path').join(__dirname, 'preload.js')
}
});
autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.on('checking-for-update', () => {
sendUpdateStatus('checking');
});
autoUpdater.on('update-available', info => {
sendUpdateStatus('available', { version: info.version });
});
autoUpdater.on('download-progress', progress => {
sendUpdateStatus('progress', { percent: progress.percent });
});
autoUpdater.on('update-downloaded', info => {
sendUpdateStatus('downloaded', { version: info.version });
});
autoUpdater.on('error', error => {
sendUpdateStatus('error', { message: error.message });
});
autoUpdater.checkForUpdates().catch(error => {
sendUpdateStatus('error', { message: error.message });
});
scheduleUpdateCheck();
});
ipcMain.handle('install-update', () => {
isQuitting = true;
autoUpdater.quitAndInstall(false, true);
});
app.on('before-quit', event => {
if (!isQuitting) {
return;
}
});
更新事件行为的确切行为取决于平台和打包设置,所以从安装的 artifact 中测试,而不是从开发模式中测试。Electron 的文档还提到 Windows 上的启动时间问题,包括 Squirrel 首次运行的案例。不要在应用程序完成所需的平台特定初始化之前触发更新检查。
保持渲染器知情而不阻塞工作
预载桥应该暴露一个狭窄的 API:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('updates', {
onStatus(callback) {
ipcRenderer.on('update-status', (_event, status) => callback(status));
},
install() {
return ipcRenderer.invoke('install-update');
}
});
渲染器侧的进度条可以保持故意简单:
window.updates.onStatus(status => {
const progress = document.querySelector('#update-progress');
const message = document.querySelector('#update-message');
if (status.status === 'progress') {
progress.hidden = false;
progress.value = status.percent;
message.textContent = `Downloading update, ${Math.round(status.percent)}%`;
}
if (status.status === 'downloaded') {
message.textContent = `Version ${status.version} is ready to install`;
}
if (status.status === 'error') {
message.textContent = 'The update could not be downloaded. We will retry later.';
}
});
在生产中在用户同意后才将安装阻塞在后台,除非您的应用程序有强烈的理由立即重启。设置一个 isQuitting 前置 quitAndInstall(),因为正常的窗口关闭处理程序可能会阻止安装程序接管。

两个失败的测试都需要明确的测试。首先,一个正在运行的客户端必须在启动时不仅在启动时调用,而且在定时调度时调用。其次,事件必须到达日志和遥测。如果应用程序吞噬它而不报告,那么您的应用程序故障排除工作流程 checkForUpdates() 为已签名的发布和清单编程CI/CD error 发布管道是用户安装的源真实性。一个本地构建,即使在一个开发人员的机器上工作,也不能证明发布的二进制文件、清单、签名和通道都描述了相同的发布。 Electron-builder的发布模型期望发布元数据和更新目标一起旅行。对于许多配置,这意味着一个像 Windows和
macOS的
,以及平台特定的包和块映射文件。一个缺失的清单可以使一个完全有效的二进制文件对客户端不可见。
在CI中显式签名 latest.yml 一个简化的__CAPGO_KEEP_0__ Actions模式看起来像这样: latest-mac.yml 在CI/CD中为已签名的发布和清单编程
发布管道是用户安装的源真实性
一个简化的GitHub Actions 模式看起来像这样:
name: release
on:
push:
tags:
- "v*"
jobs:
build:
strategy:
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run test
- run: npm run build
- name: Build and publish
shell: bash
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: npx electron-builder --publish always
使用平台特定的密钥,并将签名材料放在仓库外。签名失败应停止作业,而不是生成手动上传的未签名版本。
| 变量 | 目的 |
|---|---|
CSC_LINK |
macOS证书或证书引用 |
CSC_KEY_PASSWORD |
macOS证书或证书引用 |
WIN_CSC_LINK |
macOS签名材料的密码 |
AWS_ACCESS_KEY_ID |
Windows证书或证书引用 |
AWS_SECRET_ACCESS_KEY |
具有狭窄访问权限的发布凭证 |
与发布凭证配对的密钥
{
"build": {
"publish": {
"provider": "s3",
"bucket": "example-electron-releases",
"channel": "stable",
"publishAutoUpdate": true,
"updaterCacheDirName": "example-desktop-updater"
}
}
}
发布配置应一致地识别提供商和频道: 持续集成设置指南 持续集成设置指南 何时使用 Jenkins 和 Ansible 一起.
常见的命令意外暴露的发布是故意让人感到乏味的:
npx electron-builder --publish never
test -f dist/latest.yml
test -f dist/latest-mac.yml
find dist -name "*.blockmap" -print
这些检查并不能取代签名安装测试。它们可以捕捉到上传二进制文件而没有元数据的客户端需要发现它的操作错误。
发布、渠道和回滚策略
发布源应该表现得更像是一个部署目标而不是一个下载文件夹。保持 内部, beta, 和 最新 渠道分开,各个渠道都由自己的清单和签名的artifact集支持。推广应该在政策之间移动经过测试的发布,而不是覆盖正在下载的文件。
渠道分离也保护了生产环境免受意外测试构建的影响。更新器应该知道一个客户是否属于内部小组、beta测试群体还是稳定用户群之前就要评估发布源。
在广泛暴露之前使用小组
一个自定义的 manifest字段可以表达分阶段的发布:
version: 4.8.0
path: Example-Setup-4.8.0.exe
sha512: signed-artifact-hash
rolloutPercentage: 10
主进程可以为每个用户分配一个稳定的缓冲区,然后将该缓冲区与 rolloutPercentage稳定分配很重要。用户在每次检查时都在有资格和无资格之间切换,会导致行为不可预测,支持报告难以解释。
根据您的要求,翻译如下: 在发布生存观察窗口后才扩大该群体。具体的时间窗口应该反映您的使用习惯,但决策应该基于信号,而不是单纯的日历。跟踪更新检查结果、下载完成、启动健康、崩溃、渲染器异常和身份验证成功。
| 信号 | 动作 | 原因 |
|---|---|---|
| 错误的Feed或签名超过团队允许的限制 | 自动发布 | 客户端可能无法验证或发现发布 |
| 更新后启动检查失败 | 反向订阅 | 二进制文件可能安装成功但在启动时失败 |
| 渲染器异常在推广后增加 | 保持当前分组 | 原生安装器可能正常工作,而新应用程序code却不正常 |
| 信号保持在发布预算内 | 扩大分组 | 证据支持更广泛的暴露 |
不要将回滚混淆为删除一个 artifact。已缓存的客户端元数据可能存在,某些客户端可能已经运行了错误版本。回滚计划需要一个之前签名的发布、一个feed更改和一个可以恢复的客户端行为。
运营规则: 回滚必须由on-call工程师执行,而不需要重建应用程序以解决事件。
在实践中,runbook应该将之前的发布清单推广回受影响的频道、invalidate分段标记并确认新检查指向安全版本。如果问题出在渲染器code而不是原生shell,一个针对性的web层回滚可能更快。一个平台如 Capgo分段式发布 对独立的传输层来说,这可能是相关的,但它不应遮蔽原生二进制回滚和Web包回滚之间的界限。
将自动更新视为安全控制
Electron更新器下载可执行文件code,并且可以在用户交互最小的情况下安装它。这使得更新路径成为 安全边界,而不是仅仅是便利功能。Electron的官方文档描述了平台约束,如macOS ATS,安全覆盖已经记录了2022年的一种场景,在这种场景中,攻击者控制更新基础设施可以提供恶意包装,仍然通过code-签名检查通过,正如在 Electron Builder自动更新安全文档.
Code signing remains foundational, but it isn’t the whole trust model. Sign every release, verify the certificate and identity during CI, and maintain a documented key-rotation procedure. On macOS, combine signing with notarization and the hardened runtime appropriate to your application. On Windows, make certificate ownership, renewal, and build access auditable. Linux needs a distribution-specific strategy because Electron doesn’t provide a built-in universal updater there.
保护元数据和二进制文件一样小心
一个签名的二进制文件仍然可能与错误的发布相关联,如果元数据通道被破坏或配置不当。考虑在应用程序中嵌入公共密钥并验证清单签名,强制最低允许版本,并在未经授权的恢复路径明确允许它们之前拒绝意外降级。
此更新源也应具备生产控制:
- 限制发布访问: 仅向CI授予发布发布资产所需的权限。
- 保护签名密钥: 将证书和私钥存储在管理的机密存储中,而不是存储在仓库文件中。
- 固定依赖: 在CI中锁定Electron、electron-builder和传递依赖项。
- 审查艺术品: 扫描生成的包并将其与意图的提交和版本进行比较。
- 要求安全传输: 遵循ATS和严格HTTPS要求更新请求。
- 监控验证失败: 重复的签名或清单失败应被视为安全事件,而不是普通网络噪音。
Electron 的维护工具生态系统继续添加打包和更新器覆盖,但维护并不消除威胁建模的需求。实践目标是确保攻击者能够破坏一个存储桶、CDN 或构建步骤仍然无法让客户接受未经授权的发布。该 签名验证指南 提供了设计额外验证层的有用上下文。

生产更新运行本书和检查表
只有当另一个工程师在压力下可以操作它时,发布才准备就绪。保持检查表与部署任务和事件通道接近。
发布前门槛
- 版本身份: 确认包版本、发布标签、提交 SHA 和更改日志一致。
- 签名: 验证每个平台的艺术品都已签名,并且不良或等效验证已完成。
- 清单合同: 确认
latest.yml,latest-mac.yml确认上传的艺术品的哈希值、路径和块映射与匹配。 - 安全频道: 在发布到发布频道之前,先将发布推送到内部或beta feed。
- 遥测: 确认更新检查、下载进度、安装完成、启动健康和错误都正常到达。
金丝雀和全面发布
- 同群控制: 先从内部或beta用户群中开始,数量尽可能小。
- 健康预算: 若发布失败、下载更新失败、渲染器异常或认证失败超过团队允许的阈值时,暂停发布。
- 发布审批: 要求在将发布推送到稳定分发之前,必须获得明确的通过或否决决定。
- 客户影响: 在广泛发布之前,准备好支持信息,而不是在第一次事件之后。
事件响应
如果新二进制文件无法启动、进程创建失败或更新下载停止,请立即停止发布。恢复之前的签名清单,invalidating 阶段标记,并验证新客户是否恢复到之前的版本。然后通过遥测确认舰队恢复后再进行沟通。
具体的回滚命令取决于您的提供商,但序列应该始终被记录: 切换到上一个版本的渠道清单,invalidating 阶段标记,推送一个强制更新标记,如果恢复需要的话,验证降级路径使用实时遥测一个没有经过测试的回滚只是希望。

Capgo 提供了一个 Electron 更新器,用于交付签名的 web 层更改、目标渠道、发布控制和更新可观察性,而无需为每个渲染器更改重建原生壳。如果您想将原生二进制发布与受控的 JavaScript 和 CSS 交付分开,请访问 Capgo 并评估它与您的现有 Electron 发布管道一起使用。