From 12a54a9d47def372c7415fc27cc80d445fe13f26 Mon Sep 17 00:00:00 2001 From: Florent Benoit Date: Fri, 24 Mar 2023 10:28:49 +0100 Subject: [PATCH] fix(updates): Rework update mechanism - ensure that when we check for updates, we do not download artifacts - ensure that we're event based - ask the user if he wants to restart Podman Desktop once the update has been successfully downloaded fixes https://github.com/containers/podman-desktop/issues/1806 Change-Id: Ib6f7460db601bdae7f17758b52af506a207789b6 Signed-off-by: Florent Benoit --- packages/main/src/index.ts | 19 --- packages/main/src/mainWindow.ts | 14 +++ packages/main/src/plugin/index.ts | 196 +++++++++++++++++++++--------- 3 files changed, 154 insertions(+), 75 deletions(-) diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index cddad90c05e36..700e61d93f0f1 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts @@ -27,25 +27,6 @@ import { StartupInstall } from './system/startup-install'; export const UPDATER_UPDATE_AVAILABLE_ICON = 'fa fa-exclamation-triangle'; -// Check for updates when initially ready as well as set a 6 hour interval for any checks -if (import.meta.env.PROD) { - import('electron-updater').then(({ autoUpdater }) => { - app - .whenReady() - .then(() => { - // Check for updates on startup - autoUpdater.checkForUpdatesAndNotify(); - - // Create an interval to check for updates every 12 hours / notify the user - setInterval(() => { - autoUpdater.checkForUpdatesAndNotify(); - // check every 12 hours - }, 1000 * 60 * 60 * 12); - }) - .catch(e => console.error('Failed to start auto-updater:', e)); - }); -} - /** * Prevent multiple instances */ diff --git a/packages/main/src/mainWindow.ts b/packages/main/src/mainWindow.ts index e2cde1591f3b5..a523f2f828632 100644 --- a/packages/main/src/mainWindow.ts +++ b/packages/main/src/mainWindow.ts @@ -17,6 +17,7 @@ ***********************************************************************/ import type { BrowserWindowConstructorOptions, FileFilter } from 'electron'; +import { autoUpdater } from 'electron'; import { Menu } from 'electron'; import { BrowserWindow, ipcMain, app, dialog, screen, nativeTheme } from 'electron'; import contextMenu from 'electron-context-menu'; @@ -119,7 +120,20 @@ async function createWindow() { configurationRegistry = data; }); + // receive the message because an update is in progress and we need to quit the app + let quitAfterUpdate = false; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + autoUpdater.on('before-quit-for-update', () => { + quitAfterUpdate = true; + }); + browserWindow.on('close', e => { + if (quitAfterUpdate) { + browserWindow.destroy(); + app.quit(); + return; + } + const closeBehaviorConfiguration = configurationRegistry?.getConfiguration('preferences'); let exitonclose = isLinux(); // default value, which we will use unless the user preference is available. if (closeBehaviorConfiguration) { diff --git a/packages/main/src/plugin/index.ts b/packages/main/src/plugin/index.ts index 97e1dfd598973..637d54fb93a1f 100644 --- a/packages/main/src/plugin/index.ts +++ b/packages/main/src/plugin/index.ts @@ -90,6 +90,8 @@ import { InputQuickPickRegistry } from './input-quickpick/input-quickpick-regist import type { Menu } from '/@/plugin/menu-registry'; import { MenuRegistry } from '/@/plugin/menu-registry'; import { CancellationTokenRegistry } from './cancellation-token-registry'; +import type { UpdateCheckResult } from 'electron-updater'; +import { autoUpdater } from 'electron-updater'; type LogType = 'log' | 'warn' | 'trace' | 'debug' | 'error'; @@ -337,17 +339,20 @@ export class PluginSystem { const currentVersion = `v${app.getVersion()}`; // Add version entry to the status bar - statusBarRegistry.setEntry( - 'version', - false, - 0, - currentVersion, - `Using version ${currentVersion}`, - undefined, - true, - 'version', - undefined, - ); + const defaultVersionEntry = () => { + statusBarRegistry.setEntry( + 'version', + false, + 0, + currentVersion, + `Using version ${currentVersion}`, + undefined, + true, + 'version', + undefined, + ); + }; + defaultVersionEntry(); // Show a "No update available" only for macOS and Windows users and on production builds let detailMessage: string; @@ -366,55 +371,134 @@ export class PluginSystem { }); }); - if (import.meta.env.PROD) { - // Only import on production builds - import('electron-updater').then(({ autoUpdater }) => { - // autoUpdater.checkForUpdatesAndNotify() is called from main/src/index.ts - autoUpdater.on('update-available', () => { - // Update the 'version' entry in the status bar to show that an update is available - // this uses setEntry to update the existing entry - statusBarRegistry.setEntry( - 'version', - false, - 0, - currentVersion, - 'Update available', - UPDATER_UPDATE_AVAILABLE_ICON, - true, - 'update', - undefined, - ); + // Only check on production builds for Windows and macOS users + if (import.meta.env.PROD && !isLinux()) { + // disable auto download + autoUpdater.autoDownload = false; + + let updateInProgress = false; + let updateAlreadyDownloaded = false; + + // setup the event listeners + autoUpdater.on('update-available', () => { + updateInProgress = false; + updateAlreadyDownloaded = false; + + // Update the 'version' entry in the status bar to show that an update is available + // this uses setEntry to update the existing entry + statusBarRegistry.setEntry( + 'version', + false, + 0, + currentVersion, + 'Update available', + UPDATER_UPDATE_AVAILABLE_ICON, + true, + 'update', + undefined, + ); + }); + + autoUpdater.on('update-not-available', () => { + updateInProgress = false; + updateAlreadyDownloaded = false; + + // Update the 'version' entry in the status bar to show that no update is available + defaultVersionEntry(); + }); + + autoUpdater.on('update-downloaded', async () => { + updateAlreadyDownloaded = true; + updateInProgress = false; + const result = await dialog.showMessageBox({ + title: 'Update Downloaded', + message: 'Update downloaded, Do you want to restart Podman Desktop ?', + cancelId: 1, + type: 'info', + buttons: ['Restart', 'Cancel'], }); + if (result.response === 0) { + setImmediate(() => autoUpdater.quitAndInstall()); + } + }); + + autoUpdater.on('error', error => { + console.error('unable to check for updates', error); + updateInProgress = false; + dialog.showErrorBox('Error: ', error == null ? 'unknown' : (error.stack || error).toString()); + }); + + // check for updates now + let updateCheckResult: UpdateCheckResult | null; - // Update will create the standard "autoUpdater" dialog / update process that Electron provides - commandRegistry.registerCommand('update', () => { - autoUpdater.checkForUpdates().then(update => { - // Get the version of the update - const updateVersion = update?.updateInfo.version ? `v${update?.updateInfo.version}` : ''; - - dialog - .showMessageBox({ - type: 'info', - title: 'Update Available', - message: `A new version ${updateVersion} of Podman Desktop is available. Do you want to update your current version ${currentVersion}?`, - buttons: ['Update', 'Later'], - }) - .then(result => { - if (result.response === 0) { - // Download update and try / catch it and create a dialog if it fails - autoUpdater.downloadUpdate().catch(error => { - console.error('Update error: ', error); - dialog.showMessageBox({ - type: 'error', - title: 'Update Failed', - message: `An error occurred while trying to update to version ${updateVersion}. See the developer console for more information.`, - buttons: ['OK'], - }); - }); - } - }); + try { + updateCheckResult = await autoUpdater.checkForUpdates(); + } catch (error) { + console.error('unable to check for updates', error); + } + + // Create an interval to check for updates every 12 hours + setInterval(async () => { + try { + updateCheckResult = await autoUpdater.checkForUpdates(); + } catch (error) { + console.log('unable to check for updates', error); + } + }, 1000 * 60 * 60 * 12); + + // Update will create the standard "autoUpdater" dialog / update process that Electron provides + commandRegistry.registerCommand('update', async () => { + if (updateAlreadyDownloaded) { + const result = await dialog.showMessageBox({ + type: 'info', + title: 'Update', + message: 'There is already an update downloaded. Please Restart Podman Desktop.', + cancelId: 1, + buttons: ['Restart', 'Cancel'], }); + if (result.response === 0) { + setImmediate(() => autoUpdater.quitAndInstall()); + } + return; + } + + if (updateInProgress) { + await dialog.showMessageBox({ + type: 'info', + title: 'Update', + message: 'There is already an update in progress. Please wait until it is downloaded', + buttons: ['OK'], + }); + return; + } + + // Get the version of the update + const updateVersion = updateCheckResult?.updateInfo.version ? `v${updateCheckResult?.updateInfo.version}` : ''; + + const result = await dialog.showMessageBox({ + type: 'info', + title: 'Update Available', + message: `A new version ${updateVersion} of Podman Desktop is available. Do you want to update your current version ${currentVersion}?`, + buttons: ['Update', 'Cancel'], + cancelId: 1, }); + if (result.response === 0) { + updateInProgress = true; + updateAlreadyDownloaded = false; + + // Download update and try / catch it and create a dialog if it fails + try { + await autoUpdater.downloadUpdate(); + } catch (error) { + console.error('Update error: ', error); + dialog.showMessageBox({ + type: 'error', + title: 'Update Failed', + message: `An error occurred while trying to update to version ${updateVersion}. See the developer console for more information.`, + buttons: ['OK'], + }); + } + } }); }