feat: add 2-stage update system (asar swap + full installer)
This commit is contained in:
+72
-30
@@ -1,6 +1,6 @@
|
||||
import { app, BrowserWindow, Menu, clipboard, ipcMain, safeStorage, session, shell } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { existsSync, mkdirSync, promises as fsp } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { existsSync, mkdirSync, unlinkSync, promises as fsp } from 'fs';
|
||||
import { get as httpsGet } from 'https';
|
||||
import { execFile } from 'child_process';
|
||||
import * as os from 'os';
|
||||
@@ -10,7 +10,7 @@ import { initSwapperProtocol, registerSwapperFileProtocol, ResourceSwapper } fro
|
||||
import { UserscriptManager } from './userscripts';
|
||||
import { ALL_CLIENT_CSS } from './client-ui';
|
||||
import { electronLog, getLogPath, closeLogStreams } from './logger';
|
||||
import { checkForUpdate, downloadUpdate, installUpdate } from './updater';
|
||||
import { checkForUpdate, downloadUpdate, installUpdate, applyMinorUpdate } from './updater';
|
||||
import { showUpdateWindow } from './update-window';
|
||||
import { DiscordRPC } from './discord-rpc';
|
||||
import { listThemes, getThemeCSS, listLoadingThemes, getLoadingScreenCSS } from './css-themes';
|
||||
@@ -164,51 +164,93 @@ function saveWindowState(win: BrowserWindow): void {
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
electronLog.log('[KCC] App ready');
|
||||
electronLog.log('[KCC] Minor update test — asar swap successful');
|
||||
|
||||
// ── Auto-update check (mandatory, Windows NSIS install only) ──
|
||||
// ── Auto-update check (2-stage: minor asar swap or major installer) ──
|
||||
const isPortable = !!process.env.PORTABLE_EXECUTABLE_DIR;
|
||||
const isAppImage = !!process.env.APPIMAGE;
|
||||
const isDev = !app.isPackaged;
|
||||
if (isDev || process.platform !== 'win32' || isPortable || isAppImage) {
|
||||
electronLog.log('[KCC] Skipping auto-update (portable or non-Windows)');
|
||||
const canMajorUpdate = process.platform === 'win32' && !isPortable;
|
||||
const canMinorUpdate = !isPortable && !isAppImage;
|
||||
|
||||
if (isDev || (!canMajorUpdate && !canMinorUpdate)) {
|
||||
electronLog.log('[KCC] Skipping auto-update');
|
||||
} else {
|
||||
// Clean up stale pending asar from a previous failed swap
|
||||
const resourcesDir = join(dirname(app.getPath('exe')), 'resources');
|
||||
const stalePending = join(resourcesDir, 'app-pending.asar');
|
||||
if (existsSync(stalePending)) {
|
||||
try { unlinkSync(stalePending); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
try {
|
||||
electronLog.log('[KCC] Checking for updates...');
|
||||
const update = await checkForUpdate(appVersion);
|
||||
if (update) {
|
||||
electronLog.log(`[KCC] Update available: v${update.version}`);
|
||||
electronLog.log(`[KCC] Update available: v${update.version} (${update.updateType})`);
|
||||
const { window: updateWin, sendProgress } = showUpdateWindow();
|
||||
sendProgress(`Update available (v${update.version})`, 0);
|
||||
|
||||
const tempDir = join(app.getPath('temp'), 'kcc-update');
|
||||
if (!existsSync(tempDir)) mkdirSync(tempDir, { recursive: true });
|
||||
const installerPath = join(tempDir, `KCC-${update.version}-Setup.exe`);
|
||||
let cancelled = false;
|
||||
updateWin.on('closed', () => { cancelled = true; });
|
||||
|
||||
let cancelled = false;
|
||||
updateWin.on('closed', () => { cancelled = true; });
|
||||
if (update.updateType === 'minor' && canMinorUpdate) {
|
||||
// Minor update: download app.asar, swap via external script, restart
|
||||
sendProgress(`Patch available (v${update.version})`, 0);
|
||||
const pendingPath = join(resourcesDir, 'app-pending.asar');
|
||||
|
||||
try {
|
||||
await downloadUpdate(update.downloadUrl, installerPath, (pct) => {
|
||||
if (!cancelled && !updateWin.isDestroyed()) {
|
||||
sendProgress(`Downloading update... ${pct}%`, pct);
|
||||
try {
|
||||
await downloadUpdate(update.downloadUrl, pendingPath, (pct) => {
|
||||
if (!cancelled && !updateWin.isDestroyed()) {
|
||||
sendProgress(`Downloading patch... ${pct}%`, pct);
|
||||
}
|
||||
}, update.sha256 || undefined);
|
||||
|
||||
if (!cancelled) {
|
||||
sendProgress('Applying patch...', 100);
|
||||
applyMinorUpdate(pendingPath);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
electronLog.error('[KCC] Patch download failed:', err);
|
||||
// Clean up failed download
|
||||
if (existsSync(pendingPath)) {
|
||||
try { unlinkSync(pendingPath); } catch { /* ignore */ }
|
||||
}
|
||||
if (!updateWin.isDestroyed()) updateWin.close();
|
||||
}
|
||||
}, update.sha256);
|
||||
} else if (update.updateType === 'major' && canMajorUpdate) {
|
||||
// Major update: download Setup.exe, run installer
|
||||
sendProgress(`Update available (v${update.version})`, 0);
|
||||
const tempDir = join(app.getPath('temp'), 'kcc-update');
|
||||
if (!existsSync(tempDir)) mkdirSync(tempDir, { recursive: true });
|
||||
const installerPath = join(tempDir, `KCC-${update.version}-Setup.exe`);
|
||||
|
||||
if (!cancelled) {
|
||||
sendProgress('Installing update...', 100);
|
||||
installUpdate(installerPath);
|
||||
return; // app.quit() called by installUpdate
|
||||
try {
|
||||
await downloadUpdate(update.downloadUrl, installerPath, (pct) => {
|
||||
if (!cancelled && !updateWin.isDestroyed()) {
|
||||
sendProgress(`Downloading update... ${pct}%`, pct);
|
||||
}
|
||||
}, update.sha256 || undefined);
|
||||
|
||||
if (!cancelled) {
|
||||
sendProgress('Installing update...', 100);
|
||||
installUpdate(installerPath);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
electronLog.error('[KCC] Update download failed:', err);
|
||||
if (!updateWin.isDestroyed()) updateWin.close();
|
||||
}
|
||||
} else {
|
||||
electronLog.log('[KCC] Update available but cannot auto-install on this platform');
|
||||
if (!updateWin.isDestroyed()) updateWin.close();
|
||||
}
|
||||
} catch (err) {
|
||||
electronLog.error('[KCC] Update download failed:', err);
|
||||
if (!updateWin.isDestroyed()) updateWin.close();
|
||||
} else {
|
||||
electronLog.log('[KCC] No updates available');
|
||||
}
|
||||
} else {
|
||||
electronLog.log('[KCC] No updates available');
|
||||
} catch (err) {
|
||||
electronLog.error('[KCC] Update check failed:', err);
|
||||
}
|
||||
} catch (err) {
|
||||
electronLog.error('[KCC] Update check failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
await launchApp();
|
||||
|
||||
Reference in New Issue
Block a user