跳过主要内容

Electron应用程序自动更新:2026年实用指南

Ship Electron app auto update without the silent failures. Real code, signing tips, rollout guardrails, and rollback strategy for production teams.

Electron应用程序自动更新:2026年实用指南

您已经发布了 Electron 构建,发布页面已经上线,第一份支持票已经到达了咖啡机还没完成充电之前。 一位用户说应用程序找不到更新。 另一位用户下载了更新,但无法安装。 第三位用户仍在运行一个旧的二进制文件,具有破坏性认证流程,而您的日志显示几乎没有有用的信息。

这是自动更新 Electron 应用程序的不舒服现实。 Electron 应用程序自动更新. updater API 只是一个组件。生产发布还依赖于平台签名、传输策略、清单、托管、生命周期事件、可观察性、滚动控制和回滚路径。将其中任何一个视为可选项,一个常规补丁就可能成为一夜之间的事件。

目录

2点的更新事件引发了这份指南

发布通过CI通过了检查,看起来很普通。周五晚上,一名开发人员推送了一个未签名的Electron构建,发布作业上传了足够的资产,使发布看起来完整。应用程序在测试中启动,但没有人尝试了从已安装的生产构建中更新。

2点08分,PagerDuty警报了值班工程师。新认证流程在部分fleet中失败,更新过的用户无法完成登录。其他用户仍在使用之前的版本,因为更新器无法验证或安装artifact。一些客户发布了错误的版本,而其他fleet中的用户使用了不同的版本,没有明显的解释。

调查遵循五个检查:

  1. 检查发布源 二进制文件存在,但预期元数据未清晰地确定哪些客户端应该接收它。发布清单是发布管道与已安装客户端之间的契约,而不是可选的上传细节。
  2. 检查签名。 发布未正确签名,因此受影响平台上的验证失败。签名必须在缺失或无效时阻止发布。
  3. 比较客户端日志。 更新错误从未到达中央遥测。应用程序吞噬了事件并继续运行,留下了团队无法可靠依赖的证据。
  4. 检查发布控制。 没有内部渠道或阶段性团队。所有合格客户端都使用相同的源,因此失败没有包含点而扩散。
  5. 寻找回滚。 团队没有测试过的程序来重新发布前一个版本或将客户端导向破坏的发布。

Electron 的官方文档清楚地阐述了平台限制。 Linux 没有内置的自动更新支持,macOS 更新请求必须满足 应用传输安全要求. 文档还指出,签名是可靠的macOS更新和发布验证的前提条件。 Electron 自动更新文档 定义了 API 的约束,发布系统必须执行周边的运营控制。

Electron 应用程序自动更新后记: 一个无法解释发生了什么的更新器是一种缺乏遥测的远程安装尝试。

工程时间的成本远远超过了预期。客户对桌面客户端失去了信心,支持团队需要解释不一致的行为,团队还花了整整一天重建应该在事件发生之前就存在的发布流程。

自动更新 操作系统签名是一个发布门槛,清单定义了客户端契约,发布渠道限制了暴露,回滚是一个经过测试的路径而不是紧急创造。

选择合适的 Electron 自动更新路径

在2点,错误的更新选择变成一个运作问题。原生二进制更新必须处理签名、清单、安装器和回滚。仅渲染的JavaScript或CSS更改遵循不同的路径。托管约束也很重要:一个小GitHub托管的项目不需要与企业分布服务相同的发布控制。

For projects using electron-builder with signed artifact publishing, electron-updater is usually the practical default. Its ecosystem covers publish targets, release manifests, artifact downloads, and installation on the next launch. It supports several hosting models, but your team still owns signing, feed availability, channel policy, rollout controls, and monitoring. The Electron updater integration for Capgo Electron

update-electron-app suits teams that want a small integration around GitHub Releases. It checks at startup and then on a recurring interval, which keeps the setup simple but leaves less room for advanced channel selection, staged traffic, and custom rollback rules. The package is reasonable for a small release process, provided GitHub Releases and its availability match your operational requirements.

适用于评估混合交付模型的Web层捆绑包和本机发布的团队。 suits teams that want a small integration around Releases. It checks at startup and then on a recurring interval, which keeps the setup simple but leaves less room for advanced channel selection, staged traffic, and custom rollback rules. The package is reasonable for a small release process, provided Releases and its availability match your operational requirements. 选项
托管控制权 S3, GitHub, 通用 HTTPS 和其他发布目标 集成了打包发布签名 强大的基础,自定义策略通常围绕着 feed 中等
update-electron-app 主要是简单的 GitHub 发布流程 使用 Electron 签名模型 除非您添加周围的服务,否则有限
Squirrel.Windows 或 Squirrel.Mac 平台定制的分发流程 依赖于平台签名要求 可能,但通常需要额外的发布基础设施 对遗留应用程序而言,难度中等
自定义服务 对清单、授权、队伍和订阅有完全控制权 您负责验证设计和密钥处理 最大灵活性 最高
Capgo 实时更新 为 Web 层包管理的分发 使用其更新器和分发模型 目标受众和基于频道的分发 将原生二进制更新的运营模型与之分离

