Skip to content

Commit

Permalink
feat(updater): add custom isUpdateSupported hook for validating upd…
Browse files Browse the repository at this point in the history
…ate downloads (#8692)
  • Loading branch information
mmaietta authored Feb 3, 2025
1 parent 14ee2d6 commit 96c5d14
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-starfishes-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"electron-updater": minor
---

feat: add support for custom `isUpdateSupported` hook for validating `UpdateInfo`, with fallback to previous `minimumSystemVersion` logic
57 changes: 45 additions & 12 deletions packages/electron-updater/src/AppUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -203,6 +213,22 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter
this.configOnDisk = new Lazy<any>(() => this.loadUpdateConfig())
}

protected _isUpdateSupported: VerifyUpdateSupport = (updateInfo: UpdateInfo): boolean | Promise<boolean> => 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<Provider<any>> | null = null

protected readonly stagingUserIdPromise = new Lazy<string>(() => this.getOrCreateStagingUserId())
Expand Down Expand Up @@ -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)
Expand All @@ -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<UpdateInfoAndProvider> {
await this.app.whenReady()

Expand Down
8 changes: 4 additions & 4 deletions packages/electron-updater/src/NsisUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -22,18 +22,18 @@ export class NsisUpdater extends BaseUpdater {
super(options, app)
}

protected _verifyUpdateCodeSignature: verifyUpdateCodeSignature = (publisherNames: Array<string>, unescapedTempUpdateFile: string) =>
protected _verifyUpdateCodeSignature: VerifyUpdateCodeSignature = (publisherNames: Array<string>, 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<string | null>`.
* 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
}
Expand Down
9 changes: 6 additions & 3 deletions packages/electron-updater/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | null>

export type verifyUpdateCodeSignature = (publisherName: string[], path: string) => Promise<string | null>
export type VerifyUpdateSupport = (updateInfo: UpdateInfo) => boolean | Promise<boolean>

0 comments on commit 96c5d14

Please sign in to comment.