如何使用 electron-builder 配置自动更新差异化渠道

文章导读
用 electron-builder 做自动更新时,如果只是发布一个最新安装包并让它覆盖安装,配置确实简单。但只要有 stable 和 beta 这类并行渠道,就需要把“当前发的是哪个渠道”写进发布元数据里。否则客户端请求更新时,拿到的文件永远是同一个 latest.yml,渠道之间的版本就会互相覆盖。
📋 目录
  1. A 在 build.publish 里显式声明 channel
  2. B 用环境变量区分发布操作
  3. C 发布完成后检查元文件和客户端请求
  4. D 渠道切换时的隔离和保留策略
  5. E Windows 和 macOS 上需要留意的两点
A A

用 electron-builder 做自动更新时,如果只是发布一个最新安装包并让它覆盖安装,配置确实简单。但只要有 stable 和 beta 这类并行渠道,就需要把“当前发的是哪个渠道”写进发布元数据里。否则客户端请求更新时,拿到的文件永远是同一个 latest.yml,渠道之间的版本就会互相覆盖。

electron-builder 的自动更新依赖 electron-updater,在发布配置中通过 channel 字段区分不同渠道。默认情况下,electron-builder 会在生成 latest.yml 时使用 package.json 中版本号的主版本作为渠道名,比如 1.x 对应 v1,这很容易导致多个渠道相互覆盖。要支持差异化渠道,需要在 build.publish 中显式设置 channel,例如设置 channel: "stable" 或 channel: "beta"。这样每次发布都会生成对应的 latest-stable.yml 或 latest-beta.yml,更新时 electron-updater 也会请求对应名称的 YAML 文件。需要注意,channel 名称必须与文件名规则…

这里说的“文件名规则”指的是,channel 会直接拼到 latest 后面。channel 设为 stable,就生成 latest-stable.yml;设为 beta,就生成 latest-beta.yml。所以配置 channel 时先想好一组稳定的名字,中途改名会导致旧客户端找不到更新文件。

在 build.publish 里显式声明 channel

我通常的习惯是,不依赖默认主版本作为渠道名,而是在 build.publish 里写一个明确的 channel 值。比如 electron-builder.yml 可以这样写:

appId: com.example.app
productName: ExampleApp
publish:
  provider: generic
  url: https://download.example.com/updates
  channel: "${CHANNEL}"

这里用 ${CHANNEL} 做占位符,留到发布时注入。如果你只是在本地临时发布,也可以直接把 channel 写成 "beta" 或 "stable"。但要注意,channel 一旦写进配置,发布出来的元文件就固定跟这个渠道名绑定,后续换名字要重新处理旧文件。

electron-builder 对环境变量的展开写法,不同版本支持情况不太一样。上面这种 ${CHANNEL} 是简化示意;如果不生效,可以改成 ${env.CHANNEL},或者在执行 electron-builder 命令前用脚本把配置文件里的占位符替换掉。哪种方式更适合你的项目,需要结合当前使用版本试一下。

如何使用 electron-builder 配置自动更新差异化渠道

用环境变量区分发布操作

实际操作中,可以通过环境变量或命令行参数动态传入渠道名。例如在 package.json 的 build 配置中写入 "publish": [{ "provider": "generic", "url": "https://example.com/download", "channel": "${CHANNEL}" }],发布时在 CI 脚本中分别执行 CHANNEL=beta electron-builder --publish,CHANNEL=stable electron-builder --publish。对于 electron-updater 的配置,通常不需要额外设置,因为 latest.yml 文件名中的渠道标识会自动读取。但如果你用 autoUpdater.setFeedURL 手动指定,务必保…

这里补一句:用 setFeedURL 手动指定更新地址时,要把 channel 一并传进去,比如 autoUpdater.setFeedURL({ url: 'https://...', channel: 'beta' })。只传 URL 不传 channel,electron-updater 还是会按默认的 latest.yml 去请求,渠道就失效了。

在 CI 里执行发布时,建议每次发布前把输出目录清空一次,避免上一次构建残留的 latest-stable.yml 混进本次发布里。然后分别对 stable 和 beta 跑两条构建任务,确保两次构建用到的 productName、appId 和 artifactName 都一致。

发布完成后检查元文件和客户端请求

验证渠道配置是否生效,最直接的方法是发布后查看生成的更新元文件。在 generic provider 下,输出目录里应当同时存在 latest-stable.yml 和 latest-beta.yml,而不是只有一个 latest.yml。若看到多个 latest-*.yml,则说明渠道已经分离。然后再检查客户端本地,electron-updater 在检查更新时,会按照 channel 值请求对应文件,如 stable 渠道请求 latest-stable.yml。你可以在应用日志中搜索 'Checking for update' 和 'Downloading update from' 两条记录,确认请求的 URL 是否包含正确的渠道名称。如果没有,多半是配置没被读取。

如何使用 electron-builder 配置自动更新差异化渠道

如果服务器上还有旧的 latest.yml,建议在发布新渠道前清掉或保留到单独目录。因为有些旧客户端可能还在用默认渠道名请求,留着 latest.yml 只会让它们拿到不确定的版本。检查时也可以直接用 curl 请求 latest-stable.yml,看返回内容里的版本号是否对应刚发布的版本。

渠道切换时的隔离和保留策略

渠道之间不会自动降级,也不会自动切到另一个渠道。如果某个渠道长时间不发布新版本,客户端会一直请求该渠道的 latest-*.yml,但不会因此跳去检查另一个渠道。所以当用户要从 beta 转到 stable,比较常见的处理是:保留 beta 渠道的更新文件,或者引导用户下载对应的完整安装包重新安装。

还需要注意,如果不同渠道的安装包文件名和 appId 容易混淆,更新时可能会互相覆盖。比如 appId 不同但 productName 相同,或者 projectUrl 一样,打包出来的安装包在系统里会被当成同一个应用。建议在 artifactName 里带上渠道标识,写成 ${productName}-${version}-${env.CHANNEL}.${ext},让 stable 和 beta 的安装包在文件名这一层就分开。

Windows 和 macOS 上需要留意的两点

有两个容易忽略的问题。Windows 上如果使用 NSIS 且安装程序带有电子签名,channel 名称里包含点号或空格,生成的 YAML 文件名会被转义,导致更新失败。所以渠道名最好只用小写字母和连字符。macOS 上,electron-updater 会从 app 包的 Info.plist 读取版本信息,如果渠道配置是通过环境变量传入,而环境变量没有传给打包进程,打包出来的 app 仍然会使用默认的 latest.yml。也就是说,环境变量必须在执行 electron-builder 命令前注入,不能只在运行应用时设置。

遇到“更新检查通过,但下载失败”这类情况,可以先检查这两处。Windows 看 channel 名是否包含特殊字符,macOS 看 CI 里是否真的把 CHANNEL 传给了打包进程。这类问题通常不会在本地第一次发布时暴露,因为本地环境变量往往已经设置好了。