1. 概述
本文讲解内容:
- Electron应用自动升级部分功能的实现机制,包含
强制升级
,更多内容可以参考自动升级总结,例如灰度发布
、增量更新
、Release Notes
等 - Electron应用的开发测试方法
- Electron应用的发布流程
2. 功能介绍
2.1. 强制升级
- 在
Release Server
的latest.yml
增加记录minVersionSupported=x.y.z
- 客户端获取Update之后,比较当前版本和
minVersionSupported
,然后做出抉择。如果小于minVersionSupported
,则强制更新
autoUpdater.on('update-downloaded', (info) => {
log.info(info)
if (info.minVersionSupported == undefined ||
semver.lt(app.getVersion(), info.minVersionSupported)) {
autoUpdater.quitAndInstall(true, true)
return
}
dialog.showMessageBox({
type: 'info',
title: '软件升级',
message: '发现新版本,是否立即升级?',
buttons: ['是的', '稍后']
}).then((resp) => {
if (resp.response === 0) {
log.info('begin to install new version ...')
autoUpdater.quitAndInstall(true, true)
}
})
})
2.2. 灰度发布
计划用grayReleased: 100
实现,仔细想了一下,如果客户端每次随机一个数字,启动多次之后,可能都更新到最新版本了,所以,并没有真正灰度发布。
如果要保存随机数字与Version的对应关系
,倒是可以解决这个问题,只是稍麻烦了点,回头再说吧。
3. Electron应用的开发测试方法
3.1. 修改development模式下的代码
在开发时,如果不禁用autoupdate,每次reload都会收到一个报错:
[error] Error: Error: ENOENT: no such file or directory, open 'D:\code\im\desktop-client\dist\main\dev-app-update.yml'
简单的办法,在development模式下,禁用autoupdate,但是,后续如果要做autoupdate窗口,总不能每次都build,然后执行uppacked文件吧,会很花时间。
所以,一劳永逸的做法,修改代码,以支持development模式下的正常开发。
- 在根目录增加
dev-app-update.yml
provider: s3
bucket: public
endpoint: http://10.211.28.93:9999
channel: latest
path: /im/artifact/lastest/win
updaterCacheDirName: foxchat-updater
- 修改
updateHandle.ts
const isDev = process.env.environment == 'development'
if (isDev) {
autoUpdater.updateConfigPath = path.join(__dirname, '../../dev-app-update.yml')
}
3.2. 开发测试
如果不开发autoupdate功能
- 修改
package.json
的version
,比Release Server
的version
大即可,不会触发更新 - 修改
dev-app-update.yml
,指向另外一个地方generic server
或github
- 干脆注释掉
autoUpdater.checkForUpdates()
也可以,只是发布时候,一定要记得打开,否则风筝就断了线啦
如果开发autoupdate功能
- 修改
package.json
的version
,比Release Server
的version
小即可,会触发更新
4. Electron应用的发布流程
关键点是如何在publish时候修改latest.yml
,electron-builder也不支持
甚至如何只publish,也不支持,issue-4535
所以,只能自己开发脚本,完成latest.yml
的修改,并上传MinIO
在build之后,测试OK了,和publish
脚本一起执行即可
- 执行命令:
node -r ts-node/register script/publish
- 代码如下:
const Minio = require('minio')
const shell = require('shelljs')
const fs = require('fs')
import chalk from 'chalk'
import { version } from '../package.json'
const minioClient = new Minio.Client({
endPoint: '10.211.28.93',
port: 9999,
useSSL: false,
accessKey: 'xxx',
secretKey: 'EXAMPLEKEY'
})
const TAG = '[script/build.ts]'
const extraFile = './script/publish-extra-info.yml'
const latestFile = `./release/${version}/s3/latest.yml`
function isExists(filepath: string) {
if (!fs.existsSync(filepath)) {
console.log(TAG, `File ${filepath} not exists.`)
}
return true
}
// TODO: 该脚本被执行多次时,会累加内容到latest.yml,可能会引起自动升级功能失效。因此,不能被执行多次
if (!isExists(extraFile) || !isExists(latestFile) || shell.exec(`cat ${extraFile} >> ${latestFile}`).code !== 0) {
console.log(TAG, chalk.green(`Append failed from ${extraFile} to ${latestFile}.`))
shell.exit(1)
}
console.log(TAG, chalk.green(`Append successed from ${extraFile} to ${latestFile}.`))
minioClient.fPutObject('public', 'im/artifact/lastest/win/latest.yml', latestFile, (err: any, objInfo: string) => {
if(err) {
return console.log(TAG, err)
}
console.log(TAG, chalk.green(`Upload to MinIO successed, object id: ${objInfo}.`))
})
5. 后记
在线升级功能,必须要有,几乎标配
。否则,只能仰仗用户手动升级,体验很差。
但是,功能一定要稳定健壮,可以不炫,可以不酷,朴实可靠即可。
另外,随着项目上线,对MinIO的权限管理需要加强,包含bucket和key等。