feat(desktop): add FLUXER_DISABLE_MODULE_UPDATES opt-out (#3466)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-14 16:30:34 +02:00
1 parent e8dff6a8a1
commit e403fb6cce
11 files changed
+302 -77

No files matched your search

+12 -1
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
const {execFile} = require('node:child_process');
const {readFileSync} = require('node:fs');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
@@ -102,6 +103,7 @@ const isMacBuild = process.argv.includes('--mac');
const isWindowsBuild = process.argv.includes('--win');
const targetPlatform = isLinuxBuild ? 'linux' : isMacBuild ? 'darwin' : isWindowsBuild ? 'win32' : process.platform;
const metadataName = isLinuxBuild ? linuxPackageName : packageName;
const shellVersion = process.env.VERSION || readBundledRendererVersion();
const supportedTargetArchs = ['x64', 'arm64'];
const supportedMacTargetArchs = [...supportedTargetArchs, 'universal'];
const electronArch = process.env.ELECTRON_ARCH;
@@ -644,6 +646,15 @@ function packagePathParts(packageName) {
return [packageName];
}
function readBundledRendererVersion() {
try {
const {version} = JSON.parse(readFileSync(path.join(__dirname, 'dist', 'renderer', 'version.json'), 'utf8'));
return typeof version === 'string' && /^\d+\.\d+\.\d+$/u.test(version) ? version : null;
} catch {
return null;
}
}
function isNonEmptyString(value) {
return typeof value === 'string' && value.length > 0;
}
@@ -1623,7 +1634,7 @@ module.exports = {
extraMetadata: {
main: 'dist/main/index.js',
name: metadataName,
...(process.env.VERSION ? {version: process.env.VERSION} : {}),
...(shellVersion ? {version: shellVersion} : {}),
...(targetPlatform === 'linux' ? {desktopName: `${linuxDesktopId}.desktop`} : {}),
},
extraResources: [
+24 -11
View File
@@ -39,18 +39,20 @@ const isProduction =
process.env.FLUXER_DESKTOP_PRODUCTION === 'true' ||
process.env.GITHUB_ACTIONS === 'true';
const skipNative = process.env.FLUXER_SKIP_NATIVE === 'true';
const embeddedBuildVersion = process.env.PUBLIC_BUILD_VERSION || process.env.BUILD_VERSION || '';
let embeddedBuildVersion = process.env.PUBLIC_BUILD_VERSION || process.env.BUILD_VERSION || '';
const embeddedReleaseChannel = process.env.PUBLIC_RELEASE_CHANNEL || process.env.RELEASE_CHANNEL || '';
const modulesEnabled = process.env.FLUXER_MODULES === '1' || process.env.FLUXER_MODULES === 'true';
const offlineBuild = process.env.FLUXER_OFFLINE === '1' || process.env.FLUXER_OFFLINE === 'true';
const publicBuildDefines = {
'process.env.PUBLIC_BUILD_VERSION': JSON.stringify(embeddedBuildVersion),
'process.env.BUILD_VERSION': JSON.stringify(embeddedBuildVersion),
'process.env.PUBLIC_RELEASE_CHANNEL': JSON.stringify(embeddedReleaseChannel),
'process.env.RELEASE_CHANNEL': JSON.stringify(embeddedReleaseChannel),
'process.env.FLUXER_MODULES': JSON.stringify(modulesEnabled ? '1' : ''),
'process.env.FLUXER_OFFLINE': JSON.stringify(offlineBuild ? '1' : ''),
};
function publicBuildDefines() {
return {
'process.env.PUBLIC_BUILD_VERSION': JSON.stringify(embeddedBuildVersion),
'process.env.BUILD_VERSION': JSON.stringify(embeddedBuildVersion),
'process.env.PUBLIC_RELEASE_CHANNEL': JSON.stringify(embeddedReleaseChannel),
'process.env.RELEASE_CHANNEL': JSON.stringify(embeddedReleaseChannel),
'process.env.FLUXER_MODULES': JSON.stringify(modulesEnabled ? '1' : ''),
'process.env.FLUXER_OFFLINE': JSON.stringify(offlineBuild ? '1' : ''),
};
}
const electronExternals = [
'electron',
'electron-log',
@@ -116,6 +118,16 @@ function readBundledRendererVersion() {
}
}
function fallBackToBundledRendererVersion() {
if (embeddedBuildVersion !== '') {
return;
}
const bundledVersion = readBundledRendererVersion();
if (BUNDLED_RENDERER_VERSION_PATTERN.test(bundledVersion)) {
embeddedBuildVersion = bundledVersion;
}
}
class RendererServiceWorkerPresentError extends Error {
constructor(present) {
super(
@@ -674,7 +686,7 @@ async function buildMain() {
define: {
'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),
[MAIN_APP_OUTPUT_FILE_DEFINE]: JSON.stringify(MAIN_APP_OUTPUT_FILE_NAME),
...publicBuildDefines,
...publicBuildDefines(),
},
banner: {
js: `import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);`,
@@ -806,7 +818,7 @@ function preloadBuildOptions(entryFileName, outputFileName) {
tsconfig: DESKTOP_TSCONFIG,
define: {
'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),
...publicBuildDefines,
...publicBuildDefines(),
},
};
}
@@ -887,6 +899,7 @@ async function build() {
if (!options.useSharedRenderer) {
copyRendererIntoDist();
}
fallBackToBundledRendererVersion();
if (modulesEnabled) {
prunePackedRendererModules();
}
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {createRequire} from 'node:module';
import os from 'node:os';
import path from 'node:path';
import {after, describe, test} from 'node:test';
const require = createRequire(import.meta.url);
const DESKTOP_DIR = path.resolve(import.meta.dirname, '..', '..');
const PREFLIGHT_MODULES = path.join('src', 'main', 'NativeModulePreflightModules.json');
const temporaryRoots = [];
after(() => {
for (const root of temporaryRoots) {
fs.rmSync(root, {recursive: true, force: true});
}
});
function loadMetadataVersion({version, rendererVersionJson}) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'builder-shell-version-'));
temporaryRoots.push(root);
fs.copyFileSync(
path.join(DESKTOP_DIR, 'electron-builder.config.cjs'),
path.join(root, 'electron-builder.config.cjs'),
);
fs.mkdirSync(path.join(root, 'src', 'main'), {recursive: true});
fs.copyFileSync(path.join(DESKTOP_DIR, PREFLIGHT_MODULES), path.join(root, PREFLIGHT_MODULES));
if (rendererVersionJson != null) {
fs.mkdirSync(path.join(root, 'dist', 'renderer'), {recursive: true});
fs.writeFileSync(path.join(root, 'dist', 'renderer', 'version.json'), rendererVersionJson);
}
const previous = process.env.VERSION;
if (version == null) {
delete process.env.VERSION;
} else {
process.env.VERSION = version;
}
try {
return require(path.join(root, 'electron-builder.config.cjs')).extraMetadata.version;
} finally {
if (previous === undefined) {
delete process.env.VERSION;
} else {
process.env.VERSION = previous;
}
}
}
describe('the packaged shell version', () => {
test('comes from VERSION whenever it is set', () => {
assert.equal(
loadMetadataVersion({version: '2026.1014.2', rendererVersionJson: '{"version":"2026.1014.9"}'}),
'2026.1014.2',
);
});
test('falls back to the bundled renderer version when VERSION is unset', () => {
assert.equal(loadMetadataVersion({rendererVersionJson: '{"version":"2026.1014.9"}'}), '2026.1014.9');
});
test('keeps the package.json version when the renderer has no numeric version', () => {
assert.equal(loadMetadataVersion({rendererVersionJson: '{"version":"dev"}'}), undefined);
assert.equal(loadMetadataVersion({rendererVersionJson: 'null'}), undefined);
assert.equal(loadMetadataVersion({}), undefined);
});
});
+59 -47
View File
@@ -159,7 +159,7 @@ async function runModuleBootstrap(): Promise<void> {
const {configureUserDataPath} = await import('@electron/common/UserDataPath');
const {armNativeProbeCache} = await import('@electron/main/NativeProbeCache');
const {isStartMinimizedLaunch} = await import('@electron/main/AutostartLaunch');
const {isDesktopUpdateRequested} = await import('@electron/main/LaunchOptions');
const {areModuleUpdatesDisabled, isDesktopUpdateRequested} = await import('@electron/main/LaunchOptions');
const {relaunchStableLaunchPath} = await import('@electron/main/LinuxLaunchPath');
const {getDesktopLocalAppProtocol} = await import('@electron/main/LocalAppProtocol');
const {
@@ -410,6 +410,10 @@ async function runModuleBootstrap(): Promise<void> {
logger.warn('Failed to record whether an update is waiting', error);
}
};
const moduleUpdatesDisabled = areModuleUpdatesDisabled();
if (moduleUpdatesDisabled) {
logger.info('Module updates are disabled by FLUXER_DISABLE_MODULE_UPDATES');
}
const updater = new ModuleUpdater({
store,
shellVersion: app.getVersion(),
@@ -417,6 +421,7 @@ async function runModuleBootstrap(): Promise<void> {
bundledRendererVersion: BUNDLED_RENDERER_VERSION,
preferUnversionedBundle: app.isPackaged,
forceStartupUpdate: store.shellVersionChanged || updateWasPending || isDesktopUpdateRequested(),
moduleUpdatesDisabled,
selfUpdateShellFirst:
shellUpdatePlan.capability === ShellUpdateCapability.SELF_UPDATE
? async (latestVersion) => {
@@ -705,35 +710,37 @@ async function runModuleBootstrap(): Promise<void> {
},
};
const desktopUpdate = new DesktopUpdateRun({
probe: () => updater.checkForUpdate(),
canSelfUpdateShell: shellUpdatePlan.capability === ShellUpdateCapability.SELF_UPDATE,
runShellSelfUpdate: () => runShellSelfUpdateOnSplash(),
installModules: async () => {
const launchAttempt = await updater.installPending();
if (launchAttempt != null) {
await refreshModuleRoots(launchAttempt.committed);
}
return launchAttempt;
},
takeover: updateTakeover,
publish: (check) => {
recordPendingUpdate(check);
publishDesktopUpdateCheck(check);
},
reportFailure: (failure) => {
void import('@electron/main/DesktopUpdatePrompt')
.then(({reportDesktopUpdateFailure}) => reportDesktopUpdateFailure(failure))
.catch((error: unknown) => {
logger.error('Failed to report the desktop update failure', error);
});
},
openDownloadsPage: async () => {
const {DOWNLOAD_PAGE_URL} = await import('@electron/main/UpdaterDownloads');
await shell.openExternal(DOWNLOAD_PAGE_URL);
},
logger,
});
const desktopUpdate = moduleUpdatesDisabled
? null
: new DesktopUpdateRun({
probe: () => updater.checkForUpdate(),
canSelfUpdateShell: shellUpdatePlan.capability === ShellUpdateCapability.SELF_UPDATE,
runShellSelfUpdate: () => runShellSelfUpdateOnSplash(),
installModules: async () => {
const launchAttempt = await updater.installPending();
if (launchAttempt != null) {
await refreshModuleRoots(launchAttempt.committed);
}
return launchAttempt;
},
takeover: updateTakeover,
publish: (check) => {
recordPendingUpdate(check);
publishDesktopUpdateCheck(check);
},
reportFailure: (failure) => {
void import('@electron/main/DesktopUpdatePrompt')
.then(({reportDesktopUpdateFailure}) => reportDesktopUpdateFailure(failure))
.catch((error: unknown) => {
logger.error('Failed to report the desktop update failure', error);
});
},
openDownloadsPage: async () => {
const {DOWNLOAD_PAGE_URL} = await import('@electron/main/UpdaterDownloads');
await shell.openExternal(DOWNLOAD_PAGE_URL);
},
logger,
});
const permit = await runModuleUpdateLoop();
localNetworkHint.disarm();
@@ -741,25 +748,30 @@ async function runModuleBootstrap(): Promise<void> {
recordPendingUpdate({shellNewer: false, modulesChanged: false});
}
await refreshModuleRoots(permit.launchAttempt.committed);
setOnDemandModuleInstaller(
createOnDemandModuleInstaller({
ensure: (moduleName) => updater.ensureModule(moduleName),
refresh: serveOnDemandModules,
onFailure: (moduleName, error) => {
logger.error('Failed to install a module on demand', {module: moduleName, error});
},
}),
);
markSplashLaunching();
armDesktopUpdate({
check: async (manual) => {
const check = await desktopUpdate.check({manual});
recordPendingUpdate(check);
return check;
},
start: () => desktopUpdate.start(),
});
if (desktopUpdate != null) {
setOnDemandModuleInstaller(
createOnDemandModuleInstaller({
ensure: (moduleName) => updater.ensureModule(moduleName),
refresh: serveOnDemandModules,
onFailure: (moduleName, error) => {
logger.error('Failed to install a module on demand', {module: moduleName, error});
},
}),
);
armDesktopUpdate({
check: async (manual) => {
const check = await desktopUpdate.check({manual});
recordPendingUpdate(check);
return check;
},
start: () => desktopUpdate.start(),
});
}
armMainWindowHandoff(updater, permit.launchAttempt, () => {
if (desktopUpdate == null) {
return;
}
desktopUpdate.markLaunchSettled();
preloadUpdateSplash();
armDesktopUpdatePoll(
@@ -275,3 +275,26 @@ describe('Bootstrap entry point', () => {
);
});
});
describe('FLUXER_DISABLE_MODULE_UPDATES', () => {
test('reads the environment variable like the other FLUXER_DISABLE switches', async () => {
const {areModuleUpdatesDisabled} = await import('./LaunchOptions.ts');
const previous = process.env.FLUXER_DISABLE_MODULE_UPDATES;
try {
delete process.env.FLUXER_DISABLE_MODULE_UPDATES;
assert.equal(areModuleUpdatesDisabled(), false);
for (const value of ['1', 'true', 'YES', 'on']) {
process.env.FLUXER_DISABLE_MODULE_UPDATES = value;
assert.equal(areModuleUpdatesDisabled(), true);
}
process.env.FLUXER_DISABLE_MODULE_UPDATES = '0';
assert.equal(areModuleUpdatesDisabled(), false);
} finally {
if (previous === undefined) {
delete process.env.FLUXER_DISABLE_MODULE_UPDATES;
} else {
process.env.FLUXER_DISABLE_MODULE_UPDATES = previous;
}
}
});
});
+10 -7
View File
@@ -13,6 +13,7 @@ import type {
} from '@electron/common/Types';
import {getDesktopUpdateState, observeDesktopUpdateState} from '@electron/main/DesktopUpdateGate';
import {checkForUpdatesFromShell, startDesktopUpdateFromShell} from '@electron/main/DesktopUpdatePrompt';
import {areModuleUpdatesDisabled} from '@electron/main/LaunchOptions';
import {relaunchStableLaunchPath} from '@electron/main/LinuxLaunchPath';
import {onLocaleChange, t} from '@electron/main/MainI18n';
import {app, type BrowserWindow, clipboard, Menu, nativeImage, Tray} from 'electron';
@@ -399,13 +400,15 @@ function buildTrayMenu(): Menu {
click: () => runTrayMenuAction(startDesktopUpdateFromShell),
});
}
menuTemplate.push({
label: t('desktop.tray.checkForUpdates'),
click: () =>
runTrayMenuAction(() => {
void checkForUpdatesFromShell();
}),
});
if (!areModuleUpdatesDisabled()) {
menuTemplate.push({
label: t('desktop.tray.checkForUpdates'),
click: () =>
runTrayMenuAction(() => {
void checkForUpdatesFromShell();
}),
});
}
if (trayState.buildInfo) {
menuTemplate.push({
label: t('desktop.tray.copyBuildInfo'),
+5
View File
@@ -159,9 +159,14 @@ export function describeLaunchDiagnosticOptions(argv: ReadonlyArray<string> = pr
spellcheck: getSpellcheckLaunchMode(argv),
v8CodeCacheDisabled: shouldDisableV8CodeCache(argv),
nativeGatewayDisableReason: getNativeGatewayDisableReason(argv),
moduleUpdatesDisabled: areModuleUpdatesDisabled(),
};
}
export function areModuleUpdatesDisabled(): boolean {
return truthyEnv('FLUXER_DISABLE_MODULE_UPDATES');
}
export function isDesktopUpdateRequested(argv: ReadonlyArray<string> = process.argv): boolean {
return hasFlag(argv, DESKTOP_UPDATE_ARGS);
}
+3 -2
View File
@@ -2,6 +2,7 @@
import {DESKTOP_APP_NAME} from '@electron/common/DesktopIdentity';
import {checkForUpdatesFromShell} from '@electron/main/DesktopUpdatePrompt';
import {areModuleUpdatesDisabled} from '@electron/main/LaunchOptions';
import {onLocaleChange, t} from '@electron/main/MainI18n';
import {openExternalDeduped} from '@electron/main/OpenExternal';
import {buildTroubleshootingMenuItems} from '@electron/main/Troubleshooting';
@@ -31,7 +32,7 @@ function buildTemplate(): Array<MenuItemConstructorOptions> {
role: 'about',
label: t('desktop.appMenu.about', {appName}),
},
checkForUpdatesItem(),
...(areModuleUpdatesDisabled() ? [] : [checkForUpdatesItem()]),
{type: 'separator'},
{
label: t('desktop.appMenu.preferences'),
@@ -209,7 +210,7 @@ function buildTemplate(): Array<MenuItemConstructorOptions> {
? `${t('desktop.appMenu.help')}${MACOS_HELP_MENU_TITLE_AUTODETECT_OPT_OUT}`
: t('desktop.appMenu.help'),
submenu: [
...(isMac ? [] : [checkForUpdatesItem(), {type: 'separator' as const}]),
...(isMac || areModuleUpdatesDisabled() ? [] : [checkForUpdatesItem(), {type: 'separator' as const}]),
{
label: t('desktop.appMenu.website'),
click: async () => {
@@ -127,6 +127,7 @@ function createUpdater(store, modules, options = {}) {
hasOfflineRenderer: options.bundledRendererVersion != null,
bundledRendererVersion: options.bundledRendererVersion ?? null,
forceStartupUpdate: options.forceStartupUpdate ?? false,
moduleUpdatesDisabled: options.moduleUpdatesDisabled ?? false,
selfUpdateShellFirst: options.selfUpdateShellFirst,
onState: options.onState,
fetch: async (url, init) => {
@@ -506,6 +507,75 @@ describe('ModuleUpdater.run', () => {
});
});
describe('ModuleUpdater with module updates disabled', () => {
test('launches the verified set without fetching even when the manifest demands a shell update', async () => {
const userDataPath = createUserData();
const store = await openStore(userDataPath);
installModuleOnDisk(getModuleStoreRoot(userDataPath), 'fluxer_renderer', RENDERER_SHA, [['index.js', 'renderer']]);
await store.commit({fluxer_renderer: RENDERER_SHA});
assert.equal((await createUpdater(store, {fluxer_renderer: RENDERER_SHA}).updater.run()).status, 'launching');
const statuses = [];
let shellUpdates = 0;
const {updater, requests, sleeps} = createUpdater(
store,
{fluxer_renderer: NEXT_RENDERER_SHA},
{
shellLatest: '2026.824.0',
shellMinimum: '2026.824.0',
forceStartupUpdate: true,
moduleUpdatesDisabled: true,
onState: (state) => statuses.push(state.status),
selfUpdateShellFirst: async () => {
shellUpdates += 1;
return {reason: 'no-update'};
},
},
);
const outcome = await updater.run();
assert.equal(outcome.status, 'unreachable-launch');
assert.deepEqual(outcome.committed, {fluxer_renderer: RENDERER_SHA});
assert.deepEqual(requests, []);
assert.deepEqual(sleeps, []);
assert.equal(shellUpdates, 0);
assert.deepEqual(statuses, ['unreachable-launch']);
assert.equal(outcome.launchAttempt.committed.fluxer_renderer, RENDERER_SHA);
});
test('launches the bundled renderer on a fresh store without fetching', async () => {
const store = await openStore(createUserData());
const {updater, requests} = createUpdater(
store,
{fluxer_renderer: NEXT_RENDERER_SHA},
{bundledRendererVersion: SHELL_VERSION, moduleUpdatesDisabled: true},
);
const outcome = await updater.run();
assert.equal(outcome.status, 'unreachable-launch');
assert.deepEqual(outcome.committed, {});
assert.deepEqual(requests, []);
});
test('blocks at once on the unreachable splash without fetching when nothing is installed', async () => {
const store = await openStore(createUserData());
const {updater, requests, sleeps} = createUpdater(
store,
{fluxer_renderer: RENDERER_SHA},
{moduleUpdatesDisabled: true},
);
const outcome = await updater.run();
assert.equal(outcome.status, 'blocked-update-required');
assert.equal(outcome.reason, 'nothing-installed');
assert.equal(outcome.updateServerUnreachable, true);
assert.deepEqual(requests, []);
assert.deepEqual(sleeps, []);
});
});
describe('ModuleUpdater checks without downloading and installs only when asked', () => {
async function storeOnRenderer() {
const userDataPath = createUserData();
+26 -7
View File
@@ -267,6 +267,7 @@ interface ModuleUpdaterOptions {
readonly bundledRendererVersion?: string | null;
readonly preferUnversionedBundle?: boolean;
readonly forceStartupUpdate?: boolean;
readonly moduleUpdatesDisabled?: boolean;
readonly selfUpdateShellFirst?: (latestVersion: string) => Promise<{readonly reason: string}>;
readonly onState?: (state: ModuleUpdaterSplashState) => void;
readonly report?: (report: ModuleUpdaterReport) => void;
@@ -286,6 +287,7 @@ export class ModuleUpdater {
private readonly shellVersion: ModuleVersion;
private readonly hasOfflineRenderer: boolean;
private readonly forceStartupUpdate: boolean;
private readonly moduleUpdatesDisabled: boolean;
private readonly selfUpdateShellFirst: ((latestVersion: string) => Promise<{readonly reason: string}>) | null;
private readonly shellFirstAttempts = new Map<string, {readonly reason: string} | null>();
private readonly manifestFeed: ModuleManifestFeedIdentity;
@@ -314,6 +316,7 @@ export class ModuleUpdater {
this.shellVersion = parseModuleVersion(options.shellVersion, 'desktop shell version');
this.hasOfflineRenderer = options.hasOfflineRenderer ?? false;
this.forceStartupUpdate = options.forceStartupUpdate ?? false;
this.moduleUpdatesDisabled = options.moduleUpdatesDisabled ?? false;
this.selfUpdateShellFirst = options.selfUpdateShellFirst ?? null;
const releaseChannel = options.releaseChannel ?? BUILD_CHANNEL;
this.platform = options.platform ?? process.platform;
@@ -418,6 +421,18 @@ export class ModuleUpdater {
return reverted;
}
}
if (this.moduleUpdatesDisabled) {
const decision = await this.planner.evaluateUnreachableLaunch(this.startupUpdateEnforced);
if (decision.kind === ModuleUnreachableLaunchDecision.LAUNCH) {
return await this.launchUnreachable();
}
return this.blockUpdateRequired(
decision.reason,
[],
'module updates are disabled by FLUXER_DISABLE_MODULE_UPDATES',
{updateServerUnreachable: true},
);
}
if (this.platform === 'linux' && !(await this.startupUpdateRequired())) {
return await this.launchIfAtFloor();
}
@@ -486,13 +501,7 @@ export class ModuleUpdater {
if (exhausted || !fetched) {
const decision = await this.planner.evaluateUnreachableLaunch(this.startupUpdateEnforced);
if (decision.kind === ModuleUnreachableLaunchDecision.LAUNCH) {
const launchAttempt = await this.store.recordLaunchAttempt();
this.emit(ModuleUpdaterStatus.UNREACHABLE_LAUNCH);
return {
status: ModuleUpdaterStatus.UNREACHABLE_LAUNCH,
committed: this.store.getCommitted(),
launchAttempt,
};
return await this.launchUnreachable();
}
if (exhausted) {
return this.blockUpdateRequired(decision.reason, [], describeErrorChain(error), {
@@ -802,6 +811,16 @@ export class ModuleUpdater {
};
}
private async launchUnreachable(): Promise<ModuleUpdaterOutcome> {
const launchAttempt = await this.store.recordLaunchAttempt();
this.emit(ModuleUpdaterStatus.UNREACHABLE_LAUNCH);
return {
status: ModuleUpdaterStatus.UNREACHABLE_LAUNCH,
committed: this.store.getCommitted(),
launchAttempt,
};
}
private async launchRevertedSetBypassingFloor(
committed: Readonly<Record<string, string>>,
): Promise<ModuleUpdaterOutcome | null> {
+2 -2
View File
@@ -73,7 +73,7 @@ import {
registerIpcHandlers,
} from '@electron/main/IpcHandlers';
import {initializeJumpList} from '@electron/main/JumpList';
import {describeLaunchDiagnosticOptions} from '@electron/main/LaunchOptions';
import {areModuleUpdatesDisabled, describeLaunchDiagnosticOptions} from '@electron/main/LaunchOptions';
import {cleanupLegacyHarvestHandlers, registerLegacyHarvestHandlers} from '@electron/main/LegacyHarvestIpc';
import {initializeLegacyOriginHarvest} from '@electron/main/LegacyOriginHarvest';
import {cleanupVirtmic, registerVirtmicHandlers} from '@electron/main/LinuxAudioCapture';
@@ -600,7 +600,7 @@ if (launchConfigurationError) {
showWindow,
});
});
if (process.env.FLUXER_OFFLINE !== '1') {
if (process.env.FLUXER_OFFLINE !== '1' && !areModuleUpdatesDisabled()) {
const {registerUpdater} = await import('@electron/main/Updater');
registerUpdater(getMainWindow);
}