mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(desktop): drop orphaned Squirrel uninstall entry (#2885)
This commit is contained in:
1 parent
70e1ce682a
commit
242ed3a934
3 files changed
+95
-1
No files matched your search
@@ -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 LINUX_DESKTOP_ENTRY_ID = BUILD_CHANNEL === 'canary' ? 'fluxer-canary' : 'fluxer';
|
||||
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_LEGACY_APP_USER_MODEL_IDS = [`velopack.${WINDOWS_VELOPACK_ID}`];
|
||||
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);
|
||||
});
|
||||
}
|
||||
@@ -75,6 +75,7 @@ import {
|
||||
setQuitting,
|
||||
showWindow,
|
||||
} from '@electron/main/Window';
|
||||
import {removeLegacySquirrelUninstallEntry} from '@electron/main/WindowsLegacyUninstallEntry';
|
||||
import {removeFluxerVulkanLayerRegistrations} from '@electron/main/WindowsVulkanLayerCleanup';
|
||||
import {app, dialog, netLog} from 'electron';
|
||||
import log from 'electron-log';
|
||||
@@ -361,6 +362,11 @@ if (launchConfigurationError) {
|
||||
} catch (error: unknown) {
|
||||
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 {
|
||||
runStartupPhase('native-screen-capture-handlers', registerNativeScreenCaptureHandlers);
|
||||
} catch (error: unknown) {
|
||||
|
||||
Reference in new issue
Block a user