当您需要自定义服务,例如Hazel、Nuts或内部feed时,发布授权、租户目标、审计记录或受管部署规则可能会为此提供理由。然而,这也意味着您需要承担持续维护的责任。您的团队需要定义清单语义、保护签名密钥、保持客户端兼容性,并测试下载失败、发布被拒绝和回滚行为。

实时更新可以只推送渲染层的变化而不需要重建原生壳。它们不会替代 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和稳定版本的分离。一个频道是发布策略,而不是UI中的标签。每个频道应该解析到正确的签名包和清单。

定时检查并暴露生命周期事件

调用 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;
  }
});

更新事件行为的确切行为取决于平台和打包设置,因此应从安装的艺术品中测试,而不是从开发模式中测试。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(),因为正常的窗口关闭处理程序否则会阻止安装程序接管。

https://raw.githubusercontent.com/electron-userland/electron-builder/master/docs/electron-builder-autoupdate.png

两个失败值得明确的测试。首先,已经运行的客户端必须在 checkForUpdates() 调度上调用,而不仅仅是在启动时。其次, error 事件必须到达日志和遥测。如果应用程序吞噬它而不报告,则您的 应用程序故障排除流程 它以猜测取代证据开始。

为已签名的发布和清单编程CI/CD

发布管道是用户安装的真实来源。一个本地构建在一个开发人员的机器上工作并不证明发布的二进制文件、清单、签名和通道都描述了同一个发布。

Electron-builder的发布模型期望发布元数据和更新目标一起旅行。对于许多配置来说,这意味着一个名为artifact的文件,如 latest.yml 适用于Windows和 latest-mac.yml 适用于macOS,伴随着平台特定的包和blockmap文件。一个缺失的清单可以使一个完全有效的二进制文件对客户端不可见。

在CI中使签名显式

一个简化的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 Windows证书或证书引用
AWS_ACCESS_KEY_ID 具有狭窄访问权限的发布凭证
AWS_SECRET_ACCESS_KEY 与发布凭证配对的机密

发布配置应一致地识别提供商和频道:

{
  "build": {
    "publish": {
      "provider": "s3",
      "bucket": "example-electron-releases",
      "channel": "stable",
      "publishAutoUpdate": true,
      "updaterCacheDirName": "example-desktop-updater"
    }
  }
}

在发布之前,根据包版本、标签、提交SHA和smoke测试结果对任务进行门控。发布之后,验证feed包含预期的清单,并且清单指向由该任务生成的确切工件。然后 持续集成设置指南 当您正式化这些检查时,这很有用,团队比较管道编排也可能从了解何时使用Jenkins和Ansible一起使用中受益 常见的命令意在暴露不完整的发布:.

这些检查并不能替代签名安装测试。它们捕获的是上传二进制文件而没有元数据的客户端需要发现它的操作错误

npx electron-builder --publish never
test -f dist/latest.yml
test -f dist/latest-mac.yml
find dist -name "*.blockmap" -print

Those checks don’t replace a signed installation test. They do catch the operational mistake of uploading a binary without the metadata clients need to discover it.

发布策略、渠道和回滚策略

发布源应该表现得像一个部署目标而不是下载目录。保持 内部, 测试, 和 最新 渠道分开,各个渠道都有自己的清单和签名的文件集。推广应该将经过测试的发布移到策略之间,而不是在客户端下载它时覆盖文件。

渠道分离也保护了生产环境免受意外的测试构建。更新器应该知道一个客户是否属于内部试验组、测试用户群还是稳定用户群之前,它就应该评估发布源。

在广泛暴露之前使用试验组

一个自定义清单字段可以表达阶段性交付:

version: 4.8.0
path: Example-Setup-4.8.0.exe
sha512: signed-artifact-hash
rolloutPercentage: 10

主进程可以分配一个稳定的用户级别,然后将该级别与 rolloutPercentage比较。稳定的分配很重要。一个用户在每次检查时都在有资格和无资格之间移动会接收到不可预测的行为并使支持报告难以解释。

在发布生存观察窗口后才扩大该群体。具体的时间窗口应该反映您的使用模式,但决策应该基于信号,而不是单纯的日历。跟踪更新检查结果、下载完成、启动健康、崩溃、渲染器异常和身份验证成功。

信号 动作 原因
Feed 或签名错误超过团队批准的限制 暂停发布 客户可能无法验证或发现发布
发布后启动检查失败 恢复Feed 二进制文件可能安装但在启动时失败
渲染器异常在推广后增加 暂停当前群体 原生安装器可能是健康的,而新应用程序code不健康
信号保持在发布预算内 扩大人群 证据支持更广泛的暴露

