Browse Source
添加热更新功能,包括下载更新包、解压、标记更新状态及触发更新流程。优化命令处理逻辑,支持返回命令是否存在及执行结果。更新本地化配置,添加热更新相关文案。删除不再使用的热更新生成脚本。feat/icon
14 changed files with 237 additions and 146 deletions
@ -1,8 +1,8 @@ |
|||
import { broadcast } from "main/utils" |
|||
import { AllKeys } from "../common" |
|||
|
|||
function emitProgress(...argu) { |
|||
broadcast<AllKeys>("progress", ...argu) |
|||
function emitHotUpdateReady(...argu) { |
|||
broadcast<AllKeys>("hot-update-ready", ...argu) |
|||
} |
|||
|
|||
export { emitProgress } |
|||
export { emitHotUpdateReady } |
|||
|
@ -0,0 +1,10 @@ |
|||
import { inject } from "inversify" |
|||
import Updater from "main/modules/updater" |
|||
|
|||
export default class BasicCommand { |
|||
constructor(@inject(Updater) private _Updater: Updater) {} |
|||
|
|||
async triggerHotUpdate() { |
|||
await this._Updater.triggerHotUpdate() |
|||
} |
|||
} |
@ -1,62 +0,0 @@ |
|||
import { spawn } from "node:child_process" |
|||
import fs from "node:fs" |
|||
import path from "node:path" |
|||
import os from "node:os" |
|||
import { app } from "electron" |
|||
|
|||
function getUpdateScriptTemplate() { |
|||
return process.platform === "win32" |
|||
? ` |
|||
@echo off |
|||
timeout /t 2 |
|||
taskkill /IM "{{EXE_NAME}}" /F |
|||
xcopy /Y /E "{{UPDATE_DIR}}\\*" "{{APP_PATH}}" |
|||
start "" "{{EXE_PATH}}" |
|||
` |
|||
: ` |
|||
#!/bin/bash |
|||
sleep 2 |
|||
pkill -f "{{EXE_NAME}}" |
|||
cp -Rf "{{UPDATE_DIR}}/*" "{{APP_PATH}}/" |
|||
open "{{EXE_PATH}}" |
|||
` |
|||
} |
|||
|
|||
function generateUpdateScript() { |
|||
const scriptContent = getUpdateScriptTemplate() |
|||
.replace(/{{APP_PATH}}/g, process.platform === "win32" ? "%APP_PATH%" : "$APP_PATH") |
|||
.replace(/{{UPDATE_DIR}}/g, process.platform === "win32" ? "%UPDATE_DIR%" : "$UPDATE_DIR") |
|||
.replace(/{{EXE_PATH}}/g, process.platform === "win32" ? "%EXE_PATH%" : "$EXE_PATH") |
|||
.replace(/{{EXE_NAME}}/g, process.platform === "win32" ? "%EXE_NAME%" : "$EXE_NAME") |
|||
|
|||
const scriptPath = path.join(os.tmpdir(), `update.${process.platform === "win32" ? "bat" : "sh"}`) |
|||
fs.writeFileSync(scriptPath, scriptContent) |
|||
return scriptPath |
|||
} |
|||
|
|||
app.on("will-quit", event => { |
|||
event.preventDefault() |
|||
|
|||
// 假设已下载更新到临时目录
|
|||
const updateTempDir = path.join(os.tmpdir(), "app-update") |
|||
const appPath = app.getAppPath() |
|||
const appExePath = process.execPath |
|||
|
|||
// 生成动态脚本
|
|||
const scriptPath = generateUpdateScript() |
|||
|
|||
fs.chmodSync(scriptPath, 0o755) |
|||
|
|||
// 执行脚本
|
|||
const child = spawn(scriptPath, [], { |
|||
detached: true, |
|||
shell: true, |
|||
env: { |
|||
APP_PATH: appPath, |
|||
UPDATE_DIR: updateTempDir, |
|||
EXE_PATH: appExePath, |
|||
}, |
|||
}) |
|||
child.unref() |
|||
app.exit() |
|||
}) |
@ -0,0 +1,118 @@ |
|||
import { spawn } from "node:child_process" |
|||
import fs from "node:fs" |
|||
import path from "node:path" |
|||
import os from "node:os" |
|||
import { app } from "electron" |
|||
import extract from "extract-zip" |
|||
import { emitHotUpdateReady } from "common/event/Update/main" |
|||
|
|||
import _debug from "debug" |
|||
const debug = _debug("app:hot-updater") |
|||
|
|||
function getUpdateScriptTemplate() { |
|||
return process.platform === "win32" |
|||
? ` |
|||
@echo off |
|||
timeout /t 2 |
|||
taskkill /IM "{{EXE_NAME}}" /F |
|||
xcopy /Y /E "{{UPDATE_DIR}}\\*" "{{APP_PATH}}" |
|||
start "" "{{EXE_PATH}}" |
|||
` |
|||
: ` |
|||
#!/bin/bash |
|||
sleep 2 |
|||
pkill -f "{{EXE_NAME}}" |
|||
cp -Rf "{{UPDATE_DIR}}/*" "{{APP_PATH}}/" |
|||
open "{{EXE_PATH}}" |
|||
` |
|||
} |
|||
|
|||
function generateUpdateScript() { |
|||
const scriptContent = getUpdateScriptTemplate() |
|||
.replace(/{{APP_PATH}}/g, process.platform === "win32" ? "%APP_PATH%" : "$APP_PATH") |
|||
.replace(/{{UPDATE_DIR}}/g, process.platform === "win32" ? "%UPDATE_DIR%" : "$UPDATE_DIR") |
|||
.replace(/{{EXE_PATH}}/g, process.platform === "win32" ? "%EXE_PATH%" : "$EXE_PATH") |
|||
.replace(/{{EXE_NAME}}/g, process.platform === "win32" ? "%EXE_NAME%" : "$EXE_NAME") |
|||
|
|||
const scriptPath = path.join(os.tmpdir(), `update.${process.platform === "win32" ? "bat" : "sh"}`) |
|||
fs.writeFileSync(scriptPath, scriptContent) |
|||
return scriptPath |
|||
} |
|||
// 标记是否需要热更新
|
|||
let shouldPerformHotUpdate = false |
|||
let isReadyUpdate = false |
|||
// 更新临时目录路径
|
|||
// 使用应用名称和随机字符串创建唯一的临时目录
|
|||
const updateTempDirPath = path.join(os.tmpdir(), `${app.getName()}-update-${Math.random().toString(36).substring(2, 15)}`) |
|||
app.once("will-quit", event => { |
|||
if (!shouldPerformHotUpdate) return |
|||
event.preventDefault() |
|||
const appPath = app.getAppPath() |
|||
const appExePath = process.execPath |
|||
const exeName = path.basename(appExePath) |
|||
// 生成动态脚本
|
|||
const scriptPath = generateUpdateScript() |
|||
|
|||
fs.chmodSync(scriptPath, 0o755) |
|||
|
|||
// 执行脚本
|
|||
const child = spawn(scriptPath, [], { |
|||
detached: true, |
|||
shell: true, |
|||
env: { |
|||
APP_PATH: appPath, |
|||
UPDATE_DIR: updateTempDirPath, |
|||
EXE_PATH: appExePath, |
|||
EXE_NAME: exeName, |
|||
}, |
|||
}) |
|||
child.unref() |
|||
app.exit() |
|||
}) |
|||
|
|||
// 下载热更新包
|
|||
export async function fetchHotUpdatePackage(updatePackageUrl: string = "https://example.com/updates/latest.zip") { |
|||
if (isReadyUpdate) return |
|||
|
|||
// 清除临时目录
|
|||
clearUpdateTempDir() |
|||
// 创建临时目录
|
|||
if (!fs.existsSync(updateTempDirPath)) { |
|||
fs.mkdirSync(updateTempDirPath, { recursive: true }) |
|||
} |
|||
|
|||
// 下载文件的本地保存路径
|
|||
const downloadPath = path.join(updateTempDirPath, "update.zip") |
|||
|
|||
try { |
|||
// 使用 fetch 下载更新包
|
|||
const response = await fetch(updatePackageUrl) |
|||
if (!response.ok) { |
|||
throw new Error(`下载失败: ${response.status} ${response.statusText}`) |
|||
} |
|||
|
|||
// 将下载内容写入文件
|
|||
const arrayBuffer = await response.arrayBuffer() |
|||
fs.writeFileSync(downloadPath, Buffer.from(arrayBuffer)) |
|||
|
|||
// 解压更新包
|
|||
await extract(downloadPath, { dir: updateTempDirPath }) |
|||
|
|||
// 删除下载的zip文件
|
|||
fs.unlinkSync(downloadPath) |
|||
isReadyUpdate = true |
|||
emitHotUpdateReady() |
|||
} catch (error) { |
|||
debug("热更新包下载失败:", error) |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
function clearUpdateTempDir() { |
|||
if (!fs.existsSync(updateTempDirPath)) return |
|||
fs.rmSync(updateTempDirPath, { recursive: true }) |
|||
} |
|||
|
|||
export function flagNeedUpdate() { |
|||
shouldPerformHotUpdate = true |
|||
} |
Loading…
Reference in new issue