diff --git a/.changeset/early-deers-suffer.md b/.changeset/early-deers-suffer.md new file mode 100644 index 00000000000..1d73dc6ccad --- /dev/null +++ b/.changeset/early-deers-suffer.md @@ -0,0 +1,5 @@ +--- +"app-builder-lib": minor +--- + +feat: allow disabling of building a universal windows installer diff --git a/packages/app-builder-lib/scheme.json b/packages/app-builder-lib/scheme.json index bd141e9ce3f..d6ca211f4cb 100644 --- a/packages/app-builder-lib/scheme.json +++ b/packages/app-builder-lib/scheme.json @@ -3896,6 +3896,11 @@ "string" ] }, + "buildUniversalInstaller": { + "default": true, + "description": "Disable building an universal installer of the archs specified in the target configuration\n*Not supported for nsis-web*", + "type": "boolean" + }, "createDesktopShortcut": { "default": true, "description": "Whether to create desktop shortcut. Set to `always` if to recreate also on reinstall (even if removed by user).", @@ -4196,6 +4201,11 @@ "string" ] }, + "buildUniversalInstaller": { + "default": true, + "description": "Disable building an universal installer of the archs specified in the target configuration\n*Not supported for nsis-web*", + "type": "boolean" + }, "createDesktopShortcut": { "default": true, "description": "Whether to create desktop shortcut. Set to `always` if to recreate also on reinstall (even if removed by user).", diff --git a/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts b/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts index 27f26b42285..d8e7a06f095 100644 --- a/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts +++ b/packages/app-builder-lib/src/targets/nsis/NsisTarget.ts @@ -1,5 +1,18 @@ import BluebirdPromise from "bluebird-lst" -import { Arch, asArray, AsyncTaskManager, exec, executeAppBuilder, getPlatformIconFileName, InvalidConfigurationError, log, spawnAndWrite, use, getPath7za } from "builder-util" +import { + Arch, + asArray, + AsyncTaskManager, + exec, + executeAppBuilder, + getPlatformIconFileName, + InvalidConfigurationError, + log, + spawnAndWrite, + use, + getPath7za, + getArchSuffix, +} from "builder-util" import { CURRENT_APP_INSTALLER_FILE_NAME, CURRENT_APP_PACKAGE_FILE_NAME, PackageFileInfo, UUID } from "builder-util-runtime" import { exists, statOrNull, walk } from "builder-util" import _debug from "debug" @@ -9,7 +22,7 @@ import * as path from "path" import { getBinFromUrl } from "../../binDownload" import { Target } from "../../core" import { DesktopShortcutCreationPolicy, getEffectiveOptions } from "../../options/CommonWindowsInstallerConfiguration" -import { computeSafeArtifactNameIfNeeded, normalizeExt } from "../../platformPackager" +import { chooseNotNull, computeSafeArtifactNameIfNeeded, normalizeExt } from "../../platformPackager" import { hashFile } from "../../util/hash" import { isMacOsCatalina } from "../../util/macosVersion" import { time } from "../../util/timer" @@ -73,8 +86,16 @@ export class NsisTarget extends Target { NsisTargetOptions.resolve(this.options) } + get shouldBuildUniversalInstaller() { + const buildSeparateInstallers = this.options.buildUniversalInstaller === false + return !buildSeparateInstallers + } + build(appOutDir: string, arch: Arch) { this.archs.set(arch, appOutDir) + if (!this.shouldBuildUniversalInstaller) { + return this.buildInstaller(new Map().set(arch, appOutDir)) + } return Promise.resolve() } @@ -117,9 +138,11 @@ export class NsisTarget extends Target { } } - protected get installerFilenamePattern(): string { - // tslint:disable:no-invalid-template-strings - return "${productName} " + (this.isPortable ? "" : "Setup ") + "${version}.${ext}" + protected installerFilenamePattern(primaryArch?: Arch | null, defaultArch?: string): string { + const setupText = this.isPortable ? "" : "Setup " + const archSuffix = !this.shouldBuildUniversalInstaller && primaryArch != null ? getArchSuffix(primaryArch, defaultArch) : "" + + return "${productName} " + setupText + "${version}" + archSuffix + ".${ext}"; } private get isPortable(): boolean { @@ -127,8 +150,11 @@ export class NsisTarget extends Target { } async finishBuild(): Promise { + if (!this.shouldBuildUniversalInstaller) { + return this.packageHelper.finishBuild() + } try { - const { pattern } = this.packager.artifactPatternConfig(this.options, this.installerFilenamePattern) + const { pattern } = this.packager.artifactPatternConfig(this.options, this.installerFilenamePattern()) const builds = new Set([this.archs]) if (pattern.includes("${arch}") && this.archs.size > 1) { ;[...this.archs].forEach(([arch, appOutDir]) => builds.add(new Map().set(arch, appOutDir))) @@ -147,14 +173,8 @@ export class NsisTarget extends Target { const packager = this.packager const appInfo = packager.appInfo const options = this.options - const installerFilename = packager.expandArtifactNamePattern( - options, - "exe", - primaryArch, - this.installerFilenamePattern, - false, - this.packager.platformSpecificBuildOptions.defaultArch - ) + const defaultArch = chooseNotNull(this.packager.platformSpecificBuildOptions.defaultArch, this.packager.config.defaultArch) ?? undefined + const installerFilename = packager.expandArtifactNamePattern(options, "exe", primaryArch, this.installerFilenamePattern(primaryArch, defaultArch), false, defaultArch) const oneClick = options.oneClick !== false const installerPath = path.join(this.outDir, installerFilename) @@ -328,7 +348,7 @@ export class NsisTarget extends Target { await this.executeMakensis(defines, commands, sharedHeader + (await this.computeFinalScript(script, true, archs))) await Promise.all([packager.sign(installerPath), defines.UNINSTALLER_OUT_FILE == null ? Promise.resolve() : unlink(defines.UNINSTALLER_OUT_FILE)]) - const safeArtifactName = computeSafeArtifactNameIfNeeded(installerFilename, () => this.generateGitHubInstallerName()) + const safeArtifactName = computeSafeArtifactNameIfNeeded(installerFilename, () => this.generateGitHubInstallerName(primaryArch, defaultArch)) let updateInfo: any if (this.isWebInstaller) { updateInfo = createNsisWebDifferentialUpdateInfo(installerPath, packageFiles) @@ -351,10 +371,11 @@ export class NsisTarget extends Target { }) } - protected generateGitHubInstallerName(): string { + protected generateGitHubInstallerName(primaryArch: Arch | null, defaultArch: string | undefined): string { const appInfo = this.packager.appInfo const classifier = appInfo.name.toLowerCase() === appInfo.name ? "setup-" : "Setup-" - return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}.exe` + const archSuffix = !this.shouldBuildUniversalInstaller && primaryArch != null ? getArchSuffix(primaryArch, defaultArch) : "" + return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}${archSuffix}.exe` } private get isUnicodeEnabled(): boolean { diff --git a/packages/app-builder-lib/src/targets/nsis/WebInstallerTarget.ts b/packages/app-builder-lib/src/targets/nsis/WebInstallerTarget.ts index f8311c3f231..cad495bdc36 100644 --- a/packages/app-builder-lib/src/targets/nsis/WebInstallerTarget.ts +++ b/packages/app-builder-lib/src/targets/nsis/WebInstallerTarget.ts @@ -1,3 +1,4 @@ +import { Arch, log } from "builder-util" import { computeDownloadUrl, getPublishConfigs, getPublishConfigsForUpdateInfo } from "../../publish/PublishManager" import { WinPackager } from "../../winPackager" import { NsisWebOptions } from "./nsisOptions" @@ -35,8 +36,14 @@ export class WebInstallerTarget extends NsisTarget { defines.APP_PACKAGE_URL = appPackageUrl } - protected get installerFilenamePattern(): string { - // tslint:disable:no-invalid-template-strings + get shouldBuildUniversalInstaller() { + if (this.options.buildUniversalInstaller === false) { + log.warn({ buildUniversalInstaller: true }, "only universal builds are supported for nsis-web installers, overriding setting") + } + return true + } + + protected installerFilenamePattern(_primaryArch?: Arch | null, _defaultArch?: string): string { return "${productName} Web Setup ${version}.${ext}" } diff --git a/packages/app-builder-lib/src/targets/nsis/nsisOptions.ts b/packages/app-builder-lib/src/targets/nsis/nsisOptions.ts index d6f24b47d3c..7bf2cf47ba8 100644 --- a/packages/app-builder-lib/src/targets/nsis/nsisOptions.ts +++ b/packages/app-builder-lib/src/targets/nsis/nsisOptions.ts @@ -198,6 +198,13 @@ export interface NsisOptions extends CommonNsisOptions, CommonWindowsInstallerCo * @default [".avi", ".mov", ".m4v", ".mp4", ".m4p", ".qt", ".mkv", ".webm", ".vmdk"] */ readonly preCompressedFileExtensions?: Array | string | null + + /** + * Disable building an universal installer of the archs specified in the target configuration + * *Not supported for nsis-web* + * @default true + */ + readonly buildUniversalInstaller?: boolean } /** diff --git a/test/snapshots/windows/webInstallerTest.js.snap b/test/snapshots/windows/webInstallerTest.js.snap index dd883921a39..9c798881b27 100644 --- a/test/snapshots/windows/webInstallerTest.js.snap +++ b/test/snapshots/windows/webInstallerTest.js.snap @@ -99,6 +99,13 @@ exports[`web installer 1`] = ` }, ], "packages": { + "arm64": { + "blockMapSize": "@blockMapSize", + "file": "TestApp-1.1.0-arm64.nsis.7z", + "path": "TestApp-1.1.0-arm64.nsis.7z", + "sha512": "@sha512", + "size": "@size", + }, "x64": { "blockMapSize": "@blockMapSize", "file": "TestApp-1.1.0-x64.nsis.7z", @@ -114,11 +121,17 @@ exports[`web installer 1`] = ` }, }, { - "arch": "x64", "file": "Test App ßW Web Setup 1.1.0.exe", "safeArtifactName": "TestApp-WebSetup-1.1.0.exe", "updateInfo": { "packages": { + "arm64": { + "blockMapSize": "@blockMapSize", + "file": "TestApp-1.1.0-arm64.nsis.7z", + "path": "TestApp-1.1.0-arm64.nsis.7z", + "sha512": "@sha512", + "size": "@size", + }, "x64": { "blockMapSize": "@blockMapSize", "file": "TestApp-1.1.0-x64.nsis.7z", @@ -129,6 +142,10 @@ exports[`web installer 1`] = ` }, }, }, + { + "arch": "arm64", + "file": "TestApp-1.1.0-arm64.nsis.7z", + }, { "arch": "x64", "file": "TestApp-1.1.0-x64.nsis.7z", diff --git a/test/snapshots/windows/winPackagerTest.js.snap b/test/snapshots/windows/winPackagerTest.js.snap index 7d91d846471..8dbb4228081 100644 --- a/test/snapshots/windows/winPackagerTest.js.snap +++ b/test/snapshots/windows/winPackagerTest.js.snap @@ -3,6 +3,23 @@ exports[`beta version 1`] = ` { "win": [ + { + "arch": "arm64", + "file": "Test App ßW Setup 3.0.0-beta.2-arm64.exe", + "safeArtifactName": "TestApp-Setup-3.0.0-beta.2-arm64.exe", + "updateInfo": { + "sha512": "@sha512", + "size": "@size", + }, + }, + { + "file": "Test App ßW Setup 3.0.0-beta.2-arm64.exe.blockmap", + "safeArtifactName": "TestApp-Setup-3.0.0-beta.2-arm64.exe.blockmap", + "updateInfo": { + "sha512": "@sha512", + "size": "@size", + }, + }, { "arch": "x64", "file": "Test App ßW Setup 3.0.0-beta.2.exe", @@ -31,6 +48,11 @@ exports[`icon not an image 1`] = `"ERR_ICON_UNKNOWN_FORMAT"`; exports[`win zip 1`] = ` { "win": [ + { + "arch": "arm64", + "file": "Test App ßW-1.1.0-arm64-win.zip", + "safeArtifactName": "TestApp-1.1.0-arm64-win.zip", + }, { "arch": "x64", "file": "Test App ßW-1.1.0-win.zip", diff --git a/test/src/windows/webInstallerTest.ts b/test/src/windows/webInstallerTest.ts index 5e11f605e62..62492797156 100644 --- a/test/src/windows/webInstallerTest.ts +++ b/test/src/windows/webInstallerTest.ts @@ -6,7 +6,7 @@ import { app } from "../helpers/packTester" test.ifNotCiMac( "web installer", app({ - targets: Platform.WINDOWS.createTarget(["nsis-web"], Arch.x64), + targets: Platform.WINDOWS.createTarget(["nsis-web"], Arch.x64, Arch.arm64), config: { publish: { provider: "s3", @@ -23,6 +23,9 @@ test.ifNotCiMac( loadBrowserProcessSpecificV8Snapshot: true, grantFileProtocolExtraPrivileges: undefined, // unsupported on current electron version in our tests }, + nsisWeb: { + buildUniversalInstaller: false, + }, }, }) ) diff --git a/test/src/windows/winPackagerTest.ts b/test/src/windows/winPackagerTest.ts index 79a47da5c1e..a776d2af48a 100644 --- a/test/src/windows/winPackagerTest.ts +++ b/test/src/windows/winPackagerTest.ts @@ -4,14 +4,17 @@ import { CheckingWinPackager } from "../helpers/CheckingPackager" import { app, appThrows, assertPack, platform } from "../helpers/packTester" import * as fs from "fs/promises" -test.ifWinCi( +test.ifNotCiMac( "beta version", app({ - targets: Platform.WINDOWS.createTarget(["nsis"], Arch.x64), + targets: Platform.WINDOWS.createTarget(["nsis"], Arch.x64, Arch.arm64), config: { extraMetadata: { version: "3.0.0-beta.2", }, + nsis: { + buildUniversalInstaller: false, + }, }, }) ) @@ -19,7 +22,7 @@ test.ifWinCi( test.ifNotCiMac( "win zip", app({ - targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64), + targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64, Arch.arm64), config: { downloadAlternateFFmpeg: true, electronFuses: {