本文主要是讲述一个托管于Github的Electron项目,如何实现使用小文件的增量升级

背景

我有一个托管在github的开源项目,使用的技术框架是 Electron+Vue,electron可以说是一个有一定名气的框架了,但是在处理一个增量更新的需求的时候就遇到了一些问题,特将我的处理方法分享一下。

已知 Electron其实是有很多自动升级的方案的,但是增量升级的非常少,所谓的自动升级方案大多是利用模块中的打包工具的预留接口或者在代码中直接下载整个安装数据包,如果是公司或者大型的项目,当然无可厚非,但是对于一个小型开源项目来说,这种做法十分的不经济。

首先一方面,我所使用的Electron-builder,每次打包生成的安装文件有40M之大,这是Electron 2.0的,前期项目还使用了Electron 1.8,安装文件也有36M。第二,这个项目比较小,直接托管在github上,使用github的release进行分发,如果要做自动更新的话,势必要自己部署更新和分发服务器(因为github的release基于aws,在国内被墙)。

想到之前看到的一篇文章,利用他们团队每次只更新Renderer.js的特性,做了个小型分发服务器,于是我开始寻找是否有直接分发升级代码的可能。

问题定位

我的项目是由 Electron-builder 进行打包的,其在打包过程中使用 nsis来创建windows环境下的安装包。于是先去找到安装后释放的文件。

在对应的安装目录下,会释放一个resource文件夹,其下有一个比较大的app.asar,根据程序 log,程序的主要代码文件就都储存在这里。

然后我们将 Electron-builder的配置文件进行修改,使其不打包为asar。

由于我使用的是Electron-Vue基于Vue-cli的自动构建,因此配置文件就是package.json
"build":{
   "asar": false, 
}

在build配置项下,将asar,设置为false,重新打包。这次直接使用dir模式打包来查看他的目录结构。

代码提示了一个不安全的warning,

asar using is disabled — it is strongly not recommended solution=enable asar and use asarUnpack to unpack files that must be externally available

不用管,打包完成后,在原app.asar的位置,变成了app文件夹,打开文件夹,是node_modules、package.json和一个dist文件夹,dist文件夹里是electron四件套

├─dist
│  └─electron
│      │  index.html
│      │  main.js
│      │  renderer.js
│      │  styles.css
│      │
│      └─fonts
│              element-icons--fonts.ttf

好了,这里就是重点了,这里的index.html、main.js、rederer.js和styles.css就是每次编译生成的文件,因此,如果要进行增量更新,这四个文件是必须更新的。

那么首先,我们要去除掉对这几个文件的打包,由于每次版本更新时,package.json的内容也会改变,所以package.json也必须排除。再次修改 Electron-builder的配置:

"build":{
   "asar": true,
    "asarUnpack":[
      "./dist/electron",
      "./package.json"
    ],
}

为什么一定要保留asar包呢?因为asar包有助于加快安装文件的安装速度,如果保留所有文件夹的话,在释放文件的时候磁盘压力会比较大,因此,使用asarUnpack属性,将不需要打进asar包里的文件路径指定。

再次查看打包后的结果:

│  app.asar
│  electron.asar
│
└─app.asar.unpacked
    │  package.json
    │
    └─dist
        └─electron
            │  index.html
            │  main.js
            │  renderer.js
            │  styles.css
            │
            └─fonts
                    element-icons--fonts.ttf

我们可以看到,在原来resource文件下,多出了一个app.asar.unpack文件夹,里面有所有我们所需要的文件。

完整方案

之所以说是完整方案而不是完整代码,是因为其具备一定的特例性。

我们这次的重点,依然还是针对托管在github的项目的增量更新,因此,我们首先要保证的,就是增量更新还得在github上。

我们不可能在发布一个release安装包的同时还去发布一个增量更新文件来进行增量更新,这不好,也不自动化,根据上节,我们已知,我们需要更新的就是包括package.json在内的五个文件,因此,我们首先需要定位这五个文件的位置,我使用的方法是,基于__filename,在代码中,通过搜索app.asar来定位到resource文件夹,再通过相对的路径获取到对应的文件的目录。

为什么是执行的时候路径里会有app.asar而不是app.asar.unpack呢 ,我是这么理解的,unpack的作用就是使程序在每一次运行之前,将unpack的文件重新打进asar包,然后再继续运行,这样既不破坏asar包的路径关系,也不会影响文件的独立性。

然后,我们需要在git里解除对dist目录的ignore,这样,我们每一次提交都会留下一个可以使用的增量更新文件,在release以后也可以根据tag来找到对应的dist里的文件。

其他的就没什么好说的了,剩下的就是程序该干的事情了,使用程序检查更新,是使用github的

https://api.github.com/repos/{username}/{repo}/releases/latest

可以获取到最后一个release的tag和一些release的信息,我们主要使用的是tag_name,根据tag_name,我们可以在

https://raw.githubusercontent.com/{username}/{repo}/${tag}/{relative_route}

这个地址获取到项目对应文件的原始代码。

github的请求需要带上有效的UserAgent,否则会被认为是非法

请求到对应文件之后,根据前面获取的本地路径,调用fs模块将字符写进文件,然后重启程序,就完成了这一次的增量更新。

总的来说,这样的更新方式有优点有缺点,优点是:增量更新的文件小,更新速度快,没有分发的烦恼。缺点当然也有,缺点就是无法适用于依赖会改变的情况,如果程序的依赖发生更改,那么之前的app.asar的文件就不再和新文件匹配,需要重新安装新的安装包。

后记

从全局来看,这种增量更新的办法是非常好的,既解决了用户频繁更新需要下载大安装包的问题,又解决了开发者不便部署安装服务器的问题,整个操作的过程也并不十分复杂,只需要在取消git对dist的ignore,并且每次relase之前确保当前分支build了最新的dist文件即可,如果github的连通率太低,甚至还可以使用国内的gitee的raw文件,使用克隆git仓库,每次release之后到gitee手动点一下拉取最新更新即可,如果不嫌烦的话,还可以多部署几个其他的开源平台,运维成本基本等于0。


项目相关:BLS,一个B站直播批量挂机工具。