feat: add signed Windows launcher self-updates with SHA-256 verification

This commit is contained in:
2026-10-30 22:50:07 -07:00
parent 79dd5e7465
commit 3e2dbd312e
6 changed files with 150 additions and 4 deletions

23
main.js
View File

@@ -217,6 +217,7 @@ async function start() {
next[name] = names.includes(name) ? release.components[name].id : current[name];
}
patcher.saveCurrentVersion(release.version, release.sha256, next);
patcher.writeIntegrity(names);
} catch (err) {
try { patcher.rollback(); } catch (x) { debugLog(`Rollback failed: ${x.message}`); }
throw err;
@@ -264,6 +265,27 @@ async function start() {
return false;
};
if (release && release.installer && process.platform === 'win32' && process.env.VYLA_DEV !== '1') {
const appVersion = process.env.VYLA_APP_VERSION || '0.0.0';
const tried = updateClient.readCache().selfUpdate || {};
const recentlyTried = tried.version === release.installer.version && Date.now() - (tried.at || 0) < 24 * 60 * 60 * 1000;
if (isNewer(release.installer.version, appVersion) && !recentlyTried) {
try {
const dest = path.join(APPDATA_DIR, `vyla-installer-${normalizeVer(release.installer.version)}.exe`);
sendToTauri('status', { message: 'Downloading launcher update...' });
await downloadVerified('launcher', release.installer, dest);
updateClient.writeCache({ selfUpdate: { version: release.installer.version, at: Date.now() } });
sendToTauri('status', { message: 'Installing launcher update...' });
sendToTauri('self-update', { path: dest });
await sleep(30000);
sendToTauri('notice', { message: 'Launcher update did not start. Continuing on the current version.' });
} catch (err) {
debugLog(`Launcher update failed: ${err.message}`);
sendToTauri('notice', { message: `Launcher update failed: ${err.message}` });
}
}
}
if (release) {
const stale = patcher.staleComponents(release);
const broken = stale.some((name) => !patcher.componentIntact(name));
@@ -325,6 +347,7 @@ async function start() {
} catch (e) { }
patcher.saveCurrentVersion(ver, manSha, ids);
patcher.writeIntegrity(['frontend', 'player']);
} catch (e) {
try { patcher.rollback(); } catch (x) { }
throw e;