fix(desktop): drop orphaned Squirrel uninstall entry (#2885)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-21 20:03:33 +02:00
1 parent 70e1ce682a
commit 242ed3a934
3 files changed
+95 -1

No files matched your search

+2 -1
View File
@@ -6,7 +6,8 @@ export const DESKTOP_APP_NAME = BUILD_CHANNEL === 'canary' ? 'Fluxer Canary' : '
export const MACOS_BUNDLE_ID = BUILD_CHANNEL === 'canary' ? 'app.fluxer.canary' : 'app.fluxer'; export const MACOS_BUNDLE_ID = BUILD_CHANNEL === 'canary' ? 'app.fluxer.canary' : 'app.fluxer';
export const LINUX_DESKTOP_ENTRY_ID = BUILD_CHANNEL === 'canary' ? 'fluxer-canary' : 'fluxer'; export const LINUX_DESKTOP_ENTRY_ID = BUILD_CHANNEL === 'canary' ? 'fluxer-canary' : 'fluxer';
export const WINDOWS_SHORTCUT_AUTHOR = 'Fluxer Platform AB'; export const WINDOWS_SHORTCUT_AUTHOR = 'Fluxer Platform AB';
const WINDOWS_VELOPACK_ID = BUILD_CHANNEL === 'canary' ? 'fluxer_desktop_canary' : 'fluxer_desktop'; export const WINDOWS_VELOPACK_ID = BUILD_CHANNEL === 'canary' ? 'fluxer_desktop_canary' : 'fluxer_desktop';
export const WINDOWS_LEGACY_SQUIRREL_ID = 'fluxer_app';
export const WINDOWS_APP_USER_MODEL_ID = BUILD_CHANNEL === 'canary' ? 'Fluxer.Fluxer.Canary' : 'Fluxer.Fluxer'; export const WINDOWS_APP_USER_MODEL_ID = BUILD_CHANNEL === 'canary' ? 'Fluxer.Fluxer.Canary' : 'Fluxer.Fluxer';
export const WINDOWS_LEGACY_APP_USER_MODEL_IDS = [`velopack.${WINDOWS_VELOPACK_ID}`]; export const WINDOWS_LEGACY_APP_USER_MODEL_IDS = [`velopack.${WINDOWS_VELOPACK_ID}`];
export const WINDOWS_TOAST_ACTIVATOR_CLSID = export const WINDOWS_TOAST_ACTIVATOR_CLSID =
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {execFile} from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import {WINDOWS_LEGACY_SQUIRREL_ID, WINDOWS_VELOPACK_ID} from '@electron/common/DesktopIdentity';
import log from 'electron-log';
const UNINSTALL_REGISTRY_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall';
interface RegResult {
status: number;
stdout: string;
}
function runReg(args: Array<string>): Promise<RegResult> {
return new Promise((resolve, reject) => {
execFile('reg.exe', args, {encoding: 'utf8', windowsHide: true}, (error, stdout) => {
if (!error) {
resolve({status: 0, stdout});
return;
}
if (typeof error.code === 'number') {
resolve({status: error.code, stdout});
return;
}
reject(error);
});
});
}
function normalizeWindowsPath(target: string): string {
return path.resolve(target).replace(/\\+$/, '').toLowerCase();
}
function isRunningFromMigratedSquirrelRoot(): boolean {
const localAppData = process.env.LOCALAPPDATA;
if (!localAppData) return false;
const currentDir = path.dirname(process.execPath);
if (path.basename(currentDir).toLowerCase() !== 'current') return false;
const rootAppDir = path.dirname(currentDir);
if (normalizeWindowsPath(rootAppDir) !== normalizeWindowsPath(path.join(localAppData, WINDOWS_LEGACY_SQUIRREL_ID))) {
return false;
}
return fs.existsSync(path.join(rootAppDir, 'Update.exe'));
}
function uninstallKey(appId: string): string {
return `${UNINSTALL_REGISTRY_KEY}\\${appId}`;
}
function parseInstallLocation(stdout: string): string | null {
const match = stdout.match(/^\s*InstallLocation\s+REG_(?:EXPAND_)?SZ\s+(.*?)\s*$/im);
return match ? match[1] : null;
}
async function velopackEntryDescribesLegacyRoot(): Promise<boolean> {
const {status, stdout} = await runReg(['query', uninstallKey(WINDOWS_VELOPACK_ID), '/v', 'InstallLocation']);
if (status !== 0) return false;
const installLocation = parseInstallLocation(stdout);
if (!installLocation) return false;
return path.basename(installLocation.replace(/\\+$/, '')).toLowerCase() === WINDOWS_LEGACY_SQUIRREL_ID;
}
async function removeLegacySquirrelUninstallEntryAsync(): Promise<void> {
const legacyKey = uninstallKey(WINDOWS_LEGACY_SQUIRREL_ID);
const legacyEntry = await runReg(['query', legacyKey]);
if (legacyEntry.status !== 0) return;
if (!(await velopackEntryDescribesLegacyRoot())) {
log.info('[LegacyUninstallEntry] Keeping the Squirrel uninstall entry, no Velopack entry describes this install');
return;
}
const removal = await runReg(['delete', legacyKey, '/f']);
if (removal.status !== 0) {
log.warn('[LegacyUninstallEntry] Failed to remove the Squirrel uninstall entry', {status: removal.status});
return;
}
log.info('[LegacyUninstallEntry] Removed the Squirrel uninstall entry', {key: legacyKey});
}
export function removeLegacySquirrelUninstallEntry(): void {
if (process.platform !== 'win32') return;
if (!isRunningFromMigratedSquirrelRoot()) return;
removeLegacySquirrelUninstallEntryAsync().catch((error: unknown) => {
log.warn('[LegacyUninstallEntry] Failed to clean up the Squirrel uninstall entry', error);
});
}
+6
View File
@@ -75,6 +75,7 @@ import {
setQuitting, setQuitting,
showWindow, showWindow,
} from '@electron/main/Window'; } from '@electron/main/Window';
import {removeLegacySquirrelUninstallEntry} from '@electron/main/WindowsLegacyUninstallEntry';
import {removeFluxerVulkanLayerRegistrations} from '@electron/main/WindowsVulkanLayerCleanup'; import {removeFluxerVulkanLayerRegistrations} from '@electron/main/WindowsVulkanLayerCleanup';
import {app, dialog, netLog} from 'electron'; import {app, dialog, netLog} from 'electron';
import log from 'electron-log'; import log from 'electron-log';
@@ -361,6 +362,11 @@ if (launchConfigurationError) {
} catch (error: unknown) { } catch (error: unknown) {
log.error('[Init] Failed to remove stale Vulkan layer registrations:', error); log.error('[Init] Failed to remove stale Vulkan layer registrations:', error);
} }
try {
runStartupPhase('legacy-uninstall-entry-cleanup', removeLegacySquirrelUninstallEntry);
} catch (error: unknown) {
log.error('[Init] Failed to remove the legacy Squirrel uninstall entry:', error);
}
try { try {
runStartupPhase('native-screen-capture-handlers', registerNativeScreenCaptureHandlers); runStartupPhase('native-screen-capture-handlers', registerNativeScreenCaptureHandlers);
} catch (error: unknown) { } catch (error: unknown) {