Electron 自动更新,绕过 latest.yml 使用自定义接口

开发 前端
在开发阶段想测试一下检测更新的流程走没走通,可能不太好测试,因为 Electron 默认在开发环境下会绕过更新检测。开发环境下 Electron 启动后,如果接入了自动更新。

大家好,我是杨成功。

在上一篇文章《手撸 Electron 自动更新,再繁琐也要搞懂它》中,我们详细介绍了 Electron 自动更新的全流程。

简单来说,就是打包生成 latest.yml 文件和安装包,并上传到服务器。客户端打开时访问服务端的 latest.yml 地址,判断是否有新版本,有则自动下载新包并更新。

对于以上的更新流程,很多朋友在评论区留言表示不够灵活,比如:

  • 只能自动更新吗?能不能通过后端接口来判断是否更新?
  • 必须要用 latest.yml 吗?能不能绕开它?
  • 如何区分 Window、Mac、Mac M1 三种安装包?

下面我们一一解答这些问题,并手撸一个更灵活的自动更新接口。

如何本地测试更新?

在开发阶段想测试一下检测更新的流程走没走通,可能不太好测试,因为 Electron 默认在开发环境下会绕过更新检测。

开发环境下 Electron 启动后,如果接入了自动更新,主进程控制台会打印下面的信息:

Skip checkForUpdates because application is not packed and dev update config is not forced

意思是当前是开发环境,未打包,所以绕过检测。Electron 通过 app.isPackaged 的值来判断是否打包,那么在开发环境下,我们可以修改一下这个值:

import { app } from 'electron';

// 未打包时是开发环境
if (!app.isPackaged) {
  Object.defineProperty(app, 'isPackaged', {
    get: () => true,
  });
}

重新运行,大概率会看到第二个错误:

Error: ENOENT: no such file or directory /xxxx/app-update.yml

因为没有打包嘛,所以找不到 app-update.yml 这个文件,索性我们就创建一个。

在根目录下创建一个 dev-update.yml 文件(文件名可自定义),写入配置:

provider: generic
updaterCacheDirName: demo-updater # 下载目录

然后在开发环境指定这个配置文件地址:

import { app } from 'electron';
import path from "path";

if(!app.isPackaged) {
  ...
  autoUpdater.updateConfigPath = path.join(__dirname, "../../dev-update.yml");
}

重新运行项目,会发现检测更新的逻辑可以正常执行了。

能不能绕开 latest.yml,走后端接口?

可能大家希望的检查更新流程是这样:

调用后端的 API 接口,接口返回 JSON 格式数据,包含最新的版本号和安装包下载地址。将该版本号与本地版本号做对比,如果不一样则表示有更新,并执行下载。

然而 electron-updater 是通过 latest.yml 文件来获取版本号等信息。latest.yml 是一个配置文件,内容如下:

version: 1.0.2
files:
  - url: elapp_1.0.2.exe
    sha512: xxxxxx
    size: 72716511
path: elapp_1.0.2.exe
sha512: xxxxxx
releaseDate: '2023-11-29T02:28:28.032Z'

大家想绕过它,可能是因为 YAML 文件的内容看不太懂,或者与接口格式不匹配。其实它就是一个普通的配置文件,转换成 JSON 格式如下:

{
  "version": "1.0.2",
  "files": [
    {
      "url": "elapp_1.0.2.exe",
      "sha512": "xxxxxx",
      "size": "72716511"
    }
  ],
  "path": "elapp_1.0.2.exe"
}

可能有人会问:配置文件可以改成latest.json吗,这样后端就可以动态返回了。

我仔细查阅过文档,目前只支持 YAML 文件,不支持 JSON,所以 latest.yml 无法绕开。但这只是官方说法,咱还是有办法滴。

经过大量测试,我发现把 latest.yml 文件的内容手动改成 JSON 格式也是可以的。这样的话,就可以写一个接口来模拟 latest.yml 的地址。

假设 latest.yml 的访问路由是/ele-app/latest.yml,那么写一个接口如下:

const app = require('express')()

app.get('/ele-app/latest.yml', (req, res, next) => {
  let resinfo = {
    version: "1.0.25",
    path: "xxx_1.0.25.exe"
    sha512: "xxxxxx"
  }
  res.send(resinfo)
})

该接口就是自定义的检测更新接口。接口返回值中至少要包含version、path、sha512三个属性(与 latest.yml 中的配置保持一致)。这样我们不需要上传 latest.yml 文件了,用该接口替代即可。

基于该检测更新接口,接下来我们逐步实现自定义更新流程。

如何区分 Windows 和 Mac 系统

对于 Windows 和 Mac 两个系统的更新,electron-updater 使用不同的配置文件,分别是 latest.yml  latest-mac.yml

从上一步的检测更新接口来看,不同的配置文件就是不同的路由,我们改造接口如下:

