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

pull/311/head
王良 2024-04-23 17:10:37 +08:00 committed by GitHub
parent 178ac3c12b
commit 084e3dd886
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 215 additions and 58 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' url: 'https://gitee.com/wangliang181230/dev-sidecar/raw/docmirror/packages/core/src/config/remote_config.json5'
}, },
theme: 'light', // 主题light=亮色, dark=暗色 theme: 'light', // 主题light=亮色, dark=暗色
autoChecked: true, // 是否自动检查更新
skipPreRelease: true, // 是否忽略预发布版本
dock: { dock: {
hideWhenWinClose: false hideWhenWinClose: false
}, },

View File

@ -7,6 +7,9 @@ import fs from 'fs'
import AdmZip from 'adm-zip' import AdmZip from 'adm-zip'
import log from '../../utils/util.log' import log from '../../utils/util.log'
import appPathUtil from '../../utils/util.apppath' import appPathUtil from '../../utils/util.apppath'
import pkg from '../../../package.json'
import DevSidecar from '@docmirror/dev-sidecar'
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin' const isMac = process.platform === 'darwin'
const isLinux = process.platform === 'linux' const isLinux = process.platform === 'linux'
@ -72,6 +75,104 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
let partPackagePath = null let partPackagePath = null
// 检查更新
const releasesApiUrl = 'https://api.github.com/repos/docmirror/dev-sidecar/releases'
async function checkForUpdatesFromGitHub () {
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', action: 'checkForUpdate', 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', action: 'checkForUpdate', error: '检查更新失败github 返回数据为空' })
return
}
// 尝试解析API响应内容
let data
try {
data = JSON.parse(body)
} catch (e) {
log.error('检查更新失败github API返回数据格式不正确:', body)
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', error: '检查更新失败github API返回数据格式不正确' })
return
}
if (typeof data !== 'object' || data.length === undefined) {
log.error('检查更新失败github API返回数据不是数组:', body)
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', 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.get().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,
releaseNotes: '发布公告:' + (versionData.html_url || ('https://github.com/docmirror/dev-sidecar/releases/tag/' + versionData.tag_name))
}
})
} 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', action: 'checkForUpdate', error: message })
}
} catch (e) {
log.error('检查更新失败:', e)
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', error: '检查更新失败:' + e.message })
}
})
}
// 下载升级包
function downloadPart (app, value) { function downloadPart (app, value) {
const appPath = appPathUtil.getAppRootPath(app) const appPath = appPathUtil.getAppRootPath(app)
const fileDir = path.join(appPath, 'update') const fileDir = path.join(appPath, 'update')
@ -171,8 +272,11 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
}) })
} else if (arg.key === 'checkForUpdate') { } else if (arg.key === 'checkForUpdate') {
// 执行自动更新检查 // 执行自动更新检查
log.info('autoUpdater checkForUpdates') log.info('autoUpdater checkForUpdates:', arg.fromUser)
autoUpdater.checkForUpdates()
// 调用 github API获取release数据来检查更新
// autoUpdater.checkForUpdates()
checkForUpdatesFromGitHub()
} else if (arg.key === 'downloadUpdate') { } else if (arg.key === 'downloadUpdate') {
// 下载新版本 // 下载新版本
log.info('autoUpdater downloadUpdate') log.info('autoUpdater downloadUpdate')

View File

@ -1,5 +1,5 @@
function install (app, api) { 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) => { api.ipc.on('update', (event, message) => {
console.log('on message', event, message) console.log('on message', event, message)
handleUpdateMessage(message, app) handleUpdateMessage(message, app)
@ -10,6 +10,7 @@ function install (app, api) {
if (fromUser != null) { if (fromUser != null) {
updateParams.fromUser = fromUser updateParams.fromUser = fromUser
} }
updateParams.checking = true
api.ipc.send('update', { key: 'checkForUpdate', fromUser }) api.ipc.send('update', { key: 'checkForUpdate', fromUser })
}, },
downloadUpdate () { downloadUpdate () {
@ -27,8 +28,11 @@ function install (app, api) {
function handleUpdateMessage (message) { function handleUpdateMessage (message) {
const type = message.key const type = message.key
if (type === 'available') { if (type === 'available') {
updateParams.checking = false
updateParams.newVersionData = message.value
foundNewVersion(message.value) foundNewVersion(message.value)
} else if (type === 'notAvailable') { } else if (type === 'notAvailable') {
updateParams.checking = false
noNewVersion() noNewVersion()
} else if (type === 'downloaded') { } else if (type === 'downloaded') {
// 更新包已下载完成,让用户确认是否更新 // 更新包已下载完成,让用户确认是否更新
@ -38,9 +42,15 @@ function install (app, api) {
} else if (type === 'progress') { } else if (type === 'progress') {
progressUpdate(message.value) progressUpdate(message.value)
} else if (type === 'error') { } else if (type === 'error') {
updateParams.checking = false
updateParams.downloading = false updateParams.downloading = false
const error = message.error if (message.action === 'checkForUpdate' && updateParams.newVersionData) {
app.$message.error((error == null ? '未知错误' : (error.stack || error).toString())) // 如果检查更新报错了,但刚才成功拿到过一次数据,就拿之前的数据
foundNewVersion(updateParams.newVersionData)
} else {
const error = message.error
app.$message.error((error == null ? '未知错误' : (error.stack || error).toString()))
}
} }
} }
@ -55,55 +65,67 @@ function install (app, api) {
updateParams.progress = value updateParams.progress = value
} }
function openGithubUrl () {
api.ipc.openExternal('https://github.com/docmirror/dev-sidecar/releases')
}
function goManualUpdate (value) { function goManualUpdate (value) {
updateParams.newVersion = false updateParams.newVersion = false
app.$confirm({ app.$confirm({
title: '暂不支持自动升级', // title: '暂不支持自动升级',
title: '暂不提供自动升级',
cancelText: '取消', cancelText: '取消',
okText: '确定', okText: '打开链接',
width: 420,
content: h => { content: h => {
function openGithubUrl () { return <div>
api.ipc.openExternal('https://github.com/docmirror/dev-sidecar/releases') <div>请前往 <a onClick={openGithubUrl}>github项目release页面</a> </div>
} <div><a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a></div>
return <div>请前往 <a onClick={openGithubUrl}>github项目release页面</a> </div> </div>
},
onOk () {
openGithubUrl()
} }
}) })
} }
/** // /**
* 是否小版本升级 // * 是否小版本升级
* @param value // * @param value
*/ // */
async function isSupportPartUpdate (value) { // async function isSupportPartUpdate (value) {
const info = await api.info.get() // const info = await api.info.get()
console.log('升级版本:', value.version) // console.log('升级版本:', value.version)
console.log('增量更新最小版本:', value.partMiniVersion) // console.log('增量更新最小版本:', value.partMiniVersion)
console.log('当前版本:', info.version) // console.log('当前版本:', info.version)
if (!value.partPackage) { // if (!value.partPackage) {
return false // return false
} // }
return !!(value.partMiniVersion && value.partMiniVersion < info.version) // return !!(value.partMiniVersion && value.partMiniVersion < info.version)
} // }
async function downloadNewVersion (value) { async function downloadNewVersion (value) {
const platform = await api.shell.getSystemPlatform() // 暂时取消自动更新功能
console.log(`download new version: ${JSON.stringify(value)}, platform: ${platform}`) goManualUpdate(value)
if (platform === 'linux') {
goManualUpdate(value) // const platform = await api.shell.getSystemPlatform()
return // console.log(`download new version: ${JSON.stringify(value)}, platform: ${platform}`)
} // if (platform === 'linux') {
const partUpdate = await isSupportPartUpdate(value) // goManualUpdate(value)
if (partUpdate) { // return
// 有增量更新 // }
api.update.downloadPart(value) // const partUpdate = await isSupportPartUpdate(value)
} else { // if (partUpdate) {
if (platform === 'mac') { // // 有增量更新
goManualUpdate(value) // api.update.downloadPart(value)
return // } else {
} // if (platform === 'mac') {
updateParams.downloading = true // goManualUpdate(value)
api.update.downloadUpdate() // return
} // }
// updateParams.downloading = true
// api.update.downloadUpdate()
// }
} }
function foundNewVersion (value) { function foundNewVersion (value) {
updateParams.newVersion = true updateParams.newVersion = true
@ -116,13 +138,14 @@ function install (app, api) {
} }
console.log(value) console.log(value)
app.$confirm({ app.$confirm({
title: '发现新版本:' + value.version, title: '发现新版本:v' + value.version,
cancelText: '暂不升级', cancelText: '暂不升级',
okText: '升级', okText: '升级',
width: 710,
content: h => { content: h => {
if (value.releaseNotes) { if (value.releaseNotes) {
if (typeof value.releaseNotes === 'string') { if (typeof value.releaseNotes === 'string') {
return <div><div>更新内容</div><div>{value.releaseNotes}</div></div> return <div>{value.releaseNotes}</div>
} else { } else {
const notes = [] const notes = []
for (const note of value.releaseNotes) { for (const note of value.releaseNotes) {
@ -144,24 +167,26 @@ function install (app, api) {
function newUpdateIsReady (value) { function newUpdateIsReady (value) {
updateParams.downloading = false updateParams.downloading = false
console.log(value)
app.$confirm({ app.$confirm({
title: `新版本(v${value.version})已准备好,是否立即升级?`, title: `新版本(v${value.version})已准备好是否立即升级?`,
cancelText: '暂不升级', cancelText: '暂不升级',
okText: '立即升级', okText: '立即升级',
content: h => { content: h => {
console.log(value)
if (value.releaseNotes) { if (value.releaseNotes) {
const notes = [] if (typeof value.releaseNotes === 'string') {
for (const note of value.releaseNotes) { return <div>{value.releaseNotes}</div>
notes.push(<li>{note}</li>) } 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 () { onOk () {
api.update.doUpdateNow() api.update.doUpdateNow()
},
onCancel () {
} }
}) })
} }

View File

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

View File

@ -52,6 +52,32 @@
</a-radio-button> </a-radio-button>
</a-radio-group> </a-radio-group>
</a-form-item> </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>
<div class="form-help">
预发布版本为版本号带有 Pre-release 的版本
</div>
</a-radio-group>
</a-form-item>
<a-form-item label="首页提示" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form-item label="首页提示" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-radio-group v-model="config.app.showShutdownTip" <a-radio-group v-model="config.app.showShutdownTip"
default-value="true" button-style="solid"> default-value="true" button-style="solid">