From e841f8986ccd296370c12789d92b0d0b1afe4b38 Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Sat, 2 Mar 2024 16:01:32 -0800 Subject: [PATCH] Revert "feat: add support for differential zip updates on macOS (#7709)" This reverts commit 79df54238621fbe48ba20444129950ba2dc49983. --- packages/electron-updater/src/AppUpdater.ts | 62 -- packages/electron-updater/src/MacUpdater.ts | 41 +- packages/electron-updater/src/NsisUpdater.ts | 66 ++- .../updater/differentialUpdateTest.js.snap | 531 ------------------ test/src/updater/differentialUpdateTest.ts | 92 ++- 5 files changed, 112 insertions(+), 680 deletions(-) diff --git a/packages/electron-updater/src/AppUpdater.ts b/packages/electron-updater/src/AppUpdater.ts index 9f5ae0eb494..ffbcdcf147a 100644 --- a/packages/electron-updater/src/AppUpdater.ts +++ b/packages/electron-updater/src/AppUpdater.ts @@ -9,7 +9,6 @@ import { DownloadOptions, CancellationError, ProgressInfo, - BlockMap, } from "builder-util-runtime" import { randomBytes } from "crypto" import { EventEmitter } from "events" @@ -30,10 +29,6 @@ import { ProviderPlatform } from "./providers/Provider" import type { TypedEmitter } from "tiny-typed-emitter" import Session = Electron.Session import { AuthInfo } from "electron" -import { gunzipSync } from "zlib" -import { blockmapFiles } from "./util" -import { DifferentialDownloaderOptions } from "./differentialDownloader/DifferentialDownloader" -import { GenericDifferentialDownloader } from "./differentialDownloader/GenericDifferentialDownloader" export type AppUpdaterEvents = { error: (error: Error, message?: string) => void @@ -702,63 +697,6 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter log.info(`New version ${version} has been downloaded to ${updateFile}`) return await done(true) } - protected async differentialDownloadInstaller( - fileInfo: ResolvedUpdateFileInfo, - downloadUpdateOptions: DownloadUpdateOptions, - installerPath: string, - provider: Provider, - oldInstallerFileName: string - ): Promise { - try { - if (this._testOnlyOptions != null && !this._testOnlyOptions.isUseDifferentialDownload) { - return true - } - const blockmapFileUrls = blockmapFiles(fileInfo.url, this.app.version, downloadUpdateOptions.updateInfoAndProvider.info.version) - this._logger.info(`Download block maps (old: "${blockmapFileUrls[0]}", new: ${blockmapFileUrls[1]})`) - - const downloadBlockMap = async (url: URL): Promise => { - const data = await this.httpExecutor.downloadToBuffer(url, { - headers: downloadUpdateOptions.requestHeaders, - cancellationToken: downloadUpdateOptions.cancellationToken, - }) - - if (data == null || data.length === 0) { - throw new Error(`Blockmap "${url.href}" is empty`) - } - - try { - return JSON.parse(gunzipSync(data).toString()) - } catch (e: any) { - throw new Error(`Cannot parse blockmap "${url.href}", error: ${e}`) - } - } - - const downloadOptions: DifferentialDownloaderOptions = { - newUrl: fileInfo.url, - oldFile: path.join(this.downloadedUpdateHelper!.cacheDir, oldInstallerFileName), - logger: this._logger, - newFile: installerPath, - isUseMultipleRangeRequest: provider.isUseMultipleRangeRequest, - requestHeaders: downloadUpdateOptions.requestHeaders, - cancellationToken: downloadUpdateOptions.cancellationToken, - } - - if (this.listenerCount(DOWNLOAD_PROGRESS) > 0) { - downloadOptions.onProgress = it => this.emit(DOWNLOAD_PROGRESS, it) - } - - const blockMapDataList = await Promise.all(blockmapFileUrls.map(u => downloadBlockMap(u))) - await new GenericDifferentialDownloader(fileInfo.info, this.httpExecutor, downloadOptions).download(blockMapDataList[0], blockMapDataList[1]) - return false - } catch (e: any) { - this._logger.error(`Cannot download differentially, fallback to full download: ${e.stack || e}`) - if (this._testOnlyOptions != null) { - // test mode - throw e - } - return true - } - } } export interface DownloadUpdateOptions { diff --git a/packages/electron-updater/src/MacUpdater.ts b/packages/electron-updater/src/MacUpdater.ts index 9816d54e616..44043bb9a38 100644 --- a/packages/electron-updater/src/MacUpdater.ts +++ b/packages/electron-updater/src/MacUpdater.ts @@ -1,7 +1,6 @@ import { AllPublishOptions, newError, safeStringifyJson } from "builder-util-runtime" -import { pathExistsSync, stat } from "fs-extra" -import { createReadStream, copyFileSync } from "fs" -import * as path from "path" +import { stat } from "fs-extra" +import { createReadStream } from "fs" import { createServer, IncomingMessage, Server, ServerResponse } from "http" import { AppAdapter } from "./AppAdapter" import { AppUpdater, DownloadUpdateOptions } from "./AppUpdater" @@ -27,7 +26,6 @@ export class MacUpdater extends AppUpdater { }) this.nativeUpdater.on("update-downloaded", () => { this.squirrelDownloadedUpdate = true - this.debug("nativeUpdater.update-downloaded") }) } @@ -37,17 +35,6 @@ export class MacUpdater extends AppUpdater { } } - private closeServerIfExists() { - if (this.server) { - this.debug("Closing proxy server") - this.server.close(err => { - if (err) { - this.debug("proxy server wasn't already open, probably attempted closing again as a safety check before quit") - } - }) - } - } - protected async doDownloadUpdate(downloadUpdateOptions: DownloadUpdateOptions): Promise> { let files = downloadUpdateOptions.updateInfoAndProvider.provider.resolveFiles(downloadUpdateOptions.updateInfoAndProvider.info) @@ -92,26 +79,12 @@ export class MacUpdater extends AppUpdater { throw newError(`ZIP file not provided: ${safeStringifyJson(files)}`, "ERR_UPDATER_ZIP_FILE_NOT_FOUND") } - const provider = downloadUpdateOptions.updateInfoAndProvider.provider - const CURRENT_MAC_APP_ZIP_FILE_NAME = "update.zip" - return this.executeDownload({ fileExtension: "zip", fileInfo: zipFileInfo, downloadUpdateOptions, - task: async (destinationFile, downloadOptions) => { - const cachedFile = path.join(this.downloadedUpdateHelper!.cacheDir, CURRENT_MAC_APP_ZIP_FILE_NAME) - const canDifferentialDownload = () => { - if (!pathExistsSync(cachedFile)) { - log.info("Unable to locate previous update.zip for differential download (is this first install?), falling back to full download") - return false - } - return !downloadUpdateOptions.disableDifferentialDownload - } - if (canDifferentialDownload() && (await this.differentialDownloadInstaller(zipFileInfo, downloadUpdateOptions, destinationFile, provider, CURRENT_MAC_APP_ZIP_FILE_NAME))) { - await this.httpExecutor.download(zipFileInfo.url, destinationFile, downloadOptions) - } - copyFileSync(destinationFile, cachedFile) + task: (destinationFile, downloadOptions) => { + return this.httpExecutor.download(zipFileInfo.url, destinationFile, downloadOptions) }, done: event => this.updateDownloaded(zipFileInfo, event), }) @@ -123,8 +96,8 @@ export class MacUpdater extends AppUpdater { const log = this._logger const logContext = `fileToProxy=${zipFileInfo.url.href}` - this.closeServerIfExists() this.debug(`Creating proxy server for native Squirrel.Mac (${logContext})`) + this.server?.close() this.server = createServer() this.debug(`Proxy server for native Squirrel.Mac is created (${logContext})`) this.server.on("close", () => { @@ -243,12 +216,12 @@ export class MacUpdater extends AppUpdater { if (this.squirrelDownloadedUpdate) { // update already fetched by Squirrel, it's ready to install this.nativeUpdater.quitAndInstall() - this.closeServerIfExists() + this.server?.close() } else { // Quit and install as soon as Squirrel get the update this.nativeUpdater.on("update-downloaded", () => { this.nativeUpdater.quitAndInstall() - this.closeServerIfExists() + this.server?.close() }) if (!this.autoInstallOnAppQuit) { diff --git a/packages/electron-updater/src/NsisUpdater.ts b/packages/electron-updater/src/NsisUpdater.ts index de4780cc235..a76ca432cf4 100644 --- a/packages/electron-updater/src/NsisUpdater.ts +++ b/packages/electron-updater/src/NsisUpdater.ts @@ -1,15 +1,18 @@ -import { AllPublishOptions, newError, PackageFileInfo, CURRENT_APP_INSTALLER_FILE_NAME, CURRENT_APP_PACKAGE_FILE_NAME } from "builder-util-runtime" +import { AllPublishOptions, newError, PackageFileInfo, BlockMap, CURRENT_APP_PACKAGE_FILE_NAME, CURRENT_APP_INSTALLER_FILE_NAME } from "builder-util-runtime" import * as path from "path" import { AppAdapter } from "./AppAdapter" 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 { GenericDifferentialDownloader } from "./differentialDownloader/GenericDifferentialDownloader" +import { DOWNLOAD_PROGRESS, ResolvedUpdateFileInfo, verifyUpdateCodeSignature } from "./main" +import { blockmapFiles } from "./util" import { findFile, Provider } from "./providers/Provider" import { unlink } from "fs-extra" import { verifySignature } from "./windowsExecutableCodeSignatureVerifier" import { URL } from "url" +import { gunzipSync } from "zlib" export class NsisUpdater extends BaseUpdater { /** @@ -64,7 +67,7 @@ export class NsisUpdater extends BaseUpdater { if ( isWebInstaller || downloadUpdateOptions.disableDifferentialDownload || - (await this.differentialDownloadInstaller(fileInfo, downloadUpdateOptions, destinationFile, provider, CURRENT_APP_INSTALLER_FILE_NAME)) + (await this.differentialDownloadInstaller(fileInfo, downloadUpdateOptions, destinationFile, provider)) ) { await this.httpExecutor.download(fileInfo.url, destinationFile, downloadOptions) } @@ -173,6 +176,63 @@ export class NsisUpdater extends BaseUpdater { return true } + private async differentialDownloadInstaller( + fileInfo: ResolvedUpdateFileInfo, + downloadUpdateOptions: DownloadUpdateOptions, + installerPath: string, + provider: Provider + ): Promise { + try { + if (this._testOnlyOptions != null && !this._testOnlyOptions.isUseDifferentialDownload) { + return true + } + const blockmapFileUrls = blockmapFiles(fileInfo.url, this.app.version, downloadUpdateOptions.updateInfoAndProvider.info.version) + this._logger.info(`Download block maps (old: "${blockmapFileUrls[0]}", new: ${blockmapFileUrls[1]})`) + + const downloadBlockMap = async (url: URL): Promise => { + const data = await this.httpExecutor.downloadToBuffer(url, { + headers: downloadUpdateOptions.requestHeaders, + cancellationToken: downloadUpdateOptions.cancellationToken, + }) + + if (data == null || data.length === 0) { + throw new Error(`Blockmap "${url.href}" is empty`) + } + + try { + return JSON.parse(gunzipSync(data).toString()) + } catch (e: any) { + throw new Error(`Cannot parse blockmap "${url.href}", error: ${e}`) + } + } + + const downloadOptions: DifferentialDownloaderOptions = { + newUrl: fileInfo.url, + oldFile: path.join(this.downloadedUpdateHelper!.cacheDir, CURRENT_APP_INSTALLER_FILE_NAME), + logger: this._logger, + newFile: installerPath, + isUseMultipleRangeRequest: provider.isUseMultipleRangeRequest, + requestHeaders: downloadUpdateOptions.requestHeaders, + cancellationToken: downloadUpdateOptions.cancellationToken, + } + + if (this.listenerCount(DOWNLOAD_PROGRESS) > 0) { + downloadOptions.onProgress = it => this.emit(DOWNLOAD_PROGRESS, it) + } + + const blockMapDataList = await Promise.all(blockmapFileUrls.map(u => downloadBlockMap(u))) + await new GenericDifferentialDownloader(fileInfo.info, this.httpExecutor, downloadOptions).download(blockMapDataList[0], blockMapDataList[1]) + return false + } catch (e: any) { + this._logger.error(`Cannot download differentially, fallback to full download: ${e.stack || e}`) + if (this._testOnlyOptions != null) { + // test mode + throw e + } + return true + } + } + private async differentialDownloadWebPackage( downloadUpdateOptions: DownloadUpdateOptions, packageInfo: PackageFileInfo, diff --git a/test/snapshots/updater/differentialUpdateTest.js.snap b/test/snapshots/updater/differentialUpdateTest.js.snap index bb158b6ca60..aa982b54418 100644 --- a/test/snapshots/updater/differentialUpdateTest.js.snap +++ b/test/snapshots/updater/differentialUpdateTest.js.snap @@ -180,537 +180,6 @@ Array [ ] `; -exports[`Mac Intel 1`] = ` -Object { - "mac": Array [ - Object { - "file": "latest-mac.yml", - "fileContent": Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - "url": "Test App ßW-1.0.0-mac.zip", - }, - ], - "path": "Test App ßW-1.0.0-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.0", - }, - }, - Object { - "arch": "x64", - "file": "Test App ßW-1.0.0-mac.zip", - "safeArtifactName": "TestApp-1.0.0-mac.zip", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - Object { - "file": "Test App ßW-1.0.0-mac.zip.blockmap", - "safeArtifactName": "Test App ßW-1.0.0-mac.zip.blockmap", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - ], -} -`; - -exports[`Mac Intel 2`] = ` -Object { - "CFBundleDisplayName": "Test App ßW", - "CFBundleExecutable": "Test App ßW", - "CFBundleIconFile": "icon.icns", - "CFBundleIdentifier": "org.electron-builder.testApp", - "CFBundleInfoDictionaryVersion": "6.0", - "CFBundleName": "Test App ßW", - "CFBundlePackageType": "APPL", - "CFBundleShortVersionString": "1.0.0", - "LSApplicationCategoryType": "your.app.category.type", - "NSAppTransportSecurity": Object { - "NSAllowsLocalNetworking": true, - "NSExceptionDomains": Object { - "127.0.0.1": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - "localhost": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - }, - }, - "NSBluetoothAlwaysUsageDescription": "This app needs access to Bluetooth", - "NSBluetoothPeripheralUsageDescription": "This app needs access to Bluetooth", - "NSHighResolutionCapable": true, - "NSPrincipalClass": "AtomApplication", - "NSSupportsAutomaticGraphicsSwitching": true, -} -`; - -exports[`Mac Intel 3`] = ` -Object { - "mac": Array [ - Object { - "file": "latest-mac.yml", - "fileContent": Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - "url": "Test App ßW-1.0.1-mac.zip", - }, - ], - "path": "Test App ßW-1.0.1-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.1", - }, - }, - Object { - "arch": "x64", - "file": "Test App ßW-1.0.1-mac.zip", - "safeArtifactName": "TestApp-1.0.1-mac.zip", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - Object { - "file": "Test App ßW-1.0.1-mac.zip.blockmap", - "safeArtifactName": "Test App ßW-1.0.1-mac.zip.blockmap", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - ], -} -`; - -exports[`Mac Intel 4`] = ` -Object { - "CFBundleDisplayName": "Test App ßW", - "CFBundleExecutable": "Test App ßW", - "CFBundleIconFile": "icon.icns", - "CFBundleIdentifier": "org.electron-builder.testApp", - "CFBundleInfoDictionaryVersion": "6.0", - "CFBundleName": "Test App ßW", - "CFBundlePackageType": "APPL", - "CFBundleShortVersionString": "1.0.1", - "LSApplicationCategoryType": "your.app.category.type", - "NSAppTransportSecurity": Object { - "NSAllowsLocalNetworking": true, - "NSExceptionDomains": Object { - "127.0.0.1": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - "localhost": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - }, - }, - "NSBluetoothAlwaysUsageDescription": "This app needs access to Bluetooth", - "NSBluetoothPeripheralUsageDescription": "This app needs access to Bluetooth", - "NSHighResolutionCapable": true, - "NSPrincipalClass": "AtomApplication", - "NSSupportsAutomaticGraphicsSwitching": true, -} -`; - -exports[`Mac Intel 5`] = ` -Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - }, - ], - "path": "Test App ßW-1.0.1-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.1", -} -`; - -exports[`Mac Intel 6`] = ` -Array [ - "Test App ßW-1.0.1-mac.zip", -] -`; - -exports[`Mac arm64 1`] = ` -Object { - "mac": Array [ - Object { - "file": "latest-mac.yml", - "fileContent": Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - "url": "Test App ßW-1.0.0-arm64-mac.zip", - }, - ], - "path": "Test App ßW-1.0.0-arm64-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.0", - }, - }, - Object { - "arch": "arm64", - "file": "Test App ßW-1.0.0-arm64-mac.zip", - "safeArtifactName": "TestApp-1.0.0-arm64-mac.zip", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - Object { - "file": "Test App ßW-1.0.0-arm64-mac.zip.blockmap", - "safeArtifactName": "Test App ßW-1.0.0-arm64-mac.zip.blockmap", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - ], -} -`; - -exports[`Mac arm64 2`] = ` -Object { - "CFBundleDisplayName": "Test App ßW", - "CFBundleExecutable": "Test App ßW", - "CFBundleIconFile": "icon.icns", - "CFBundleIdentifier": "org.electron-builder.testApp", - "CFBundleInfoDictionaryVersion": "6.0", - "CFBundleName": "Test App ßW", - "CFBundlePackageType": "APPL", - "CFBundleShortVersionString": "1.0.0", - "LSApplicationCategoryType": "your.app.category.type", - "NSAppTransportSecurity": Object { - "NSAllowsLocalNetworking": true, - "NSExceptionDomains": Object { - "127.0.0.1": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - "localhost": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - }, - }, - "NSBluetoothAlwaysUsageDescription": "This app needs access to Bluetooth", - "NSBluetoothPeripheralUsageDescription": "This app needs access to Bluetooth", - "NSHighResolutionCapable": true, - "NSPrincipalClass": "AtomApplication", - "NSSupportsAutomaticGraphicsSwitching": true, -} -`; - -exports[`Mac arm64 3`] = ` -Object { - "mac": Array [ - Object { - "file": "latest-mac.yml", - "fileContent": Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - "url": "Test App ßW-1.0.1-arm64-mac.zip", - }, - ], - "path": "Test App ßW-1.0.1-arm64-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.1", - }, - }, - Object { - "arch": "arm64", - "file": "Test App ßW-1.0.1-arm64-mac.zip", - "safeArtifactName": "TestApp-1.0.1-arm64-mac.zip", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - Object { - "file": "Test App ßW-1.0.1-arm64-mac.zip.blockmap", - "safeArtifactName": "Test App ßW-1.0.1-arm64-mac.zip.blockmap", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - ], -} -`; - -exports[`Mac arm64 4`] = ` -Object { - "CFBundleDisplayName": "Test App ßW", - "CFBundleExecutable": "Test App ßW", - "CFBundleIconFile": "icon.icns", - "CFBundleIdentifier": "org.electron-builder.testApp", - "CFBundleInfoDictionaryVersion": "6.0", - "CFBundleName": "Test App ßW", - "CFBundlePackageType": "APPL", - "CFBundleShortVersionString": "1.0.1", - "LSApplicationCategoryType": "your.app.category.type", - "NSAppTransportSecurity": Object { - "NSAllowsLocalNetworking": true, - "NSExceptionDomains": Object { - "127.0.0.1": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - "localhost": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - }, - }, - "NSBluetoothAlwaysUsageDescription": "This app needs access to Bluetooth", - "NSBluetoothPeripheralUsageDescription": "This app needs access to Bluetooth", - "NSHighResolutionCapable": true, - "NSPrincipalClass": "AtomApplication", - "NSSupportsAutomaticGraphicsSwitching": true, -} -`; - -exports[`Mac arm64 5`] = ` -Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - }, - ], - "path": "Test App ßW-1.0.1-arm64-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.1", -} -`; - -exports[`Mac arm64 6`] = ` -Array [ - "Test App ßW-1.0.1-arm64-mac.zip", -] -`; - -exports[`Mac universal 1`] = ` -Object { - "mac": Array [ - Object { - "file": "latest-mac.yml", - "fileContent": Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - "url": "Test App ßW-1.0.0-universal-mac.zip", - }, - ], - "path": "Test App ßW-1.0.0-universal-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.0", - }, - }, - Object { - "arch": "universal", - "file": "Test App ßW-1.0.0-universal-mac.zip", - "safeArtifactName": "TestApp-1.0.0-universal-mac.zip", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - Object { - "file": "Test App ßW-1.0.0-universal-mac.zip.blockmap", - "safeArtifactName": "Test App ßW-1.0.0-universal-mac.zip.blockmap", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - ], -} -`; - -exports[`Mac universal 2`] = ` -Object { - "CFBundleDisplayName": "Test App ßW", - "CFBundleExecutable": "Test App ßW", - "CFBundleIconFile": "icon.icns", - "CFBundleIdentifier": "org.electron-builder.testApp", - "CFBundleInfoDictionaryVersion": "6.0", - "CFBundleName": "Test App ßW", - "CFBundlePackageType": "APPL", - "CFBundleShortVersionString": "1.0.0", - "LSApplicationCategoryType": "your.app.category.type", - "NSAppTransportSecurity": Object { - "NSAllowsLocalNetworking": true, - "NSExceptionDomains": Object { - "127.0.0.1": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - "localhost": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - }, - }, - "NSBluetoothAlwaysUsageDescription": "This app needs access to Bluetooth", - "NSBluetoothPeripheralUsageDescription": "This app needs access to Bluetooth", - "NSHighResolutionCapable": true, - "NSPrincipalClass": "AtomApplication", - "NSSupportsAutomaticGraphicsSwitching": true, -} -`; - -exports[`Mac universal 3`] = ` -Object { - "mac": Array [ - Object { - "file": "latest-mac.yml", - "fileContent": Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - "url": "Test App ßW-1.0.1-universal-mac.zip", - }, - ], - "path": "Test App ßW-1.0.1-universal-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.1", - }, - }, - Object { - "arch": "universal", - "file": "Test App ßW-1.0.1-universal-mac.zip", - "safeArtifactName": "TestApp-1.0.1-universal-mac.zip", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - Object { - "file": "Test App ßW-1.0.1-universal-mac.zip.blockmap", - "safeArtifactName": "Test App ßW-1.0.1-universal-mac.zip.blockmap", - "updateInfo": Object { - "sha512": "@sha512", - "size": "@size", - }, - }, - ], -} -`; - -exports[`Mac universal 4`] = ` -Object { - "CFBundleDisplayName": "Test App ßW", - "CFBundleExecutable": "Test App ßW", - "CFBundleIconFile": "icon.icns", - "CFBundleIdentifier": "org.electron-builder.testApp", - "CFBundleInfoDictionaryVersion": "6.0", - "CFBundleName": "Test App ßW", - "CFBundlePackageType": "APPL", - "CFBundleShortVersionString": "1.0.1", - "LSApplicationCategoryType": "your.app.category.type", - "NSAppTransportSecurity": Object { - "NSAllowsLocalNetworking": true, - "NSExceptionDomains": Object { - "127.0.0.1": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - "localhost": Object { - "NSIncludesSubdomains": false, - "NSTemporaryExceptionAllowsInsecureHTTPLoads": true, - "NSTemporaryExceptionAllowsInsecureHTTPSLoads": false, - "NSTemporaryExceptionMinimumTLSVersion": "1.0", - "NSTemporaryExceptionRequiresForwardSecrecy": false, - }, - }, - }, - "NSBluetoothAlwaysUsageDescription": "This app needs access to Bluetooth", - "NSBluetoothPeripheralUsageDescription": "This app needs access to Bluetooth", - "NSHighResolutionCapable": true, - "NSPrincipalClass": "AtomApplication", - "NSSupportsAutomaticGraphicsSwitching": true, -} -`; - -exports[`Mac universal 5`] = ` -Object { - "files": Array [ - Object { - "sha512": "@sha512", - "size": "@size", - }, - ], - "path": "Test App ßW-1.0.1-universal-mac.zip", - "releaseDate": "@releaseDate", - "sha512": "@sha512", - "version": "1.0.1", -} -`; - -exports[`Mac universal 6`] = ` -Array [ - "Test App ßW-1.0.1-universal-mac.zip", -] -`; - exports[`nsis 1`] = ` Object { "win": Array [ diff --git a/test/src/updater/differentialUpdateTest.ts b/test/src/updater/differentialUpdateTest.ts index 1699dcb8f55..9ba8cc9be66 100644 --- a/test/src/updater/differentialUpdateTest.ts +++ b/test/src/updater/differentialUpdateTest.ts @@ -1,8 +1,8 @@ import { Arch, Configuration, Platform } from "app-builder-lib" import { getBinFromUrl } from "app-builder-lib/out/binDownload" -import { doSpawn, getArchSuffix } from "builder-util" +import { doSpawn } from "builder-util" import { GenericServerOptions, S3Options } from "builder-util-runtime" -import { AppImageUpdater, BaseUpdater, MacUpdater, NsisUpdater } from "electron-updater" +import { AppImageUpdater, MacUpdater, NsisUpdater } from "electron-updater" import { EventEmitter } from "events" import { move } from "fs-extra" import * as path from "path" @@ -80,7 +80,7 @@ test.ifWindows("web installer", async () => { const oldDir = outDirs[0] await move(path.join(oldDir, "nsis-web", `TestApp-${OLD_VERSION_NUMBER}-x64.nsis.7z`), path.join(getTestUpdaterCacheDir(oldDir), testAppCacheDirName, "package.7z")) - await testBlockMap(outDirs[0], path.join(outDirs[1], "nsis-web"), NsisUpdater, Platform.WINDOWS, Arch.x64) + await testBlockMap(outDirs[0], path.join(outDirs[1], "nsis-web"), NsisUpdater, "win-unpacked", Platform.WINDOWS) }) test.ifWindows("nsis", async () => { @@ -89,11 +89,10 @@ test.ifWindows("nsis", async () => { await doBuild(outDirs, Platform.WINDOWS.createTarget(["nsis"], Arch.x64), tmpDir, true) const oldDir = outDirs[0] - // move to new dir so that localhost server can read both blockmaps await move(path.join(oldDir, `Test App ßW Setup ${OLD_VERSION_NUMBER}.exe`), path.join(getTestUpdaterCacheDir(oldDir), testAppCacheDirName, "installer.exe")) await move(path.join(oldDir, `Test App ßW Setup ${OLD_VERSION_NUMBER}.exe.blockmap`), path.join(outDirs[1], "Test App ßW Setup 1.0.0.exe.blockmap")) - await testBlockMap(outDirs[0], outDirs[1], NsisUpdater, Platform.WINDOWS, Arch.x64) + await testBlockMap(outDirs[0], outDirs[1], NsisUpdater, "win-unpacked", Platform.WINDOWS) }) async function testLinux(arch: Arch) { @@ -105,7 +104,7 @@ async function testLinux(arch: Arch) { await doBuild(outDirs, Platform.LINUX.createTarget(["appimage"], arch), tmpDir, false) process.env.APPIMAGE = path.join(outDirs[0], `Test App ßW-${OLD_VERSION_NUMBER}${arch === Arch.ia32 ? "-i386" : ""}.AppImage`) - await testBlockMap(outDirs[0], outDirs[1], AppImageUpdater, Platform.LINUX, arch) + await testBlockMap(outDirs[0], outDirs[1], AppImageUpdater, `linux-${arch === Arch.ia32 ? "ia32-" : ""}unpacked`, Platform.LINUX) } finally { await tmpDir.cleanup() } @@ -115,40 +114,20 @@ test.ifDevOrLinuxCi("AppImage", () => testLinux(Arch.x64)) test.ifDevOrLinuxCi("AppImage ia32", () => testLinux(Arch.ia32)) -async function testMac(arch: Arch) { - process.env.TEST_UPDATER_ARCH = Arch[arch] - +// ifAll.ifMac.ifNotCi todo +test.skip("zip", async () => { const outDirs: Array = [] const tmpDir = new TmpDir("differential-updater-test") - try { - await doBuild(outDirs, Platform.MAC.createTarget(["zip"], arch), tmpDir, false, { - mac: { - electronUpdaterCompatibility: ">=2.17.0", - }, - }) - - // move to new dir so that localhost server can read both blockmaps - const oldDir = outDirs[0] - const blockmap = `Test App ßW-${OLD_VERSION_NUMBER}${getArchSuffix(arch)}-mac.zip.blockmap` - await move(path.join(oldDir, blockmap), path.join(outDirs[1], blockmap)) - await move(path.join(oldDir, `Test App ßW-${OLD_VERSION_NUMBER}${getArchSuffix(arch)}-mac.zip`), path.join(getTestUpdaterCacheDir(oldDir), testAppCacheDirName, "update.zip")) - - await testBlockMap(outDirs[0], outDirs[1], MacUpdater, Platform.MAC, arch, "Test App ßW") - } finally { - await tmpDir.cleanup() - } -} - -test.ifMac("Mac Intel", () => testMac(Arch.x64)) -test.ifMac("Mac universal", () => testMac(Arch.universal)) - -// only run on arm64 macs, otherwise of course no files can be found to be updated to (due to arch mismatch) -test.ifMac.ifEnv(process.arch === "arm64")("Mac arm64", () => testMac(Arch.arm64)) + await doBuild(outDirs, Platform.MAC.createTarget(["zip"], Arch.x64), tmpDir, false, { + mac: { + electronUpdaterCompatibility: ">=2.17.0", + }, + }) -async function checkResult(updater: BaseUpdater) { - // disable automatic install otherwise mac updater will permanently wait on mocked electron's native updater to receive update (mocked server can't install) - updater.autoInstallOnAppQuit = false + await testBlockMap(outDirs[0], path.join(outDirs[1]), MacUpdater, "mac/Test App ßW.app", Platform.MAC) +}) +async function checkResult(updater: NsisUpdater) { const updateCheckResult = await updater.checkForUpdates() const downloadPromise = updateCheckResult?.downloadPromise // noinspection JSIgnoredPromiseFromCall @@ -156,7 +135,7 @@ async function checkResult(updater: BaseUpdater) { const files = await downloadPromise const fileInfo: any = updateCheckResult?.updateInfo.files[0] - // delete url because port is random + // because port is random expect(fileInfo.url).toBeDefined() delete fileInfo.url expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() @@ -164,13 +143,27 @@ async function checkResult(updater: BaseUpdater) { } class TestNativeUpdater extends EventEmitter { + // private updateUrl: string | null = null + + // noinspection JSMethodCanBeStatic checkForUpdates() { console.log("TestNativeUpdater.checkForUpdates") // MacUpdater expects this to emit corresponding update-downloaded event this.emit("update-downloaded") + // this.download() + // .catch(error => { + // this.emit("error", error) + // }) } - setFeedURL(updateConfig: any) { - console.log("TestNativeUpdater.setFeedURL " + updateConfig.url) + + // private async download() { + // } + + // noinspection JSMethodCanBeStatic + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setFeedURL(_updateUrl: string) { + // console.log("TestNativeUpdater.setFeedURL " + updateUrl) + // this.updateUrl = updateUrl } } @@ -178,17 +171,14 @@ function getTestUpdaterCacheDir(oldDir: string) { return path.join(oldDir, "updater-cache") } -async function testBlockMap(oldDir: string, newDir: string, updaterClass: any, platform: Platform, arch: Arch, productFilename?: string) { - const appUpdateConfigPath = path.join( - `${platform.buildConfigurationKey}${getArchSuffix(arch)}${platform === Platform.MAC ? "" : "-unpacked"}`, - platform === Platform.MAC ? `${productFilename}.app` : "" - ) +async function testBlockMap(oldDir: string, newDir: string, updaterClass: any, appUpdateConfigPath: string, platform: Platform) { const port = 8000 + (updaterClass.name.charCodeAt(0) as number) + Math.floor(Math.random() * 10000) - const serverBin = await getBinFromUrl("ran", "0.1.3", "imfA3LtT6umMM0BuQ29MgO3CJ9uleN5zRBi3sXzcTbMOeYZ6SQeN7eKr3kXZikKnVOIwbH+DDO43wkiR/qTdkg==") - const httpServerProcess = doSpawn(path.join(serverBin, process.platform, "ran"), [`-root=${newDir}`, `-port=${port}`, "-gzip=false", "-listdir=true"]) - - // Mac uses electron's native autoUpdater to serve updates to, we mock here since electron API isn't available within jest runtime + // noinspection SpellCheckingInspection + const httpServerProcess = doSpawn( + path.join(await getBinFromUrl("ran", "0.1.3", "imfA3LtT6umMM0BuQ29MgO3CJ9uleN5zRBi3sXzcTbMOeYZ6SQeN7eKr3kXZikKnVOIwbH+DDO43wkiR/qTdkg=="), process.platform, "ran"), + [`-root=${newDir}`, `-port=${port}`, "-gzip=false", "-listdir=true"] + ) const mockNativeUpdater = new TestNativeUpdater() jest.mock( "electron", @@ -200,7 +190,7 @@ async function testBlockMap(oldDir: string, newDir: string, updaterClass: any, p { virtual: true } ) - return await new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { httpServerProcess.on("error", reject) const updater = new updaterClass(null, new TestAppAdapter(OLD_VERSION_NUMBER, getTestUpdaterCacheDir(oldDir))) @@ -230,7 +220,9 @@ async function testBlockMap(oldDir: string, newDir: string, updaterClass: any, p await checkResult(updater) } - doTest().then(resolve).catch(reject) + doTest() + .then(() => resolve(null)) + .catch(reject) }).then( v => { httpServerProcess.kill()