app.get('/ele-app/:platform', (req, res, next) => {
  let { platform } = req.params
  let resinfo = null
  // 返回 Windows 配置
  if(platform == 'latest.yml') {
    resinfo = {
      version: "1.0.2",
      path: "xxx_1.0.2.exe"
      sha512: "xxxxxx"
    }
  }
  // 返回 Mac 配置
  if(platform == 'latest-mac.yml') {
    resinfo = {
      version: "1.0.3",
      path: "xxx_1.0.3.dmg"
      sha512: "xxxxxx"
    }
  }
  if(!resinfo) {
    resinfo = { code: 400, msg: '参数错误' }
  }
  res.send(resinfo)
})

上面代码中,使用动态路由返回 Windows 和 Mac 的配置,同时兼容了两个平台的更新检测。

完整的自定义更新流程

经过上面的介绍,自定义检测更新的关键思路已经讲清楚了。完整的更新流程如下:

(1)打包各个平台的安装包,上传服务器。

如何打包在上一篇介绍过,就不展开说了。注意的是:现在你只需要上传安装包,不需要上传 latest.yml 文件。

(2)在主进程中设置更新地址,并手动控制更新。

假设我们编写的检测更新接口已经部署,设置方法如下:

import { autoUpdater } from 'electron-updater';

// 设置检测更新的地址
autoUpdater.setFeedURL('http://[xxx]/ele-app');
// 不自动下载
autoUpdater.autoDownload = false;
// 触发检测
autoUpdater.checkForUpdatesAndNotify().catch();

// 监听到可更新
autoUpdater.on('update-available', (info) => {
  // info 是检测更新接口返回的数据
  if (info.can_download) {
    // can_download 是自定义属性
    autoUpdater.downloadUpdate();
  }
});

(3)编写检测更新接口,返回配置。

返回的配置我们可以自定义,假设返回结果如下:

{
  "version": "1.0.2",
  "path": "http://xxx/xxx_1.0.2.exe",
  "sha512": "xxxxxx",
  "can_download": false
}

上述示例中,接口返回了自定义属性 can_download,我们在第(2)步中使用了该属性,用于判断是否执行下载更新。

通过这种方式,即便我们更新了安装包,也可以自由决定是否要下载安装。

这里有一个小惊喜:path 属性的值可以是一个完整的安装包地址,这样可以把安装包上传到任意地方。如果值是一个文件名,那么会以第(2)步中 setFeedURL() 方法设置的地址为前缀。

提醒:sha512 属性的值必须从打包生成的 latest.yml 中获取,不可以随意写,否则在安装时不能通过检验,会报这个错:

Error: sha512 checksum mismatch

特别篇:Mac 如何区分 Intel 和 M1?

Mac 系统有两种软件包,分别对应 M1 芯片和 Intel 芯片,两者不兼容。一般打包时我们也会构建两种安装包。

那么在检测更新时,我们就需要返回适配当前系统的安装包。但不管是 M1 还是 Intel 都使用 latest-mac.yml 这一个配置文件,该如何区分呢?

这个时候就要从主进程中获取参数,然后传给接口了。步骤如下:

(1)在主进程中,获取系统架构,并通过请求头传给检测更新接口。

import { autoUpdater } from 'electron-updater';

// 添加请求头
autoUpdater.requestHeaders = {
  elearch: process.arch,
};

(2)在接口中接收参数,并在返回 Mac 配置中判断:

app.get('/ele-app/:platform', (req, res, next) => {
  let { platform } = req.params
  let { elearch } = req.headers

  if(platform == 'latest-mac.yml') {
    if(elearch == 'arm64') {
      // M1
      return {...}
    } else {
      // Intel
      return {...}
    }
  }
})

有了上面的逻辑,我们在打包时就可以单独打两个包,分别上传,然后在检测时返回不同的下载地址。

总结

本文使用自定义接口的方式,绕过了 latest.yml 配置文件,使自动更新的灵活性更高,也更符合我们的常规习惯,但是需要维护一个更新接口。

如果不想维护接口,那么上传 latest.yml 文件的方式更适合你。

责任编辑:姜华 来源: 程序员成功
相关推荐

2023-11-22 17:00:10

ElectronWeb

2015-02-12 15:33:43

微信SDK

2015-02-12 15:38:26

微信SDK

2015-03-30 14:15:55

自动更新Android

2021-11-08 22:24:04

Windows 10Windows微软

2017-10-20 12:13:11

数据库PostgreSQL时间戳

2010-05-24 11:27:20

SVN配置自动更新WE

2018-03-19 11:50:00

LinuxCentOS yum命令行

2009-06-23 11:35:44

JSF的Naviati

2019-12-25 11:47:27

LinuxFVWM

2010-10-25 16:05:07

oracle自定义函数

2022-01-14 09:17:13

PythonAPISIX插件

2010-01-08 13:20:52

ibmdwWeb

2010-07-01 09:38:31

jQuery

2021-05-28 08:58:41

Golang网卡metrics

2009-08-21 15:38:45

ControllerF

2022-11-01 11:15:56

接口策略模式

2022-09-13 15:44:52

VSLook插件

2021-10-28 08:39:22

Node Export自定义 监控

2012-04-16 14:55:29

MacChrome
点赞
收藏

51CTO技术栈公众号