diff --git a/apps/desktop/src/main/updater.main.ts b/apps/desktop/src/main/updater.main.ts index 0e2efa66f91..51d5073911e 100644 --- a/apps/desktop/src/main/updater.main.ts +++ b/apps/desktop/src/main/updater.main.ts @@ -1,6 +1,6 @@ import { dialog, shell } from "electron"; import log from "electron-log"; -import { autoUpdater } from "electron-updater"; +import { autoUpdater, UpdateDownloadedEvent, VerifyUpdateSupport } from "electron-updater"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -15,6 +15,8 @@ export class UpdaterMain { private doingUpdateCheck = false; private doingUpdateCheckWithFeedback = false; private canUpdate = false; + private updateDownloaded: UpdateDownloadedEvent = null; + private originalRolloutFunction: VerifyUpdateSupport = null; constructor( private i18nService: I18nService, @@ -22,6 +24,8 @@ export class UpdaterMain { ) { autoUpdater.logger = log; + this.originalRolloutFunction = autoUpdater.isUserWithinRollout; + const linuxCanUpdate = process.platform === "linux" && isAppImage(); const windowsCanUpdate = process.platform === "win32" && !isWindowsStore() && !isWindowsPortable(); @@ -57,20 +61,16 @@ export class UpdaterMain { }); if (result.response === 0) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - autoUpdater.downloadUpdate(); + await autoUpdater.downloadUpdate(); } else { this.reset(); } } }); - autoUpdater.on("update-not-available", () => { + autoUpdater.on("update-not-available", async () => { if (this.doingUpdateCheckWithFeedback && this.windowMain.win != null) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - dialog.showMessageBox(this.windowMain.win, { + await dialog.showMessageBox(this.windowMain.win, { message: this.i18nService.t("noUpdatesAvailable"), buttons: [this.i18nService.t("ok")], defaultId: 0, @@ -86,22 +86,8 @@ export class UpdaterMain { return; } - const result = await dialog.showMessageBox(this.windowMain.win, { - type: "info", - title: this.i18nService.t("bitwarden") + " - " + this.i18nService.t("restartToUpdate"), - message: this.i18nService.t("restartToUpdate"), - detail: this.i18nService.t("restartToUpdateDesc", info.version), - buttons: [this.i18nService.t("restart"), this.i18nService.t("later")], - cancelId: 1, - defaultId: 0, - noLink: true, - }); - - if (result.response === 0) { - // Quit and install have a different window logic, setting `isQuitting` just to be safe. - this.windowMain.isQuitting = true; - autoUpdater.quitAndInstall(true, true); - } + this.updateDownloaded = info; + await this.promptRestartUpdate(info); }); autoUpdater.on("error", (error) => { @@ -117,15 +103,22 @@ export class UpdaterMain { } async checkForUpdate(withFeedback = false) { - if (this.doingUpdateCheck || isDev()) { + if (isDev()) { + return; + } + + if (this.updateDownloaded && withFeedback) { + await this.promptRestartUpdate(this.updateDownloaded); + return; + } + + if (this.doingUpdateCheck) { return; } if (!this.canUpdate) { if (withFeedback) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - shell.openExternal("https://github.com/bitwarden/clients/releases"); + void shell.openExternal("https://github.com/bitwarden/clients/releases"); } return; @@ -134,6 +127,10 @@ export class UpdaterMain { this.doingUpdateCheckWithFeedback = withFeedback; if (withFeedback) { autoUpdater.autoDownload = false; + + // If the user has explicitly checked for updates, we want to bypass + // the current staging rollout percentage + autoUpdater.isUserWithinRollout = (info) => true; } await autoUpdater.checkForUpdates(); @@ -141,7 +138,29 @@ export class UpdaterMain { private reset() { autoUpdater.autoDownload = true; + // Reset the rollout check to the default behavior + autoUpdater.isUserWithinRollout = this.originalRolloutFunction; this.doingUpdateCheck = false; + this.updateDownloaded = null; + } + + private async promptRestartUpdate(info: UpdateDownloadedEvent) { + const result = await dialog.showMessageBox(this.windowMain.win, { + type: "info", + title: this.i18nService.t("bitwarden") + " - " + this.i18nService.t("restartToUpdate"), + message: this.i18nService.t("restartToUpdate"), + detail: this.i18nService.t("restartToUpdateDesc", info.version), + buttons: [this.i18nService.t("restart"), this.i18nService.t("later")], + cancelId: 1, + defaultId: 0, + noLink: true, + }); + + if (result.response === 0) { + // Quit and install have a different window logic, setting `isQuitting` just to be safe. + this.windowMain.isQuitting = true; + autoUpdater.quitAndInstall(true, true); + } } private userDisabledUpdates(): boolean { diff --git a/package-lock.json b/package-lock.json index 367a31f0420..5bb5dc6e852 100644 --- a/package-lock.json +++ b/package-lock.json @@ -135,7 +135,7 @@ "electron-log": "5.2.4", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", - "electron-updater": "6.3.9", + "electron-updater": "6.6.4", "eslint": "8.57.1", "eslint-config-prettier": "10.1.2", "eslint-import-resolver-typescript": "4.3.4", @@ -18500,13 +18500,13 @@ "license": "ISC" }, "node_modules/electron-updater": { - "version": "6.3.9", - "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.9.tgz", - "integrity": "sha512-2PJNONi+iBidkoC5D1nzT9XqsE8Q1X28Fn6xRQhO3YX8qRRyJ3mkV4F1aQsuRnYPqq6Hw+E51y27W75WgDoofw==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.6.4.tgz", + "integrity": "sha512-RcuWXbXTQkI5X3vir/eJ9S0VZQJ73eaQdlrA2J0mVrb0sbkHtnkjBMGo9umFtX8axn6r/pShN3CORxdmiF4OOw==", "dev": true, "license": "MIT", "dependencies": { - "builder-util-runtime": "9.2.10", + "builder-util-runtime": "9.3.2", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", @@ -18517,9 +18517,9 @@ } }, "node_modules/electron-updater/node_modules/builder-util-runtime": { - "version": "9.2.10", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", - "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.3.2.tgz", + "integrity": "sha512-7QDXJ1FwT6d9ZhG4kuObUUPY8/ENBS/Ky26O4hR5vbeoRGavgekS2Jxv+8sCn/v23aPGU2DXRWEeJuijN2ooYA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7ed7ebb4224..b1af1abe606 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "electron-log": "5.2.4", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", - "electron-updater": "6.3.9", + "electron-updater": "6.6.4", "eslint": "8.57.1", "eslint-config-prettier": "10.1.2", "eslint-import-resolver-typescript": "4.3.4",