mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-17 11:07:11 +08:00
fix(desktop): add updater logging + allow prerelease
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "build/entitlements.mac.plist",
|
||||
"notarize": true,
|
||||
"notarize": false,
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
// electron-updater is CommonJS: its members live on module.exports, with no
|
||||
// meaningful default export. Under module=commonjs + esModuleInterop, a named
|
||||
// import compiles to `electron_updater_1.autoUpdater` and resolves correctly,
|
||||
@@ -17,16 +19,59 @@ export type UpdateStatus =
|
||||
|
||||
let getWindow: () => BrowserWindow | null = () => null
|
||||
|
||||
// Persist update logs to a file so a user hitting a silent "spinner never
|
||||
// resolves" can just send us userData/logs/updater.log. We can't rely on a
|
||||
// logging dep, so this is a tiny append-only writer, plus console for the
|
||||
// in-app log view / terminal.
|
||||
let logFile: string | null = null
|
||||
|
||||
function initLogFile() {
|
||||
try {
|
||||
const dir = path.join(app.getPath('userData'), 'logs')
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
logFile = path.join(dir, 'updater.log')
|
||||
} catch {
|
||||
logFile = null
|
||||
}
|
||||
}
|
||||
|
||||
function log(...parts: unknown[]) {
|
||||
const line = `[${new Date().toISOString()}] [updater] ${parts
|
||||
.map((p) => (typeof p === 'string' ? p : safeStringify(p)))
|
||||
.join(' ')}`
|
||||
// Console: shows up in the terminal (dev) and the packaged app's stdout.
|
||||
console.log(line)
|
||||
if (logFile) {
|
||||
try {
|
||||
fs.appendFileSync(logFile, line + '\n')
|
||||
} catch {
|
||||
// ignore disk errors — logging must never break the updater
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function safeStringify(v: unknown): string {
|
||||
try {
|
||||
return JSON.stringify(v)
|
||||
} catch {
|
||||
return String(v)
|
||||
}
|
||||
}
|
||||
|
||||
function send(status: UpdateStatus) {
|
||||
getWindow()?.webContents.send('update-status', status)
|
||||
}
|
||||
|
||||
export function initUpdater(windowGetter: () => BrowserWindow | null): void {
|
||||
getWindow = windowGetter
|
||||
initLogFile()
|
||||
|
||||
log(`init: appVersion=${app.getVersion()} packaged=${app.isPackaged} logFile=${logFile ?? '<none>'}`)
|
||||
|
||||
// In dev (not packaged) there's no update feed; skip wiring entirely so
|
||||
// electron-updater doesn't throw on the missing app-update.yml.
|
||||
if (!app.isPackaged) {
|
||||
log('not packaged — updater wiring skipped')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,21 +79,51 @@ export function initUpdater(windowGetter: () => BrowserWindow | null): void {
|
||||
// download, rather than pulling bytes silently in the background.
|
||||
autoUpdater.autoDownload = false
|
||||
autoUpdater.autoInstallOnAppQuit = true
|
||||
// The desktop channel ships pre-release-tagged builds (e.g. 0.0.8-test), so a
|
||||
// current version like 0.0.7-test must be allowed to compare against, and be
|
||||
// offered, other pre-release versions. Without this electron-updater's semver
|
||||
// compare can silently skip pre-releases and neither "available" nor
|
||||
// "not-available" fires — the UI just spins forever.
|
||||
autoUpdater.allowPrerelease = true
|
||||
autoUpdater.allowDowngrade = false
|
||||
// Route electron-updater's own internal logging to our file too, so we
|
||||
// capture the feed URL, parsed versions and any stack traces it logs.
|
||||
autoUpdater.logger = {
|
||||
info: (m: unknown) => log('eu-info:', m),
|
||||
warn: (m: unknown) => log('eu-warn:', m),
|
||||
error: (m: unknown) => log('eu-error:', m),
|
||||
debug: (m: unknown) => log('eu-debug:', m),
|
||||
} as unknown as typeof autoUpdater.logger
|
||||
|
||||
autoUpdater.on('checking-for-update', () => send({ state: 'checking' }))
|
||||
autoUpdater.on('update-available', (info) =>
|
||||
send({ state: 'available', version: info.version, notes: typeof info.releaseNotes === 'string' ? info.releaseNotes : undefined })
|
||||
)
|
||||
autoUpdater.on('update-not-available', () => send({ state: 'not-available' }))
|
||||
autoUpdater.on('download-progress', (p) =>
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
log(`checking-for-update: current=${app.getVersion()}`)
|
||||
send({ state: 'checking' })
|
||||
})
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
log(`update-available: current=${app.getVersion()} remote=${info.version} -> update needed`)
|
||||
send({
|
||||
state: 'available',
|
||||
version: info.version,
|
||||
notes: typeof info.releaseNotes === 'string' ? info.releaseNotes : undefined,
|
||||
})
|
||||
})
|
||||
autoUpdater.on('update-not-available', (info) => {
|
||||
log(`update-not-available: current=${app.getVersion()} remote=${info?.version ?? '<unknown>'} -> up to date`)
|
||||
send({ state: 'not-available' })
|
||||
})
|
||||
autoUpdater.on('download-progress', (p) => {
|
||||
log(`download-progress: ${Math.round(p.percent)}% (${p.transferred}/${p.total} bytes, ${Math.round(p.bytesPerSecond / 1024)} KB/s)`)
|
||||
send({ state: 'downloading', percent: Math.round(p.percent) })
|
||||
)
|
||||
autoUpdater.on('update-downloaded', (info) =>
|
||||
})
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
log(`update-downloaded: version=${info.version} -> ready to install`)
|
||||
send({ state: 'downloaded', version: info.version })
|
||||
)
|
||||
autoUpdater.on('error', (err) =>
|
||||
send({ state: 'error', message: err == null ? 'unknown' : (err.message || String(err)) })
|
||||
)
|
||||
})
|
||||
autoUpdater.on('error', (err) => {
|
||||
const message = err == null ? 'unknown' : err.message || String(err)
|
||||
log(`error: ${message}`, err instanceof Error && err.stack ? err.stack : '')
|
||||
send({ state: 'error', message })
|
||||
})
|
||||
}
|
||||
|
||||
// Silent check shortly after launch. When not packaged there's no update feed,
|
||||
@@ -56,23 +131,31 @@ export function initUpdater(windowGetter: () => BrowserWindow | null): void {
|
||||
// reply "not-available" so the menu can show "up to date".
|
||||
export function checkForUpdates(): void {
|
||||
if (!app.isPackaged) {
|
||||
log('checkForUpdates: not packaged, replying not-available')
|
||||
send({ state: 'not-available' })
|
||||
return
|
||||
}
|
||||
log(`checkForUpdates: requesting feed, current=${app.getVersion()}`)
|
||||
autoUpdater.checkForUpdates().catch((err) => {
|
||||
send({ state: 'error', message: err?.message || String(err) })
|
||||
const message = err?.message || String(err)
|
||||
log(`checkForUpdates: request failed: ${message}`, err instanceof Error && err.stack ? err.stack : '')
|
||||
send({ state: 'error', message })
|
||||
})
|
||||
}
|
||||
|
||||
export function startDownload(): void {
|
||||
if (!app.isPackaged) return
|
||||
log('startDownload: user requested download')
|
||||
autoUpdater.downloadUpdate().catch((err) => {
|
||||
send({ state: 'error', message: err?.message || String(err) })
|
||||
const message = err?.message || String(err)
|
||||
log(`startDownload: failed: ${message}`, err instanceof Error && err.stack ? err.stack : '')
|
||||
send({ state: 'error', message })
|
||||
})
|
||||
}
|
||||
|
||||
export function quitAndInstall(): void {
|
||||
if (!app.isPackaged) return
|
||||
log('quitAndInstall: relaunching to install update')
|
||||
// isSilent=false (show installer), isForceRunAfter=true (relaunch after).
|
||||
autoUpdater.quitAndInstall(false, true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user