不要将回滚与删除 artifact 混淆。已缓存的客户端可能有元数据,某些客户端可能已经运行了坏版本。需要回滚计划的前提是有一个之前签名的发布、一个feed更改和一个可以恢复的客户端行为。

运营规则: 回滚必须由on-call工程师执行,而不需要重建应用程序以应对事件。

在实践中,runbook应该将之前的发布清单推广到受影响的频道,invalidated staging标记,并确认新检查指向安全版本。如果问题出在渲染器code而不是原生shell,一个针对性的web层回滚可能更快。一个平台,如 Capgo 分段回滚 可以适用于该独立的交付层,但不应该掩盖原生二进制回滚和web-bundle回滚之间的界限。

将Auto-Update视为安全控制

一个Electron更新程序下载可执行code并且可以在用户几乎不需要交互的情况下安装。因此,更新路径成为 安全边界这不仅仅是一个便利功能。Electron的官方文档描述了平台约束,如macOS ATS,安全性已经记录了2022年的场景,其中攻击者控制更新基础设施可以提供恶意包,仍然通过code-签名检查通过,正如在 Electron Builder 自动更新安全文档.

Code签名仍然是基础,但这不是整个信任模型。签署每个版本,验证证书和身份在CI期间,维护一个文档的密钥轮换程序。在macOS上,结合签名与notarization和硬化的运行时,适合于您的应用。在Windows上,确保证书所有权、续期和构建访问是可审计的。Linux需要一个分布式策略,因为Electron没有提供一个通用的更新器。

保护元数据就像保护二进制文件一样小心

一个签名的二进制文件仍然可以与错误的发布相关联,如果元数据通道被破坏或配置不当。考虑添加一个签名的清单,验证它与公共密钥嵌入在应用程序中,强制最低允许版本,拒绝未经授权的降级,除非有一个明确允许的恢复路径。

也需要对feed进行生产控制:

  • 限制发布访问: 只给CI授予发布发布资产所需的权限。
  • 保护签名密钥: 将证书和私钥存储在管理的机密存储中,而不是存储在仓库文件中。
  • 依赖项: 在CI中锁定Electron、electron-builder和传递依赖项。
  • 检查工件: 扫描生成的包并与预期的提交和版本进行比较。
  • 要求安全传输: 遵循ATS和严格HTTPS要求的更新请求。
  • 监控验证失败: 将重复的签名或清单失败视为安全事件,而不是普通网络噪音。

Electron的维护工具生态系统继续添加打包和更新器覆盖,但维护不意味着不需要威胁建模。实践目标是确保攻击者在破坏一个存储桶、CDN或构建步骤后仍然无法让客户接受未经授权的发布。该 签名验证指南 为设计额外验证层提供了有用的上下文。

生产更新流程的四步流程图,显示了验证、分阶段发布、事件监控和回滚协议。

生产环境更新手册和检查清单

只有当另一个工程师在压力下可以操作它时,发布才算完成。将检查清单放在部署任务和事件通道附近。

预发布门槛

  • 版本标识: 确认包版本、发布标签、提交SHA和更改日志一致。
  • 签名: 验证每个平台的艺术品已签名,并且已完成不良或等效验证。
  • 清单合同: 确认 latest.yml, latest-mac.yml,哈希值、路径和块映射与上传的艺术品匹配。
  • 频道安全: 在内部或beta feed发布之前,推广发布频道。
  • __CAPGO_KEEP_0__: __CAPGO_KEEP_1__:

__CAPGO_KEEP_2__:

  • __CAPGO_KEEP_3__: __CAPGO_KEEP_4__:
  • __CAPGO_KEEP_5__: __CAPGO_KEEP_6__:
  • __CAPGO_KEEP_7__: __CAPGO_KEEP_8__:
  • __CAPGO_KEEP_9__: __CAPGO_KEEP_10__:

__CAPGO_KEEP_11__:

如果新二进制无法启动,进程生成失败,更新下载停止完成,立即停止推广。恢复之前的签名清单,invalidating 阶段标记,并验证新客户是否解析到之前的版本。然后通过遥测确认舰队正在恢复之前与用户沟通关闭。

具体的回滚命令取决于您的提供商,但序列应该始终被记录: 将频道清单翻转到之前的版本,invalidating 阶段标记,推送一个强制更新标记,如果恢复需要它,验证下降路径与实时遥测一个没有经过测试的回滚只是希望。

专业的生产软件更新管理清单,包括规划、执行和更新后验证步骤。


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

Live updates for Capacitor apps

当一个web层bug活跃时,通过Capgo将修复推送到应用程序,而不是等待几天的应用商店批准。用户在后台接收更新,而本机更改保持在正常的审查路径中。

人工支持

立即开始

最新博客文章

Capgo为您提供创建真正专业的移动应用所需的最佳见解。