🐛 fix: simplify updater config logic (#11636)

* 🐛 fix: simplify updater config logic

- Remove isDev check from UpdaterManager initialization
- Remove unused enableRenderHotUpdate field
- Simplify updater behavior to respect enableAppUpdate config

* chore: cleanup

Signed-off-by: Innei <tukon479@gmail.com>

---------

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2026-01-20 20:16:33 +08:00
committed by GitHub
parent ddbe661642
commit 5c645f09fd
3 changed files with 9 additions and 81 deletions

View File

@@ -14,10 +14,8 @@ import { createLogger } from '@/utils/logger';
import type { App as AppCore } from '../App';
// Allow forcing dev update config via env (for testing updates in packaged app)
const FORCE_DEV_UPDATE_CONFIG = getDesktopEnv().FORCE_DEV_UPDATE_CONFIG;
// Create logger
const logger = createLogger('core:UpdaterManager');
export class UpdaterManager {
@@ -31,11 +29,10 @@ export class UpdaterManager {
constructor(app: AppCore) {
this.app = app;
// 设置日志
log.transports.file.level = 'info';
autoUpdater.logger = log;
logger.debug(`[Updater] Log file should be at: ${log.transports.file.getFile().path}`); // 打印路径
logger.debug(`[Updater] Log file should be at: ${log.transports.file.getFile().path}`);
}
get mainWindow() {
@@ -44,50 +41,36 @@ export class UpdaterManager {
public initialize = async () => {
logger.debug('Initializing UpdaterManager');
// If updates are disabled and in production environment, don't initialize updates
if (!updaterConfig.enableAppUpdate && !isDev) {
if (!updaterConfig.enableAppUpdate) {
logger.info('App updates are disabled, skipping updater initialization');
return;
}
// Configure autoUpdater
autoUpdater.autoDownload = false; // Set to false, we'll control downloads manually
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.allowDowngrade = false;
// Enable test mode in development environment or when forced via env
// IMPORTANT: This must be set BEFORE channel configuration so that
// dev-app-update.yml takes precedence over programmatic configuration
const useDevConfig = isDev || FORCE_DEV_UPDATE_CONFIG;
if (useDevConfig) {
// In dev mode, use dev-app-update.yml for all configuration including channel
// Don't set channel here - let dev-app-update.yml control it (defaults to "latest")
autoUpdater.forceDevUpdateConfig = true;
logger.info(
`Using dev update config (isDev=${isDev}, FORCE_DEV_UPDATE_CONFIG=${FORCE_DEV_UPDATE_CONFIG})`,
);
logger.info('Dev mode: Using dev-app-update.yml for update configuration');
} else {
// Only configure channel and update provider programmatically in production
// Note: channel is configured in configureUpdateProvider based on provider type
autoUpdater.allowPrerelease = channel !== 'stable';
logger.info(`Production mode: channel=${channel}, allowPrerelease=${channel !== 'stable'}`);
this.configureUpdateProvider();
}
// Register events
this.registerEvents();
// If auto-check for updates is configured, set up periodic checks
if (updaterConfig.app.autoCheckUpdate) {
// Delay update check by 1 minute after startup to avoid network instability
setTimeout(() => this.checkForUpdates(), 60 * 1000);
// Set up periodic checks
setInterval(() => this.checkForUpdates(), updaterConfig.app.checkUpdateInterval);
}
// Log the channel and allowPrerelease values
logger.debug(
`Initialized with channel: ${autoUpdater.channel}, allowPrerelease: ${autoUpdater.allowPrerelease}`,
);
@@ -105,15 +88,12 @@ export class UpdaterManager {
this.checking = true;
this.isManualCheck = manual;
// Ensure allowPrerelease is correctly set before each check
// This guards against any internal state reset by electron-updater
if (!isStableChannel) {
autoUpdater.allowPrerelease = true;
}
logger.info(`${manual ? 'Manually checking' : 'Auto checking'} for updates...`);
// Log detailed updater configuration for debugging
const inferredChannel =
autoUpdater.channel ||
(autoUpdater.currentVersion?.prerelease?.[0]
@@ -133,7 +113,6 @@ export class UpdaterManager {
logger.info('[Updater Config] usingFallbackProvider:', this.usingFallbackProvider);
logger.info('[Updater Config] GitHub config:', JSON.stringify(githubConfig));
// If manual check, notify renderer process about check start
if (manual) {
this.mainWindow.broadcast('manualUpdateCheckStart');
}
@@ -143,7 +122,6 @@ export class UpdaterManager {
} catch (error) {
logger.error('Error checking for updates:', error.message);
// If manual check, notify renderer process about check error
if (manual) {
this.mainWindow.broadcast('updateError', (error as Error).message);
}
@@ -162,7 +140,6 @@ export class UpdaterManager {
this.downloading = true;
logger.info(`${manual ? 'Manually downloading' : 'Auto downloading'} update...`);
// If manual download or manual check, notify renderer process about download start
if (manual || this.isManualCheck) {
this.mainWindow.broadcast('updateDownloadStart');
}
@@ -173,7 +150,6 @@ export class UpdaterManager {
this.downloading = false;
logger.error('Error downloading update:', error);
// If manual download or manual check, notify renderer process about download error
if (manual || this.isManualCheck) {
this.mainWindow.broadcast('updateError', (error as Error).message);
}
@@ -186,14 +162,10 @@ export class UpdaterManager {
public installNow = () => {
logger.info('Installing update now...');
// Mark application for exit
this.app.isQuiting = true;
// Close all windows first to ensure clean exit
logger.info('Closing all windows before update installation...');
const { BrowserWindow, app } = require('electron');
// do not close windows and quit first
// on Windows, window-all-closed -> app.quit()` can terminate the process before the timer fires
if (!isWindows) {
const allWindows = BrowserWindow.getAllWindows();
allWindows.forEach((window) => {
@@ -203,16 +175,10 @@ export class UpdaterManager {
});
}
// Release single instance lock before quitting
// This ensures the new instance can acquire the lock
logger.info('Releasing single instance lock...');
app.releaseSingleInstanceLock();
// Small delay to ensure windows are closed and lock is released
setTimeout(() => {
// quitAndInstall parameters:
// - isSilent: true (don't show installation UI)
// - isForceRunAfter: true (force start app after installation)
logger.info('Calling autoUpdater.quitAndInstall...');
autoUpdater.quitAndInstall(true, true);
}, 100);
@@ -224,10 +190,7 @@ export class UpdaterManager {
public installLater = () => {
logger.info('Update will be installed on next restart');
// Mark for installation on next launch, but don't exit application
autoUpdater.autoInstallOnAppQuit = true;
// Notify renderer process that update will be installed on next launch
this.mainWindow.broadcast('updateWillInstallLater');
};
@@ -241,7 +204,6 @@ export class UpdaterManager {
logger.info('Simulating update available...');
const mainWindow = this.mainWindow;
// Simulate a new version update
const mockUpdateInfo = {
releaseDate: new Date().toISOString(),
releaseNotes: ` #### Version 1.0.0 Release Notes
@@ -253,14 +215,11 @@ export class UpdaterManager {
version: '1.0.0',
};
// Set update available state
this.updateAvailable = true;
// Notify renderer process
if (this.isManualCheck) {
mainWindow.broadcast('manualUpdateAvailable', mockUpdateInfo);
} else {
// In auto-check mode, directly simulate download
this.simulateDownloadProgress();
}
};
@@ -276,7 +235,6 @@ export class UpdaterManager {
const mainWindow = this.app.browserManager.getMainWindow();
if (mainWindow) {
// Simulate a new version update
const mockUpdateInfo = {
releaseDate: new Date().toISOString(),
releaseNotes: ` #### Version 1.0.0 Release Notes
@@ -288,10 +246,7 @@ export class UpdaterManager {
version: '1.0.0',
};
// Set download state
this.downloading = false;
// Notify renderer process
mainWindow.broadcast('updateDownloaded', mockUpdateInfo);
}
};
@@ -307,28 +262,22 @@ export class UpdaterManager {
const mainWindow = this.app.browserManager.getMainWindow();
// Set download state
this.downloading = true;
// Only broadcast download start event if manual check
if (this.isManualCheck) {
mainWindow.broadcast('updateDownloadStart');
}
// Simulate progress updates
let progress = 0;
const interval = setInterval(() => {
progress += 10;
if (
progress <= 100 && // Only broadcast download progress if manual check
this.isManualCheck
) {
if (progress <= 100 && this.isManualCheck) {
mainWindow.broadcast('updateDownloadProgress', {
bytesPerSecond: 1024 * 1024,
percent: progress, // 1MB/s
total: 1024 * 1024 * 100, // 100MB
transferred: 1024 * 1024 * progress, // Progress * 1MB
percent: progress,
total: 1024 * 1024 * 100,
transferred: 1024 * 1024 * progress,
});
}
@@ -348,8 +297,6 @@ export class UpdaterManager {
*/
private configureUpdateProvider() {
if (isStableChannel && UPDATE_SERVER_URL && !this.usingFallbackProvider) {
// Stable channel uses custom update server (generic HTTP) as primary
// S3 has stable-mac.yml, so we set channel to 'stable'
autoUpdater.channel = 'stable';
logger.info(`Configuring generic provider for stable channel (primary)`);
logger.info(`Update server URL: ${UPDATE_SERVER_URL}`);
@@ -360,9 +307,6 @@ export class UpdaterManager {
url: UPDATE_SERVER_URL,
});
} else {
// GitHub provider:
// - stable: use default latest-mac.yml (GitHub uploads latest* only)
// - beta/nightly: leave channel unset so prerelease matching uses tag (e.g. next)
const reason = this.usingFallbackProvider ? '(fallback from S3)' : '';
logger.info(`Configuring GitHub provider for ${channel} channel ${reason}`);
if (autoUpdater.channel !== null) {
@@ -370,7 +314,6 @@ export class UpdaterManager {
}
logger.info('Channel left unset (defaults to latest-mac.yml for GitHub)');
// For beta/nightly channels, we need prerelease versions
const needPrerelease = channel !== 'stable';
autoUpdater.setFeedURL({
@@ -379,8 +322,6 @@ export class UpdaterManager {
repo: githubConfig.repo,
});
// Ensure allowPrerelease is set correctly after setFeedURL
// setFeedURL may reset some internal states
autoUpdater.allowPrerelease = needPrerelease;
logger.info(
@@ -394,7 +335,6 @@ export class UpdaterManager {
* Called when primary provider (S3) fails
*/
private switchToFallbackAndRetry = async () => {
// Only fallback if we're on stable channel with S3 configured and haven't already fallen back
if (!isStableChannel || !UPDATE_SERVER_URL || this.usingFallbackProvider) {
return false;
}
@@ -403,7 +343,6 @@ export class UpdaterManager {
this.usingFallbackProvider = true;
this.configureUpdateProvider();
// Retry update check with fallback provider
try {
await autoUpdater.checkForUpdates();
return true;
@@ -437,13 +376,11 @@ export class UpdaterManager {
logger.info(`Update available: ${info.version}`);
this.updateAvailable = true;
// Reset to primary provider for next check cycle
this.resetToPrimaryProvider();
if (this.isManualCheck) {
this.mainWindow.broadcast('manualUpdateAvailable', info);
} else {
// If it's an automatic check, start downloading automatically
logger.info('Auto check found update, starting download automatically...');
this.downloadUpdate();
}
@@ -452,7 +389,6 @@ export class UpdaterManager {
autoUpdater.on('update-not-available', (info) => {
logger.info(`Update not available. Current: ${info.version}`);
// Reset to primary provider for next check cycle
this.resetToPrimaryProvider();
if (this.isManualCheck) {
@@ -462,7 +398,6 @@ export class UpdaterManager {
autoUpdater.on('error', async (err) => {
logger.error('Error in auto-updater:', err);
// Log configuration state when error occurs for debugging
logger.error('[Updater Error Context] Channel:', autoUpdater.channel);
logger.error('[Updater Error Context] allowPrerelease:', autoUpdater.allowPrerelease);
logger.error('[Updater Error Context] Build channel from config:', channel);
@@ -471,12 +406,11 @@ export class UpdaterManager {
logger.error('[Updater Error Context] usingFallbackProvider:', this.usingFallbackProvider);
logger.error('[Updater Error Context] GitHub config:', JSON.stringify(githubConfig));
// Try fallback to GitHub if S3 failed
if (!this.usingFallbackProvider && isStableChannel && UPDATE_SERVER_URL) {
logger.info('Attempting fallback to GitHub provider...');
const fallbackSucceeded = await this.switchToFallbackAndRetry();
if (fallbackSucceeded) {
return; // Fallback initiated, don't report error yet
return;
}
}
@@ -497,7 +431,6 @@ export class UpdaterManager {
autoUpdater.on('update-downloaded', (info) => {
logger.info(`Update downloaded: ${info.version}`);
this.downloading = false;
// Always notify about downloaded update
this.mainWindow.broadcast('updateDownloaded', info);
});

View File

@@ -76,7 +76,6 @@ vi.mock('@/modules/updater/configs', () => ({
checkUpdateInterval: 60 * 60 * 1000,
},
enableAppUpdate: true,
enableRenderHotUpdate: true,
},
}));

View File

@@ -27,10 +27,6 @@ export const updaterConfig = {
// Update check interval (milliseconds)
checkUpdateInterval: 60 * 60 * 1000, // 1 hour
},
// Whether to enable application updates
enableAppUpdate: !isDev,
// 是否启用渲染层热更新
enableRenderHotUpdate: !isDev,
};