前言
目前electron程序主流的打包方式是使用electron-builder,同时使用electron-updater进行版本更新,但是本人所在团队目前新开发的一个electron项目技术方案为Electron Forge+ React + TypeScript,采用Electron Forge打包,打包方式不一样导致版本更新方式也不一样,也就是项目没法使用electron-updater进行版本更新😟😟😟,根据Electron Forge官方文档中介绍的版本更新方式,最终决定采用服务端搭建electron-release-server+客户端使用autoUpdater进行版本更新。
Deploying an Update Server
If you're developing a private Electron application, or if you're not publishing releases to GitHub Releases, it may be necessary to run your own update server.
Depending on your needs, you can choose from one of these:
- Hazel – Update server for private or open-source apps which can be deployed for free on Vercel. It pulls from GitHub Releases and leverages the power of GitHub's CDN.
- Nuts – Also uses GitHub Releases, but caches app updates on disk and supports private repositories.
- electron-release-server – Provides a dashboard for handling releases and does not require releases to originate on GitHub.
- Nucleus – A complete update server for Electron apps maintained by Atlassian. Supports multiple applications and channels; uses a static file store to minify server cost.
此处先放上效果图:
搭建electron-release-server
- 从GitHub上下载electron-release-server源码
git clone https://github.com/ArekSredzki/electron-release-server.git
本机运行
本机环境:macOS Big Sur,版本11.5.2,Apple M1芯片
- 下载安装PostgreSQL 11.5版本 下载地址:www.enterprisedb.com/downloads/p…
安装完成后将Postgresql路径添加到PATH环境变量,如下:
vi /etc/profile
export PATH=${PATH}:/Library/Postgresql/11/bin
source /etc/profile
- 打开SQL Shell(psql),执行以下命令:
CREATE ROLE electron_release_server_user ENCRYPTED PASSWORD '123456' LOGIN;
CREATE DATABASE electron_release_server OWNER "electron_release_server_user";
CREATE DATABASE electron_release_server_sessions OWNER "electron_release_server_user";
启动Postgresql的命令如下:
pg_ctl -D /Library/Postgresql/11/data start
- 修改config.js
cd electron-release-server
cp config/local.template config/local.js
vi config/local.js
修改后的config.js如下:
module.exports = {
appUrl: 'http://localhost:80',
auth: {
static: {
username: 'admin',
password: '123456'
},
},
jwt: {
token_secret: 'EB9F0CA4414893F7B72DDF0F8507D88042DB4DBF8BD9D0A5279ADB54158EB2F0'
},
connections: {
postgresql: {
adapter: 'sails-postgresql',
host: 'localhost',
user: 'electron_release_server_user',
password: '123456',
database: 'electron_release_server'
}
},
session: {
secret: 'EB9F0CA4414893F7B72DDF0F8507D88042DB4DBF8BD9D0A5279ADB54158EB2F0',
database: 'electron_release_server_sessions',
host: 'localhost',
user: 'electron_release_server_user',
password: '123456',
port: 5432
},
files: {
dirname: '/Users/kongpf/Desktop/electron-release-server/releases',
}
};
字段 | 说明 |
---|---|
appUrl | 网站地址,如http://localhost:80 |
username | 管理员的用户名,如admin |
password | 管理员的密码,如123456 |
token_secret | jwt生成token的secret,推荐为63个随机字母或数字 |
host | 主机名,当前机器即为localhost |
user | 数据库用户名,此处为electron_release_server_user |
password | 数据库密码,此处为123456 |
database | 数据库名称,此处为electron_release_server或electron_release_server_sessions |
port | 数据库端口,此处为默认端口5432 |
dirname | 存放上传文件的路径,根据自己的机器情况,设置为合适的值 |
- 设置session
mkdir sails_pg_session
cd sails_pg_session
git clone https://github.com/ravitej91/sails-pg-session.git .
npm update
psql electron_release_server_sessions < ./sql/sails-pg-session-support.sql postgres
- 安装运行
//安装依赖
npm install -g sails
npm install
//开发环境运行
npm start
//生产环境运行
npm start --prod
注意:node版本要为10,如10.16.0,版本太高程序运行不起来,bower.json文件中"bootstrap-sass-official": "^3.3.7"改为"bootstrap-sass-official": "3.3.7"
程序运行成功后在浏览器里访问地址http://localhost:80,就可看到以下页面:
docker运行
- 安装docker
- 修改docker-compose.yml文件 主要修改APP_USERNAME(管理员用户名),APP_PASSWORD(管理员密码),TOKEN_SECRET(JWT Secret)信息,修改后的配置如下:
version: '2'
services:
web:
build: .
environment:
APP_USERNAME: admin
APP_PASSWORD: 123456
DB_HOST: db
DB_PORT: 5432
DB_USERNAME: releaseserver
DB_NAME: releaseserver
DB_PASSWORD: secret
TOKEN_SECRET: EB9F0CA4414893F7B72DDF0F8507D88042DB4DBF8BD9D0A5279ADB54158EB2F0
APP_URL: 'localhost:5000'
ASSETS_PATH: '/usr/src/electron-release-server/releases'
depends_on:
- db
ports:
- '5000:80'
entrypoint: ./scripts/wait.sh db:5432 -- npm start
volumes:
- ./releases:/usr/src/electron-release-server/releases
db:
image: postgres:11
environment:
POSTGRES_PASSWORD: secret
POSTGRES_USER: releaseserver
volumes:
- ./postgresql:/var/lib/postgresql/data
- 构建镜像
docker-compose up -d
构建成功后在浏览器里访问地址http://localhost:5000,就可看到以下页面:
注意:构建过程中要访问Github下载依赖,很可能下载依赖失败导致构建失败,贴心的我已经帮你打包好了镜像并上传DockerHub,替换docker-compose.yml文件中的build: .为image: rainboy2010/electron-release-server:latest即可
修改后的electron-release-server代码已经上传到GitHub,地址: github.com/kongpf8848/…
上传新版本
1.以Admin登录,用户名和密码为之前配置的admin/123456
2.在版本管理页面,添加版本信息
字段 | 说明 |
---|---|
VersionName | 版本名称,如1.0.1 |
Flavor | 风味,取默认值default即可 |
Channel | 渠道,取默认值stable即可 |
Change Notes | 更新日志 |
3.添加Asset
Platform可取的值为:
- Windows 64 bit
- Windows 32 bit
- OSX 64 bit Intel
- OSX 64 bit ARM
- Linux 64 bit
- Linux 32 bit
macOS程序更新需上传.dmg和.zip文件,Windows程序更新需上传.exe和.nupkg文件
以macOS系统为例,上传.dmg和.zip文件后,页面如下:
客户端代码
- 编写一个update.js文件,用于封装版本更新逻辑,代码如下:
//update.js
import {app,BrowserWindow,ipcMain,dialog,autoUpdater} from 'electron'
export class UpdateManager {
private static instance: UpdateManager = new UpdateManager();
public static getInstance() {
return this.instance;
}
baseUrl="http://localhost:5000"
mainWindow:BrowserWindow=null;
sendUpdateMessage(text:any) {
this.mainWindow?.webContents.send('update-message', text)
}
init(window:BrowserWindow){
console.log("ipcMain-update,init")
this.mainWindow=window
var platform=""
var arch=""
if(process.platform === 'darwin'){
platform="osx"
if(process.arch === 'arm64'){
arch='arm64'
}else{
arch='64'
}
}else if(process.platform === 'win32'){
platform="windows"
if(process.arch === 'x64'){
arch='64'
}else{
arch='32'
}
}else if(process.platform === 'linux'){
platform="linux"
if(process.arch === 'x64' || process.arch === 'x86_64'){
arch='64'
}else{
arch='32'
}
}
var feedUrl=this.baseUrl+"/update/"+platform+"_"+arch+"/"+app.getVersion()+"/stable"
autoUpdater.setFeedURL({url: feedUrl});
autoUpdater.on('error', error=> {
console.log("ipcMain-update,error:"+error)
this.sendUpdateMessage({cmd: 'error',message: error})
});
autoUpdater.on('update-not-available',()=>{
console.log("ipcMain-update,update-not-available")
this.sendUpdateMessage({cmd: 'update-not-available',message: 'has not found any updates'})
});
autoUpdater.on('update-available',()=> {
console.log("ipcMain-update,update-available")
this.sendUpdateMessage({cmd: 'update-available',message: 'find new version'})
});
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
console.log("ipcMain-update,update-downloaded:"+event)
this.sendUpdateMessage({cmd: 'update-downloaded',message:{
releaseNotes:releaseNotes,
releaseName:releaseName
}})
const dialogOpts = {
type: 'info',
buttons: ['确定', '稍后'],
title: '版本更新',
message: "发现新版本:"+releaseName,detail: '新版本已经下载完成,是否现在重启?'
}
dialog.showMessageBox(dialogOpts).then((returnValue) => {
if (returnValue.response === 0) {
autoUpdater.quitAndInstall()
}
})
})
ipcMain.on("checkForUpdate", (event, args) => {
console.log("ipcMain-update,checkForUpdate")
autoUpdater.checkForUpdates();
})
}
}
feedUrl的格式如下:
${baseUrl}/update/${platform}/${version}/${channel}
,如:
http://localhost:5000/update/osx_64/0.0.1/stable
http://localhost:5000/update/osx_arm64/0.0.1/stable
http://localhost:5000/update/windows_64/0.0.1/stable
http://localhost:5000/update/windows_32/0.0.1/stable
- 在主进程中初始化版本更新
//main.js
let mainWindow: BrowserWindow
const createWindow = (): void => {
mainWindow = new BrowserWindow({
width: 1200,
height: 600,
minWidth: 700,
minHeight: 350,
webPreferences: {
nodeIntegration: true,
contextIsolation: true
}
});
......
if(app.isPackaged) {
UpdateManager.getInstance().init(mainWindow)
}
};
app.on('ready', createWindow);
- 在渲染进程中检查新版本,如下:
//render.js
window.addEventListener('DOMContentLoaded', () => {
ipcRenderer.on("update-message",function(event,args){
console.log("ipcRenderer,update-message,message:"+JSON.stringify(args.message))
if("update-not-available" ===args.cmd){
console.log("ipcRenderer,update-not-available")
}else if("update-available" ===args.cmd){
console.log("ipcRenderer,update-available")
}else if("update-downloaded" ===args.cmd){
console.log("ipcRenderer,update-downloaded")
}else if("error" ===args.cmd){
console.log("ipcRenderer,error")
}
});
setTimeout(() => {
console.log("ipcRenderer,checkForUpdate")
ipcRenderer.send("checkForUpdate");
},3000);
});
当检查到有版本更新时,app会自动从远程服务器下载更新包,下载完成后会弹窗提示用户安装,如下:
macOS系统 | Windows系统 |
---|---|
注意:使用autoUpdater更新程序时,发现新版本后app会立即下载,中间没办法暂停下载,下载过程中也没有下载进度回调😒😓😨