feature: 1)检查更新功能,使用 github api,来获取版本数据;2)自动检查更新功能,可配置化。

pull/303/head
王良 2024-04-23 16:03:07 +08:00
parent 9d445e31fd
commit c498dd7cc1
5 changed files with 211 additions and 65 deletions

View File

@ -24,6 +24,8 @@ module.exports = {
url: 'https://gitee.com/wangliang181230/dev-sidecar/raw/docmirror/packages/core/src/config/remote_config.json5'
},
theme: 'light', // 主题light=亮色, dark=暗色
autoChecked: true, // 是否自动检查更新
skipPreRelease: true, // 是否忽略预发布版本
dock: {
hideWhenWinClose: false
},

View File

@ -3,17 +3,19 @@ import { autoUpdater } from 'electron-updater'
import path from 'path'
import request from 'request'
import progress from 'request-progress'
// win是所有窗口的引用
import fs from 'fs'
import AdmZip from 'adm-zip'
import logger from '../../utils/util.log'
import log from '../../utils/util.log'
import appPathUtil from '../../utils/util.apppath'
import pkg from '../../../package.json'
import DevSidecar from '@docmirror/dev-sidecar'
// eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin'
const isLinux = process.platform === 'linux'
function downloadFile (uri, filePath, onProgress, onSuccess, onError) {
logger.info('download url', uri)
log.info('download url', uri)
progress(request(uri), {
// throttle: 2000, // Throttle the progress event to 2000ms, defaults to 1000ms
// delay: 1000, // Only start to emit after 1000ms delay, defaults to 0ms
@ -21,11 +23,11 @@ function downloadFile (uri, filePath, onProgress, onSuccess, onError) {
})
.on('progress', function (state) {
onProgress(state.percent * 100)
logger.log('progress', state.percent)
log.log('progress', state.percent)
})
.on('error', function (err) {
// Do something with err
logger.error('下载升级包失败', err)
log.error('下载升级包失败', err)
onError(err)
})
.on('end', function () {
@ -35,7 +37,11 @@ function downloadFile (uri, filePath, onProgress, onSuccess, onError) {
.pipe(fs.createWriteStream(filePath))
}
// 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写
/**
* 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写
*
* @param win win是所有窗口的引用
*/
function updateHandle (app, api, win, beforeQuit, quit, log) {
// // 更新前,删除本地安装包 ↓
// const updaterCacheDirName = 'dev-sidecar-updater'
@ -64,15 +70,115 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
}
}
logger.info('auto updater', autoUpdater.getFeedURL())
log.info('auto updater', autoUpdater.getFeedURL())
autoUpdater.autoDownload = false
let partPackagePath = null
// 检查更新
const releasesApiUrl = 'https://api.github.com/repos/docmirror/dev-sidecar/releases'
async function checkForUpdatesFromGitHub () {
log.info('DevSidecar.api.config.app.skipPreRelease:', DevSidecar.api.config.get().app.skipPreRelease)
request(releasesApiUrl, { headers: { 'User-Agent': 'DS/' + pkg.version } }, (error, response, body) => {
try {
if (error) {
log.error('检查更新失败:', error)
const errorMsg = '检查更新失败:' + error
win.webContents.send('update', { key: 'error', error: errorMsg })
return
}
if (response && response.statusCode === 200) {
if (body == null || body.length < 2) {
log.warn('检查更新失败github API返回数据为空:', body)
win.webContents.send('update', { key: 'error', error: '检查更新失败github 返回数据为空' })
return
}
// 尝试解析API响应内容
let data
try {
data = JSON.parse(body)
} catch (e) {
log.error(`检查更新失败github API返回数据格式不正确, url: ${releasesApiUrl}, body: ${body}`)
win.webContents.send('update', { key: 'error', error: '检查更新失败github API返回数据格式不正确' })
return
}
if (typeof data !== 'object' || data.length === undefined) {
win.webContents.send('update', { key: 'error', error: '检查更新失败github API返回数据格式不正确' })
return
}
log.info('github api返回的release数据', JSON.stringify(data, null, '\t'))
// 检查更新
for (let i = 0; i < data.length; i++) {
const versionData = data[i]
if (!versionData.assets || versionData.assets.length === 0) {
continue // 跳过空版本,即上传过安装包
}
if (DevSidecar.api.config.app.skipPreRelease && versionData.name.indexOf('Pre-release') >= 0) {
continue // 跳过预发布版本
}
// log.info('最近正式版本数据:', versionData)
let version = versionData.tag_name
if (version.indexOf('v') === 0) {
version = version.substring(1)
}
if (version !== pkg.version) {
log.info('检查更新-发现新版本:', version)
win.webContents.send('update', {
key: 'available',
value: {
version: versionData.tag_name,
releaseNotes: [
'请查看发布公告:' + versionData.html_url
]
}
})
} else {
log.info('检查更新-没有新版本,最新版本号为:', version)
win.webContents.send('update', { key: 'notAvailable' })
}
return // 只检查最近一个正式版本
}
log.info('检查更新-没有正式版本数据')
win.webContents.send('update', { key: 'notAvailable' })
} else {
log.error('检查更新失败, status:', response.statusCode, ', body:', body)
let bodyObj
try {
bodyObj = JSON.parse(body)
} catch (e) {
bodyObj = null
}
let message
if (response) {
message = '检查更新失败: ' + (bodyObj && bodyObj.message ? bodyObj.message : response.message) + ', code: ' + response.statusCode
} else {
message = '检查更新失败: ' + (bodyObj && bodyObj.message ? bodyObj.message : body)
}
win.webContents.send('update', { key: 'error', error: message })
}
} catch (e) {
log.error('检查更新失败:', e)
win.webContents.send('update', { key: 'error', error: '检查更新失败:' + e.message })
}
})
}
// 下载升级包
function downloadPart (app, value) {
const appPath = appPathUtil.getAppRootPath(app)
const fileDir = path.join(appPath, 'update')
logger.info('download dir', fileDir)
log.info('download dir', fileDir)
try {
fs.accessSync(fileDir, fs.constants.F_OK)
} catch (e) {
@ -85,7 +191,7 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
}, () => {
// 文件下载完成
win.webContents.send('update', { key: 'progress', value: 100 })
logger.info('升级包下载成功:', filePath)
log.info('升级包下载成功:', filePath)
partPackagePath = filePath
win.webContents.send('update', {
key: 'downloaded',
@ -168,8 +274,11 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
})
} else if (arg.key === 'checkForUpdate') {
// 执行自动更新检查
log.info('autoUpdater checkForUpdates')
autoUpdater.checkForUpdates()
log.info('autoUpdater checkForUpdates:', arg.fromUser)
// 调用 github API获取release数据来检查更新
// autoUpdater.checkForUpdates()
checkForUpdatesFromGitHub()
} else if (arg.key === 'downloadUpdate') {
// 下载新版本
log.info('autoUpdater downloadUpdate')
@ -177,7 +286,6 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
} else if (arg.key === 'downloadPart') {
// 下载增量更新版本
log.info('autoUpdater downloadPart')
// autoUpdater.downloadUpdate()
downloadPart(app, arg.value)
}
})

View File

@ -1,5 +1,5 @@
function install (app, api) {
const updateParams = app.$global.update = { fromUser: false, autoDownload: false, progress: 0, downloading: false, newVersion: false, isFullUpdate: true }
const updateParams = app.$global.update = { fromUser: false, autoDownload: false, progress: 0, checking: false, downloading: false, newVersion: false, isFullUpdate: true }
api.ipc.on('update', (event, message) => {
console.log('on message', event, message)
handleUpdateMessage(message, app)
@ -10,7 +10,10 @@ function install (app, api) {
if (fromUser != null) {
updateParams.fromUser = fromUser
}
api.ipc.send('update', { key: 'checkForUpdate' })
if (updateParams.fromUser) {
updateParams.checking = true
}
api.ipc.send('update', { key: 'checkForUpdate', fromUser })
},
downloadUpdate () {
api.ipc.send('update', { key: 'downloadUpdate' })
@ -27,8 +30,10 @@ function install (app, api) {
function handleUpdateMessage (message) {
const type = message.key
if (type === 'available') {
updateParams.checking = false
foundNewVersion(message.value)
} else if (type === 'notAvailable') {
updateParams.checking = false
noNewVersion()
} else if (type === 'downloaded') {
// 更新包已下载完成,让用户确认是否更新
@ -38,12 +43,14 @@ function install (app, api) {
} else if (type === 'progress') {
progressUpdate(message.value)
} else if (type === 'error') {
updateParams.checking = false
updateParams.downloading = false
const error = message.error
app.$message.error('Error: ' + (error == null ? '未知错误' : (error.stack || error).toString()))
app.$message.error((error == null ? '未知错误' : (error.stack || error).toString()))
}
}
function noNewVersion (value) {
function noNewVersion () {
updateParams.newVersion = false
if (updateParams.fromUser) {
app.$message.info('当前已经是最新版本')
@ -55,8 +62,10 @@ function install (app, api) {
}
function goManualUpdate (value) {
updateParams.newVersion = false
app.$confirm({
title: '暂不支持自动升级',
// title: '暂不支持自动升级',
title: '暂不提供自动升级',
cancelText: '取消',
okText: '确定',
content: h => {
@ -64,50 +73,49 @@ function install (app, api) {
api.ipc.openExternal('https://github.com/docmirror/dev-sidecar/releases')
}
return <div>
<div>请前往github项目release页面下载新版本手动安装</div>
<ol>
<li><a onClick={openGithubUrl}>Github release</a></li>
</ol>
<div>请前往 <a onClick={openGithubUrl}>github项目release页面</a> </div>
</div>
}
})
}
/**
* 是否小版本升级
* @param version1
* @param version2
*/
async function isSupportPartUpdate (value) {
const info = await api.info.get()
console.log('升级版本:', value.version)
console.log('增量更新最小版本:', value.partMiniVersion)
console.log('当前版本:', info.version)
if (!value.partPackage) {
return false
}
return !!(value.partMiniVersion && value.partMiniVersion < info.version)
}
// /**
// * 是否小版本升级
// * @param value
// */
// async function isSupportPartUpdate (value) {
// const info = await api.info.get()
// console.log('升级版本:', value.version)
// console.log('增量更新最小版本:', value.partMiniVersion)
// console.log('当前版本:', info.version)
// if (!value.partPackage) {
// return false
// }
// return !!(value.partMiniVersion && value.partMiniVersion < info.version)
// }
async function downloadNewVersion (value) {
const platform = await api.shell.getSystemPlatform()
console.log('download new version platform', platform)
if (platform === 'linux') {
goManualUpdate(app, value)
return
}
const partUpdate = await isSupportPartUpdate(value)
if (partUpdate) {
// 有增量更新
api.update.downloadPart(value)
} else {
if (platform === 'mac') {
goManualUpdate(value)
return
}
updateParams.downloading = true
api.update.downloadUpdate()
}
// 暂时取消自动更新功能
goManualUpdate(value)
// const platform = await api.shell.getSystemPlatform()
// console.log(`download new version: ${JSON.stringify(value)}, platform: ${platform}`)
// if (platform === 'linux') {
// goManualUpdate(value)
// return
// }
// const partUpdate = await isSupportPartUpdate(value)
// if (partUpdate) {
// // 有增量更新
// api.update.downloadPart(value)
// } else {
// if (platform === 'mac') {
// goManualUpdate(value)
// return
// }
// updateParams.downloading = true
// api.update.downloadUpdate()
// }
}
function foundNewVersion (value) {
updateParams.newVersion = true
@ -118,18 +126,23 @@ function install (app, api) {
downloadNewVersion(value)
return
}
console.log(value)
app.$confirm({
title: '发现新版本',
title: '发现新版本' + value.version,
cancelText: '暂不升级',
okText: '升级',
width: 600,
content: h => {
console.log(value)
if (value.releaseNotes) {
const notes = []
for (const note of value.releaseNotes) {
notes.push(<li>{note}</li>)
if (typeof value.releaseNotes === 'string') {
return <div><div>更新内容</div><div>{value.releaseNotes}</div></div>
} else {
const notes = []
for (const note of value.releaseNotes) {
notes.push(<li>{note}</li>)
}
return <div><div>更新内容</div><ol>{notes}</ol></div>
}
return <div><div>更新内容</div><ol>{notes}</ol></div>
}
},
onOk () {

View File

@ -7,10 +7,10 @@
<a-badge :count="_rootCaSetuped?0:1" dot>安装根证书</a-badge>
</a-button>
<a-button style="margin-right:10px" @click="doCheckUpdate(true)" :loading="update.downloading"
<a-button style="margin-right:10px" @click="doCheckUpdate(true)" :loading="update.downloading || update.checking"
:title="'当前版本:'+info.version">
<a-badge :count="update.newVersion?1:0" dot>
<span v-if="update.downloading">{{ update.progress }}%</span>{{ update.downloading ? '' : '' }}
<span v-if="update.downloading">{{ update.progress }}%</span>{{ update.downloading ? '' : ('' + (update.checking ? '' : '')) }}
</a-badge>
</a-button>
</span>
@ -161,7 +161,7 @@ export default {
setupCa: {
visible: false
},
update: { downloading: false, progress: 0, newVersion: false }
update: { checking: false, downloading: false, progress: 0, newVersion: false }
}
},
async created () {
@ -170,8 +170,8 @@ export default {
this.$set(this, 'status', this.$status)
this.switchBtns = this.createSwitchBtns()
this.$set(this, 'update', this.$global.update)
if (!this.update.autoChecked) {
this.update.autoChecked = true
if (!this.update.autoChecked && this.config.app.autoChecked) {
this.update.autoChecked = true //
this.doCheckUpdate(false)
}
this.$api.info.get().then(ret => {

View File

@ -52,6 +52,29 @@
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="自动检查更新" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-radio-group v-model="config.app.autoChecked" default-value="light" button-style="solid">
<a-radio-button :value="true">
开启
</a-radio-button>
<a-radio-button :value="false">
关闭
</a-radio-button>
</a-radio-group>
<div class="form-help">
开启自动检查更新后每次应用启动时会检查一次更新如有新版本则会弹出提示
</div>
</a-form-item>
<a-form-item v-if="config.app.autoChecked" label="忽略预发布版本" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-radio-group v-model="config.app.skipPreRelease" default-value="light" button-style="solid">
<a-radio-button :value="true">
忽略
</a-radio-button>
<a-radio-button :value="false">
不忽略
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="首页提示" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-radio-group v-model="config.app.showShutdownTip"
default-value="true" button-style="solid">