diff --git a/.changeset/smart-starfishes-invent.md b/.changeset/smart-starfishes-invent.md new file mode 100644 index 00000000000..36ba6972c4f --- /dev/null +++ b/.changeset/smart-starfishes-invent.md @@ -0,0 +1,5 @@ +--- +"electron-updater": minor +--- + +feat: add support for custom `isUpdateSupported` hook for validating `UpdateInfo`, with fallback to previous `minimumSystemVersion` logic diff --git a/packages/electron-updater/src/AppUpdater.ts b/packages/electron-updater/src/AppUpdater.ts index 3b0fe5e5b39..be764d1d1c7 100644 --- a/packages/electron-updater/src/AppUpdater.ts +++ b/packages/electron-updater/src/AppUpdater.ts @@ -26,7 +26,17 @@ import { createTempUpdateFile, DownloadedUpdateHelper } from "./DownloadedUpdate import { ElectronAppAdapter } from "./ElectronAppAdapter" import { ElectronHttpExecutor, getNetSession, LoginCallback } from "./electronHttpExecutor" import { GenericProvider } from "./providers/GenericProvider" -import { DOWNLOAD_PROGRESS, Logger, Provider, ResolvedUpdateFileInfo, UPDATE_DOWNLOADED, UpdateCheckResult, UpdateDownloadedEvent, UpdaterSignal } from "./main" +import { + DOWNLOAD_PROGRESS, + Logger, + Provider, + ResolvedUpdateFileInfo, + UPDATE_DOWNLOADED, + UpdateCheckResult, + UpdateDownloadedEvent, + UpdaterSignal, + VerifyUpdateSupport, +} from "./main" import { createClient, isUrlProbablySupportMultiRangeRequests } from "./providerFactory" import { ProviderPlatform } from "./providers/Provider" import type { TypedEmitter } from "tiny-typed-emitter" @@ -203,6 +213,22 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter this.configOnDisk = new Lazy(() => this.loadUpdateConfig()) } + protected _isUpdateSupported: VerifyUpdateSupport = (updateInfo: UpdateInfo): boolean | Promise => this.checkIfUpdateSupported(updateInfo) + + /** + * Allows developer to override default logic for determining if an update is supported. + * The default logic compares the `UpdateInfo` minimum system version against the `os.release()` with `semver` package + */ + get isUpdateSupported(): VerifyUpdateSupport { + return this._isUpdateSupported + } + + set isUpdateSupported(value: VerifyUpdateSupport) { + if (value) { + this._isUpdateSupported = value + } + } + private clientPromise: Promise> | null = null protected readonly stagingUserIdPromise = new Lazy(() => this.getOrCreateStagingUserId()) @@ -396,17 +422,8 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter return false } - const minimumSystemVersion = updateInfo?.minimumSystemVersion - const currentOSVersion = release() - if (minimumSystemVersion) { - try { - if (isVersionLessThan(currentOSVersion, minimumSystemVersion)) { - this._logger.info(`Current OS version ${currentOSVersion} is less than the minimum OS version required ${minimumSystemVersion} for version ${currentOSVersion}`) - return false - } - } catch (e: any) { - this._logger.warn(`Failed to compare current OS version(${currentOSVersion}) with minimum OS version(${minimumSystemVersion}): ${(e.message || e).toString()}`) - } + if (!(await Promise.resolve(this.isUpdateSupported(updateInfo)))) { + return false } const isStagingMatch = await this.isStagingMatch(updateInfo) @@ -425,6 +442,22 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter return this.allowDowngrade && isLatestVersionOlder } + private checkIfUpdateSupported(updateInfo: UpdateInfo) { + const minimumSystemVersion = updateInfo?.minimumSystemVersion + const currentOSVersion = release() + if (minimumSystemVersion) { + try { + if (isVersionLessThan(currentOSVersion, minimumSystemVersion)) { + this._logger.info(`Current OS version ${currentOSVersion} is less than the minimum OS version required ${minimumSystemVersion} for version ${currentOSVersion}`) + return false + } + } catch (e: any) { + this._logger.warn(`Failed to compare current OS version(${currentOSVersion}) with minimum OS version(${minimumSystemVersion}): ${(e.message || e).toString()}`) + } + } + return true + } + protected async getUpdateInfoAndProvider(): Promise { await this.app.whenReady() diff --git a/packages/electron-updater/src/NsisUpdater.ts b/packages/electron-updater/src/NsisUpdater.ts index f2138b2cfdd..1d9755c178d 100644 --- a/packages/electron-updater/src/NsisUpdater.ts +++ b/packages/electron-updater/src/NsisUpdater.ts @@ -5,7 +5,7 @@ import { DownloadUpdateOptions } from "./AppUpdater" import { BaseUpdater, InstallOptions } from "./BaseUpdater" import { DifferentialDownloaderOptions } from "./differentialDownloader/DifferentialDownloader" import { FileWithEmbeddedBlockMapDifferentialDownloader } from "./differentialDownloader/FileWithEmbeddedBlockMapDifferentialDownloader" -import { DOWNLOAD_PROGRESS, verifyUpdateCodeSignature } from "./main" +import { DOWNLOAD_PROGRESS, VerifyUpdateCodeSignature } from "./main" import { findFile, Provider } from "./providers/Provider" import { unlink } from "fs-extra" import { verifySignature } from "./windowsExecutableCodeSignatureVerifier" @@ -22,18 +22,18 @@ export class NsisUpdater extends BaseUpdater { super(options, app) } - protected _verifyUpdateCodeSignature: verifyUpdateCodeSignature = (publisherNames: Array, unescapedTempUpdateFile: string) => + protected _verifyUpdateCodeSignature: VerifyUpdateCodeSignature = (publisherNames: Array, unescapedTempUpdateFile: string) => verifySignature(publisherNames, unescapedTempUpdateFile, this._logger) /** * The verifyUpdateCodeSignature. You can pass [win-verify-signature](/~https://github.com/beyondkmp/win-verify-trust) or another custom verify function: ` (publisherName: string[], path: string) => Promise`. * The default verify function uses [windowsExecutableCodeSignatureVerifier](/~https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts) */ - get verifyUpdateCodeSignature(): verifyUpdateCodeSignature { + get verifyUpdateCodeSignature(): VerifyUpdateCodeSignature { return this._verifyUpdateCodeSignature } - set verifyUpdateCodeSignature(value: verifyUpdateCodeSignature) { + set verifyUpdateCodeSignature(value: VerifyUpdateCodeSignature) { if (value) { this._verifyUpdateCodeSignature = value } diff --git a/packages/electron-updater/src/main.ts b/packages/electron-updater/src/main.ts index 60570d5d0bf..5f621ce3ad8 100644 --- a/packages/electron-updater/src/main.ts +++ b/packages/electron-updater/src/main.ts @@ -146,7 +146,10 @@ export interface Logger { debug?(message: string): void } -// return null if verify signature succeed -// return error message if verify signature failed +/** + * return null if verify signature succeed + * return error message if verify signature failed + */ +export type VerifyUpdateCodeSignature = (publisherName: string[], path: string) => Promise -export type verifyUpdateCodeSignature = (publisherName: string[], path: string) => Promise +export type VerifyUpdateSupport = (updateInfo: UpdateInfo) => boolean | Promise