diff --git a/.changeset/wide-wolves-help.md b/.changeset/wide-wolves-help.md new file mode 100644 index 00000000000..85cb9eac574 --- /dev/null +++ b/.changeset/wide-wolves-help.md @@ -0,0 +1,6 @@ +--- +"electron-builder": minor +"app-builder-lib": minor +--- + +feat(win): updating win-codesign tooling to latest from `electron-builder-binaries` diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e4e61d591af..65164b91b4c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -254,7 +254,7 @@ jobs: testFiles: - oneClickInstallerTest,assistedInstallerTest,webInstallerTest,packageManagerTest,HoistedNodeModuleTest,rebuilderTest - winPackagerTest,winCodeSignTest,BuildTest,blackboxUpdateTest,debTest - - masTest,filesTest,macPackagerTest,macArchiveTest + - masTest,dmgTest,filesTest,macPackagerTest,macArchiveTest - concurrentBuildsTest,macIconTest steps: - name: Checkout code repository diff --git a/packages/app-builder-lib/scheme.json b/packages/app-builder-lib/scheme.json index 2ad70797805..065d20bd567 100644 --- a/packages/app-builder-lib/scheme.json +++ b/packages/app-builder-lib/scheme.json @@ -6879,6 +6879,22 @@ "default": true, "description": "Whether to verify the signature of an available update before installation.\nThe [publisher name](#publisherName) will be used for the signature verification.", "type": "boolean" + }, + "winCodeSign": { + "anyOf": [ + { + "enum": [ + "0.0.0", + "1.0.0" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": "0.0.0", + "description": "`win-codesign` version to use for signing Windows artifacts.\nLocated at https://github.com/electron-userland/electron-builder-binaries/releases?q=win-codesign&expanded=true\n0.0.0 - legacy toolset (winCodeSign)\n1.0.0 - windows-kits-bundle-10_0_26100_0" } }, "type": "object" diff --git a/packages/app-builder-lib/src/codeSign/windowsSignToolManager.ts b/packages/app-builder-lib/src/codeSign/windowsSignToolManager.ts index 00aeeec2a94..a57a313e78c 100644 --- a/packages/app-builder-lib/src/codeSign/windowsSignToolManager.ts +++ b/packages/app-builder-lib/src/codeSign/windowsSignToolManager.ts @@ -2,25 +2,18 @@ import { asArray, InvalidConfigurationError, log, retry } from "builder-util" import { MemoLazy, parseDn } from "builder-util-runtime" import { rename } from "fs-extra" import { Lazy } from "lazy-val" -import * as os from "os" import * as path from "path" -import { getBin } from "../binDownload" import { Target } from "../core" import { WindowsConfiguration } from "../options/winOptions" import AppXTarget from "../targets/AppxTarget" import { executeAppBuilderAsJson } from "../util/appBuilder" -import { computeToolEnv, ToolInfo } from "../util/bundledTool" -import { isUseSystemSigncode } from "../util/flags" import { resolveFunction } from "../util/resolve" import { VmManager } from "../vm/vm" import { WinPackager } from "../winPackager" import { importCertificate } from "./codesign" import { SignManager } from "./signManager" import { WindowsSignOptions } from "./windowsCodeSign" - -export function getSignVendorPath() { - return getBin("winCodeSign") -} +import { getSignToolPath } from "../targets/tools" export type CustomWindowsSign = (configuration: CustomWindowsSignTaskConfiguration, packager?: WinPackager) => Promise @@ -264,40 +257,91 @@ export class WindowsSignToolManager implements SignManager { // on windows be aware of http://stackoverflow.com/a/32640183/1910191 computeSignToolArgs(options: WindowsSignTaskConfiguration, isWin: boolean, vm: VmManager = new VmManager()): Array { + return isWin ? this.computeWindowsSignArgs(options, vm) : this.computeOsslsigncodeArgs(options, vm) + } + + private computeWindowsSignArgs(options: WindowsSignTaskConfiguration, vm: VmManager): Array { const inputFile = vm.toVmFile(options.path) - const outputPath = isWin ? inputFile : this.getOutputPath(inputFile, options.hash) - if (!isWin) { - options.resultOutputPath = outputPath + const args = ["sign"] + + // Timestamping + if (process.env.ELECTRON_BUILDER_OFFLINE !== "true") { + const isRfc3161 = options.isNest || options.hash === "sha256" + args.push(isRfc3161 ? "/tr" : "/t") + + const timestampUrl = isRfc3161 + ? options.options.signtoolOptions?.rfc3161TimeStampServer || "http://timestamp.digicert.com" + : options.options.signtoolOptions?.timeStampServer || "http://timestamp.digicert.com" + args.push(timestampUrl) } - const args = isWin ? ["sign"] : ["-in", inputFile, "-out", outputPath] + // Certificate + this.addCertificateArgs(args, options, vm, true) + + // Hash algorithm + args.push("/fd", options.hash.toLowerCase()) + if (process.env.ELECTRON_BUILDER_OFFLINE !== "true" && !args.includes("/t")) { + args.push("/td", "sha256") + } + + // Optional parameters + this.addCommonSigningArgs(args, options, vm, true) + + // Windows-specific + args.push("/debug") + args.push(inputFile) // Must be last + + return args + } + + private computeOsslsigncodeArgs(options: WindowsSignTaskConfiguration, vm: VmManager): Array { + const inputFile = vm.toVmFile(options.path) + const outputPath = this.getOutputPath(inputFile, options.hash) + options.resultOutputPath = outputPath + + const args = ["sign", "-in", inputFile, "-out", outputPath] + // Timestamping if (process.env.ELECTRON_BUILDER_OFFLINE !== "true") { - const timestampingServiceUrl = options.options.signtoolOptions?.timeStampServer || "http://timestamp.digicert.com" - if (isWin) { - args.push( - options.isNest || options.hash === "sha256" ? "/tr" : "/t", - options.isNest || options.hash === "sha256" ? options.options.signtoolOptions?.rfc3161TimeStampServer || "http://timestamp.digicert.com" : timestampingServiceUrl - ) - } else { - args.push("-t", timestampingServiceUrl) - } + const timestampUrl = options.options.signtoolOptions?.timeStampServer || "http://timestamp.digicert.com" + args.push("-t", timestampUrl) } + // Certificate + this.addCertificateArgs(args, options, vm, false) + + // Hash algorithm + args.push("-h", options.hash.toLowerCase()) + + // Optional parameters + this.addCommonSigningArgs(args, options, vm, false) + + // Proxy support + const httpsProxy = process.env.HTTPS_PROXY + if (httpsProxy?.length) { + args.push("-p", httpsProxy) + } + + return args + } + + private addCertificateArgs(args: Array, options: WindowsSignTaskConfiguration, vm: VmManager, isWin: boolean): void { const certificateFile = (options.cscInfo as FileCodeSigningInfo).file + if (certificateFile == null) { - const cscInfo = options.cscInfo as CertificateFromStoreInfo - const subjectName = cscInfo.thumbprint + // Certificate from store (Windows only) if (!isWin) { - throw new Error(`${subjectName == null ? "certificateSha1" : "certificateSubjectName"} supported only on Windows`) + throw new Error("certificateSha1/certificateSubjectName supported only on Windows") } + const cscInfo = options.cscInfo as CertificateFromStoreInfo args.push("/sha1", cscInfo.thumbprint) args.push("/s", cscInfo.store) if (cscInfo.isLocalMachineStore) { args.push("/sm") } } else { + // Certificate file const certExtension = path.extname(certificateFile) if (certExtension === ".p12" || certExtension === ".pfx") { args.push(isWin ? "/f" : "-pkcs12", vm.toVmFile(certificateFile)) @@ -305,14 +349,9 @@ export class WindowsSignToolManager implements SignManager { throw new Error(`Please specify pkcs12 (.p12/.pfx) file, ${certificateFile} is not correct`) } } + } - if (!isWin || options.hash !== "sha1") { - args.push(isWin ? "/fd" : "-h", options.hash) - if (isWin && process.env.ELECTRON_BUILDER_OFFLINE !== "true") { - args.push("/td", "sha256") - } - } - + private addCommonSigningArgs(args: Array, options: WindowsSignTaskConfiguration, vm: VmManager, isWin: boolean): void { if (options.name) { args.push(isWin ? "/d" : "-n", options.name) } @@ -321,12 +360,11 @@ export class WindowsSignToolManager implements SignManager { args.push(isWin ? "/du" : "-i", options.site) } - // msi does not support dual-signing if (options.isNest) { args.push(isWin ? "/as" : "-nest") } - const password = options.cscInfo == null ? null : (options.cscInfo as FileCodeSigningInfo).password + const password = (options.cscInfo as FileCodeSigningInfo)?.password if (password) { args.push(isWin ? "/p" : "-pass", password) } @@ -335,20 +373,6 @@ export class WindowsSignToolManager implements SignManager { if (additionalCert) { args.push(isWin ? "/ac" : "-ac", vm.toVmFile(additionalCert)) } - - const httpsProxyFromEnv = process.env.HTTPS_PROXY - if (!isWin && httpsProxyFromEnv != null && httpsProxyFromEnv.length) { - args.push("-p", httpsProxyFromEnv) - } - - if (isWin) { - // https://github.com/electron-userland/electron-builder/issues/2875#issuecomment-387233610 - args.push("/debug") - // must be last argument - args.push(inputFile) - } - - return args } getOutputPath(inputPath: string, hash: string) { @@ -356,40 +380,6 @@ export class WindowsSignToolManager implements SignManager { return path.join(path.dirname(inputPath), `${path.basename(inputPath, extension)}-signed-${hash}${extension}`) } - getWinSignTool(vendorPath: string): string { - // use modern signtool on Windows Server 2012 R2 to be able to sign AppX - if (isOldWin6()) { - return path.join(vendorPath, "windows-6", "signtool.exe") - } else { - return path.join(vendorPath, "windows-10", process.arch, "signtool.exe") - } - } - - async getToolPath(isWin = process.platform === "win32"): Promise { - if (isUseSystemSigncode()) { - return { path: "osslsigncode" } - } - - const result = process.env.SIGNTOOL_PATH - if (result) { - return { path: result } - } - - const vendorPath = await getSignVendorPath() - if (isWin) { - // use modern signtool on Windows Server 2012 R2 to be able to sign AppX - return { path: this.getWinSignTool(vendorPath) } - } else if (process.platform === "darwin") { - const toolDirPath = path.join(vendorPath, process.platform, "10.12") - return { - path: path.join(toolDirPath, "osslsigncode"), - env: computeToolEnv([path.join(toolDirPath, "lib")]), - } - } else { - return { path: path.join(vendorPath, process.platform, "osslsigncode") } - } - } - async getCertificateFromStoreInfo(options: WindowsConfiguration, vm: VmManager): Promise { const certificateSubjectName = options.signtoolOptions?.certificateSubjectName const certificateSha1 = options.signtoolOptions?.certificateSha1?.toUpperCase() @@ -432,11 +422,10 @@ export class WindowsSignToolManager implements SignManager { const timeout = parseInt(process.env.SIGNTOOL_TIMEOUT as any, 10) || 10 * 60 * 1000 // decide runtime argument by cases let args: Array - let env = process.env let vm: VmManager const useVmIfNotOnWin = configuration.path.endsWith(".appx") || !("file" in configuration.cscInfo!) /* certificateSubjectName and other such options */ const isWin = process.platform === "win32" || useVmIfNotOnWin - const toolInfo = await this.getToolPath(isWin) + const toolInfo = await getSignToolPath(this.packager.config.win?.winCodeSign, isWin) const tool = toolInfo.path if (useVmIfNotOnWin) { vm = await packager.vm.value @@ -444,12 +433,9 @@ export class WindowsSignToolManager implements SignManager { } else { vm = new VmManager() args = configuration.computeSignToolArgs(isWin) - if (toolInfo.env != null) { - env = toolInfo.env - } } - await retry(() => vm.exec(tool, args, { timeout, env }), { + await retry(() => vm.exec(tool, args, { timeout, env: { ...process.env, ...(toolInfo.env || {}) } }), { retries: 2, interval: 15000, backoff: 10000, @@ -467,8 +453,3 @@ export class WindowsSignToolManager implements SignManager { }) } } - -export function isOldWin6() { - const winVersion = os.release() - return winVersion.startsWith("6.") && !winVersion.startsWith("6.3") -} diff --git a/packages/app-builder-lib/src/options/winOptions.ts b/packages/app-builder-lib/src/options/winOptions.ts index 6f4568b9b41..568acadb9cc 100644 --- a/packages/app-builder-lib/src/options/winOptions.ts +++ b/packages/app-builder-lib/src/options/winOptions.ts @@ -24,6 +24,16 @@ export interface WindowsConfiguration extends PlatformSpecificBuildOptions { */ readonly legalTrademarks?: string | null + /** + * `win-codesign` version to use for signing Windows artifacts. + * Located at https://github.com/electron-userland/electron-builder-binaries/releases?q=win-codesign&expanded=true + * 0.0.0 - legacy toolset (winCodeSign) + * 1.0.0 - windows-kits-bundle-10_0_26100_0 + * @default "0.0.0" + * + */ + readonly winCodeSign?: "0.0.0" | "1.0.0" | null + /** * Options for usage with signtool.exe * Cannot be used in conjunction with `azureSignOptions`, signing will default to Azure Trusted Signing diff --git a/packages/app-builder-lib/src/targets/AppxTarget.ts b/packages/app-builder-lib/src/targets/AppxTarget.ts index 07f71532e8c..9ed1e26d893 100644 --- a/packages/app-builder-lib/src/targets/AppxTarget.ts +++ b/packages/app-builder-lib/src/targets/AppxTarget.ts @@ -3,12 +3,13 @@ import { Nullish } from "builder-util-runtime" import { emptyDir, readdir, readFile, writeFile } from "fs-extra" import * as path from "path" import { AppXOptions } from "../" -import { getSignVendorPath, isOldWin6 } from "../codeSign/windowsSignToolManager" +import { getWindowsKitsBundle } from "./tools" import { Target } from "../core" import { getTemplatePath } from "../util/pathManager" import { VmManager } from "../vm/vm" import { WinPackager } from "../winPackager" import { createStageDir } from "./targetUtil" +import { isOldWin6 } from "../targets/tools" const APPX_ASSETS_DIR_NAME = "appx" @@ -73,7 +74,7 @@ export default class AppXTarget extends Target { arch, }) - const vendorPath = await getSignVendorPath() + const vendorPath = await getWindowsKitsBundle({ winCodeSign: this.packager.config.win?.winCodeSign, arch: process.arch }) const vm = await packager.vm.value const stageDir = await createStageDir(this, packager, arch) @@ -98,7 +99,7 @@ export default class AppXTarget extends Target { ) const userAssetDir = await this.packager.getResource(undefined, APPX_ASSETS_DIR_NAME) - const assetInfo = await AppXTarget.computeUserAssets(vm, vendorPath, userAssetDir) + const assetInfo = await AppXTarget.computeUserAssets(vm, vendorPath.appxAssets, userAssetDir) const userAssets = assetInfo.userAssets const manifestFile = stageDir.getTempFile("AppxManifest.xml") @@ -107,11 +108,10 @@ export default class AppXTarget extends Target { await packager.info.emitAppxManifestCreated(manifestFile) mappingList.push(assetInfo.mappings) mappingList.push([`"${vm.toVmFile(manifestFile)}" "AppxManifest.xml"`]) - const signToolArch = arch === Arch.arm64 ? "x64" : Arch[arch] if (isScaledAssetsProvided(userAssets)) { const outFile = vm.toVmFile(stageDir.getTempFile("resources.pri")) - const makePriPath = vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makepri.exe")) + const makePriPath = vm.toVmFile(path.join(vendorPath.kit, "makepri.exe")) const assetRoot = stageDir.getTempFile("appx/assets") await emptyDir(assetRoot) @@ -148,7 +148,7 @@ export default class AppXTarget extends Target { makeAppXArgs.push(...this.options.makeappxArgs) } this.buildQueueManager.add(async () => { - await vm.exec(vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makeappx.exe")), makeAppXArgs) + await vm.exec(vm.toVmFile(path.join(vendorPath.kit, "makeappx.exe")), makeAppXArgs) await packager.signIf(artifactPath) await stageDir.cleanup() diff --git a/packages/app-builder-lib/src/targets/tools.ts b/packages/app-builder-lib/src/targets/tools.ts index c3a4f535a3a..ea6d22a5107 100644 --- a/packages/app-builder-lib/src/targets/tools.ts +++ b/packages/app-builder-lib/src/targets/tools.ts @@ -1,20 +1,37 @@ import * as path from "path" -import { getBinFromUrl } from "../binDownload" +import { getBin, getBinFromUrl } from "../binDownload" +import { isEmptyOrSpaces, log } from "builder-util" +import { Nullish } from "builder-util-runtime" +import * as os from "os" +import { computeToolEnv, ToolInfo } from "../util/bundledTool" +import { WindowsConfiguration } from "../options/winOptions" +import { isUseSystemSigncode } from "../util/flags" + +const wincodesignChecksums = { + "rcedit-windows-2_0_0.zip": "NrBrX6M6qMG5vhUlMsD1P+byOfBq45KAD12Ono0lEfX8ynu3t0DmwJEMsRIjV/l0/SlptzM/eQXtY6+mOsvyjw==", + "win-codesign-darwin-arm64.zip": "D2w1EXL+4yTZ4vLvc2R+fox1nCl3D+o4m8CPo8BcIXNXHy5evnIgRGycb1nXNwRvyzS7trmOdVabW4W+A8CY7w==", + "win-codesign-darwin-x86_64.zip": "eF8TsYdSnPp2apYx/LoJMwwOvUAWo0ew0yqPxKfW6VflND2lmloJKxyfJzcBqhb1bvUNZAJtGuXU6KKOrUtPPQ==", + "win-codesign-linux-amd64.zip": "bHk5IbCv90BELGQxN7YUiiwVjQ10tEmIgLWn30/+9ejCGW6Hx1ammuX+katIxSm0osCrSGkHKY+E9Lo2qZCx5A==", + "win-codesign-linux-arm64.zip": "KLxwF6pvbyg37PI+IES17oOmrynaK3HR5fsFS7lUDzm7cNR8CUDirarwFP+G60Rl4cRC8hKbwNPumnPGStBXWQ==", + "win-codesign-linux-i386.zip": "sgI+axxrzKGbrKey9cIHg+FfniQqD6+u80xN6OQfcPcGmA3+z1R1Q0W/Wxy+qJkylhIgcRgeHgjzWkdDDNucyA==", + "win-codesign-windows-x64.zip": "XixPi+4XhoOdN5j90jx9rDgVAI0KHuM50D3dcWsn9NCxlZ5iTbDscvU7ARQG9h4+tWnprYZ2qbSoJiCvqlWZ4g==", + "windows-kits-bundle-10_0_26100_0.zip": "vvvH4J0JG2FoUcpRzXxrQHyONCALUZjQigff5CawjDP1DuwwwdVcZdfE33IQoRl4TqMOSu56hOy7nN72hskqyg==", +} + +// It's just easier to copy the map of checksums here rather then adding them to within each if-statement. Also, easy copy-paste from the releases page +const fpmChecksums = { + "fpm-1.17.0-ruby-3.4.3-darwin-arm64.7z": "0n3BG/Xz1T5YIsoNNTG1bBege9E8A7rym5e3mfzHSHbiSiTS44v6GIHW4amDQk1Y5dtKtWXVq7FwjdmAf3kmMg==", + "fpm-1.17.0-ruby-3.4.3-darwin-x86_64.7z": "wPX3UheBznIlAXduM22W/d27i+DZVIB/MYnY5eh/qLeEEASZqHJWgN+pIckz3jT0dP37g1SQCikXXfsgxtMSPA==", + "fpm-1.17.0-ruby-3.4.3-linux-amd64.7z": "7miGWr6dfJSzXDD9ALqkwxvGACp7s7GR50NPcU0YwzbJL825H1SLwGJSGME+v57BxDI2xac47gFEkRZf5u9EtA==", + "fpm-1.17.0-ruby-3.4.3-linux-arm64v8.7z": "moRNjg6Q2iSXpkrm5sGNL2F6KilGNPagbefxhtr3VEqvAUSg2k2pMLr5xXUo0L4rZ4V+uETbwmbDCpeO3pmLyQ==", + "fpm-1.17.0-ruby-3.4.3-linux-i386.7z": "UPzsXhkW2T7+oHSKgFsZsFUxxmPC9lNZHsQbT+OeoTbIGsb6+qf3m7c6uP0XvRFnJiu3MM3lE1xAWQOctvajWA==", +} export function getLinuxToolsPath() { return getBinFromUrl("linux-tools-mac-10.12.3", "linux-tools-mac-10.12.3.7z", "SQ8fqIRVXuQVWnVgaMTDWyf2TLAJjJYw3tRSqQJECmgF6qdM7Kogfa6KD49RbGzzMYIFca9Uw3MdsxzOPRWcYw==") } export async function getFpmPath() { - // It's just easier to copy the map of checksums here rather then adding them to within each if-statement. Also, easy copy-paste from the releases page - const fpmChecksumMap = { - "fpm-1.17.0-ruby-3.4.3-darwin-arm64.7z": "0n3BG/Xz1T5YIsoNNTG1bBege9E8A7rym5e3mfzHSHbiSiTS44v6GIHW4amDQk1Y5dtKtWXVq7FwjdmAf3kmMg==", - "fpm-1.17.0-ruby-3.4.3-darwin-x86_64.7z": "wPX3UheBznIlAXduM22W/d27i+DZVIB/MYnY5eh/qLeEEASZqHJWgN+pIckz3jT0dP37g1SQCikXXfsgxtMSPA==", - "fpm-1.17.0-ruby-3.4.3-linux-amd64.7z": "7miGWr6dfJSzXDD9ALqkwxvGACp7s7GR50NPcU0YwzbJL825H1SLwGJSGME+v57BxDI2xac47gFEkRZf5u9EtA==", - "fpm-1.17.0-ruby-3.4.3-linux-arm64v8.7z": "moRNjg6Q2iSXpkrm5sGNL2F6KilGNPagbefxhtr3VEqvAUSg2k2pMLr5xXUo0L4rZ4V+uETbwmbDCpeO3pmLyQ==", - "fpm-1.17.0-ruby-3.4.3-linux-i386.7z": "UPzsXhkW2T7+oHSKgFsZsFUxxmPC9lNZHsQbT+OeoTbIGsb6+qf3m7c6uP0XvRFnJiu3MM3lE1xAWQOctvajWA==", - } - if (process.env.CUSTOM_FPM_PATH != null) { return path.resolve(process.env.CUSTOM_FPM_PATH) } @@ -39,6 +56,113 @@ export async function getFpmPath() { } const filename = getKey() - const fpmPath = await getBinFromUrl("fpm@2.1.4", filename, fpmChecksumMap[filename]) + const fpmPath = await getBinFromUrl("fpm@2.1.4", filename, fpmChecksums[filename]) return path.join(fpmPath, exec) } + +export async function getSignToolPath(winCodeSign: WindowsConfiguration["winCodeSign"] | Nullish, isWin = process.platform === "win32"): Promise { + if (isUseSystemSigncode()) { + return { path: "osslsigncode" } + } + + const result = process.env.SIGNTOOL_PATH?.trim() + if (result) { + return { path: path.resolve(result) } + } + + if (isWin) { + return { path: await getWindowsSignToolExe({ winCodeSign }) } + } else { + const vendor = await getOsslSigncodeBundle({ winCodeSign }) + return { path: vendor.path, env: vendor.env } + } +} + +export async function getWindowsKitsBundle({ winCodeSign, arch }: { winCodeSign: WindowsConfiguration["winCodeSign"] | Nullish; arch: NodeJS.Architecture }) { + const overridePath = process.env.ELECTRON_BUILDER_WINDOWS_KITS_PATH + if (!isEmptyOrSpaces(overridePath)) { + return { kit: overridePath, appxAssets: overridePath } + } + + const windowsKitArch = (x86: string) => (arch === "ia32" ? x86 : arch === "arm64" ? "arm64" : "x64") + + const useLegacy = winCodeSign == null || winCodeSign === "0.0.0" + if (useLegacy === true) { + const vendorPath = await getBin("winCodeSign") + return { kit: path.resolve(vendorPath, "windows-10", windowsKitArch("ia32")), appxAssets: vendorPath } + } + const file = "windows-kits-bundle-10_0_26100_0.zip" + const vendorPath = await getBinFromUrl("win-codesign@1.0.0", file, wincodesignChecksums[file]) + return { kit: path.resolve(vendorPath, windowsKitArch("x86")), appxAssets: vendorPath } +} + +export function isOldWin6() { + const winVersion = os.release() + return winVersion.startsWith("6.") && !winVersion.startsWith("6.3") +} + +async function getWindowsSignToolExe({ winCodeSign }: { winCodeSign: WindowsConfiguration["winCodeSign"] | Nullish }) { + if (winCodeSign === "0.0.0" || winCodeSign == null) { + // use modern signtool on Windows Server 2012 R2 to be able to sign AppX + const vendorPath = await getBin("winCodeSign") + if (isOldWin6()) { + return path.resolve(vendorPath, "windows-6", "signtool.exe") + } else { + return path.resolve(vendorPath, "windows-10", process.arch, "signtool.exe") + } + } + const vendorPath = await getWindowsKitsBundle({ winCodeSign, arch: process.arch }) + return path.resolve(vendorPath.kit, "signtool.exe") +} + +async function getOsslSigncodeBundle({ winCodeSign }: { winCodeSign: WindowsConfiguration["winCodeSign"] | Nullish }) { + const overridePath = process.env.ELECTRON_BUILDER_OSSL_SIGNCODE_PATH + if (!isEmptyOrSpaces(overridePath)) { + return { path: overridePath } + } + if (process.platform === "win32" || process.env.USE_SYSTEM_OSSLSIGNCODE === "true") { + return { path: "osslsigncode" } + } + + if (winCodeSign === "0.0.0" || winCodeSign == null) { + const vendorBase = path.resolve(await getBin("winCodeSign"), process.platform) + const vendorPath = process.platform === "darwin" ? path.resolve(vendorBase, "10.12") : vendorBase + return { path: path.resolve(vendorPath, "osslsigncode"), env: process.platform === "darwin" ? computeToolEnv([path.resolve(vendorPath, "lib")]) : undefined } + } + + const filename = (() => { + if (process.platform === "linux") { + if (process.arch == "x64") { + return "win-codesign-linux-amd64.zip" + } else if (process.arch === "arm64") { + return "win-codesign-linux-arm64.zip" + } + return "win-codesign-linux-i386.zip" + } + // darwin arm64 + if (process.arch === "arm64") { + return "win-codesign-darwin-arm64.zip" + } + return "win-codesign-darwin-x86_64.zip" + })() + const toolPath = await getBinFromUrl("win-codesign@1.0.0", filename, wincodesignChecksums[filename]) + return { path: path.resolve(toolPath, "osslsigncode") } +} + +export async function getRceditBundle({ winCodeSign }: { winCodeSign: WindowsConfiguration["winCodeSign"] | Nullish }) { + const ia32 = "rcedit-ia32.exe" + const x86 = "rcedit-x86.exe" + const x64 = "rcedit-x64.exe" + const overridePath = process.env.ELECTRON_BUILDER_RCEDIT_PATH?.trim() + if (!isEmptyOrSpaces(overridePath)) { + log.debug({ searchFiles: [x86, x64], overridePath }, `Using RCEdit from ELECTRON_BUILDER_RCEDIT_PATH`) + return { x86: path.join(overridePath, x86), x64: path.join(overridePath, x64) } + } + if (winCodeSign === "0.0.0" || winCodeSign == null) { + const vendorPath = await getBin("winCodeSign") + return { x86: path.join(vendorPath, ia32), x64: path.join(vendorPath, x64) } + } + const file = "rcedit-windows-2_0_0.zip" + const vendorPath = await getBinFromUrl("win-codesign@1.0.0", file, wincodesignChecksums[file]) + return { x86: path.join(vendorPath, x86), x64: path.join(vendorPath, x64) } +} diff --git a/packages/app-builder-lib/src/winPackager.ts b/packages/app-builder-lib/src/winPackager.ts index 6a71bb78a35..70c30275707 100644 --- a/packages/app-builder-lib/src/winPackager.ts +++ b/packages/app-builder-lib/src/winPackager.ts @@ -8,7 +8,7 @@ import * as path from "path" import { SignManager } from "./codeSign/signManager" import { signWindows, WindowsSignOptions } from "./codeSign/windowsCodeSign" import { WindowsSignAzureManager } from "./codeSign/windowsSignAzureManager" -import { FileCodeSigningInfo, getSignVendorPath, WindowsSignToolManager } from "./codeSign/windowsSignToolManager" +import { FileCodeSigningInfo, WindowsSignToolManager } from "./codeSign/windowsSignToolManager" import { AfterPackContext } from "./configuration" import { DIR_TARGET, Platform, Target } from "./core" import { RequestedExecutionLevel, WindowsConfiguration } from "./options/winOptions" @@ -26,6 +26,7 @@ import { isBuildCacheEnabled } from "./util/flags" import { time } from "./util/timer" import { getWindowsVm, VmManager } from "./vm/vm" import { execWine } from "./wine" +import { getRceditBundle } from "./targets/tools" export class WinPackager extends PlatformPackager { _iconPath = new Lazy(() => this.getOrConvertIcon("ico")) @@ -215,8 +216,8 @@ export class WinPackager extends PlatformPackager { if (process.platform === "win32" || process.platform === "darwin") { await executeAppBuilder(["rcedit", "--args", JSON.stringify(args)], undefined /* child-process */, {}, 3 /* retry three times */) } else if (this.info.framework.name === "electron") { - const vendorPath = await getSignVendorPath() - await execWine(path.join(vendorPath, "rcedit-ia32.exe"), path.join(vendorPath, "rcedit-x64.exe"), args) + const vendor = await getRceditBundle({ winCodeSign: this.platformSpecificBuildOptions.winCodeSign }) + await execWine(vendor.x86, vendor.x64, args) } await this.signIf(file) diff --git a/packages/electron-builder/src/cli/create-self-signed-cert.ts b/packages/electron-builder/src/cli/create-self-signed-cert.ts index 1ce7ec3eac5..94cc02def63 100644 --- a/packages/electron-builder/src/cli/create-self-signed-cert.ts +++ b/packages/electron-builder/src/cli/create-self-signed-cert.ts @@ -1,4 +1,4 @@ -import { getSignVendorPath } from "app-builder-lib/out/codeSign/windowsSignToolManager" +import { getWindowsKitsBundle } from "app-builder-lib/out/targets/tools" import { exec, log, spawn, TmpDir, unlinkIfExists } from "builder-util" import { sanitizeFileName } from "builder-util/out/filename" import * as chalk from "chalk" @@ -17,7 +17,7 @@ export async function createSelfSignedCert(publisher: string) { try { await mkdir(path.dirname(tempPrefix), { recursive: true }) - const vendorPath = path.join(await getSignVendorPath(), "windows-10", process.arch) + const vendorPath = path.join((await getWindowsKitsBundle({ winCodeSign: null, arch: process.arch })).kit) await exec(path.join(vendorPath, "makecert.exe"), ["-r", "-h", "0", "-n", `CN=${quoteString(publisher)}`, "-eku", "1.3.6.1.5.5.7.3.3", "-pe", "-sv", pvk, cer]) const pfx = path.join(targetDir, `${sanitizeFileName(publisher)}.pfx`) diff --git a/test/snapshots/windows/winPackagerTest.js.snap b/test/snapshots/windows/winPackagerTest.js.snap index 118900455dd..1d6e4f8a7e5 100644 --- a/test/snapshots/windows/winPackagerTest.js.snap +++ b/test/snapshots/windows/winPackagerTest.js.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`beta version 1`] = ` +exports[`winCodeSign: 0.0.0 > beta version 1`] = ` { "win": [ { @@ -41,11 +41,203 @@ exports[`beta version 1`] = ` } `; -exports[`icon < 256 1`] = `"ERR_ICON_TOO_SMALL"`; +exports[`winCodeSign: 0.0.0 > icon < 256 1`] = `"ERR_ICON_TOO_SMALL"`; -exports[`icon not an image 1`] = `"ERR_ICON_UNKNOWN_FORMAT"`; +exports[`winCodeSign: 0.0.0 > icon not an image 1`] = `"ERR_ICON_UNKNOWN_FORMAT"`; -exports[`win zip 1`] = ` +exports[`winCodeSign: 0.0.0 > legacy win-codesign 1`] = ` +{ + "win": [ + { + "arch": "x64", + "file": "Test App ßW-1.1.0-win.zip", + "safeArtifactName": "TestApp-1.1.0-win.zip", + }, + ], +} +`; + +exports[`winCodeSign: 0.0.0 > win zip 1`] = ` +{ + "win": [ + { + "arch": "x64", + "file": "Test App ßW-1.1.0-win.zip", + "safeArtifactName": "TestApp-1.1.0-win.zip", + }, + { + "arch": "arm64", + "file": "Test App ßW-1.1.0-arm64-win.zip", + "safeArtifactName": "TestApp-1.1.0-arm64-win.zip", + }, + ], +} +`; + +exports[`winCodeSign: 0.0.0 > win zip 2`] = ` +[ + "chrome_100_percent.pak", + "chrome_200_percent.pak", + "d3dcompiler_47.dll", + "ffmpeg.dll", + "icudtl.dat", + "libEGL.dll", + "libGLESv2.dll", + "LICENSE.electron.txt", + "LICENSES.chromium.html", + "locales/", + "resources/", + "resources/app.asar", + "resources/extraAsar.asar", + "resources/subdir/", + "resources/subdir/extraAsar2.asar", + "resources.pak", + "snapshot_blob.bin", + "Test App ßW.exe", + "v8_context_snapshot.bin", + "vk_swiftshader.dll", + "vk_swiftshader_icd.json", + "vulkan-1.dll", +] +`; + +exports[`winCodeSign: 0.0.0 > win zip 3`] = ` +[ + { + "alg": "SHA256", + "file": "resources\\app.asar", + "value": "hash", + }, + { + "alg": "SHA256", + "file": "resources\\extraAsar.asar", + "value": "hash", + }, + { + "alg": "SHA256", + "file": "resources\\subdir\\extraAsar2.asar", + "value": "hash", + }, +] +`; + +exports[`winCodeSign: 0.0.0 > win zip 4`] = ` +[ + "chrome_100_percent.pak", + "chrome_200_percent.pak", + "d3dcompiler_47.dll", + "ffmpeg.dll", + "icudtl.dat", + "libEGL.dll", + "libGLESv2.dll", + "LICENSE.electron.txt", + "LICENSES.chromium.html", + "locales/", + "resources/", + "resources/app.asar", + "resources/extraAsar.asar", + "resources/subdir/", + "resources/subdir/extraAsar2.asar", + "resources.pak", + "snapshot_blob.bin", + "Test App ßW.exe", + "v8_context_snapshot.bin", + "vk_swiftshader.dll", + "vk_swiftshader_icd.json", + "vulkan-1.dll", +] +`; + +exports[`winCodeSign: 0.0.0 > win zip 5`] = ` +[ + { + "alg": "SHA256", + "file": "resources\\app.asar", + "value": "hash", + }, + { + "alg": "SHA256", + "file": "resources\\extraAsar.asar", + "value": "hash", + }, + { + "alg": "SHA256", + "file": "resources\\subdir\\extraAsar2.asar", + "value": "hash", + }, +] +`; + +exports[`winCodeSign: 0.0.0 > zip artifactName 1`] = ` +{ + "win": [ + { + "arch": "x64", + "file": "Test App ßW-1.1.0-win-x64.zip", + "safeArtifactName": "TestApp-1.1.0-win.zip", + }, + ], +} +`; + +exports[`winCodeSign: 1.0.0 > beta version 1`] = ` +{ + "win": [ + { + "arch": "x64", + "file": "Test App ßW Setup 3.0.0-beta.2.exe", + "safeArtifactName": "TestApp-Setup-3.0.0-beta.2.exe", + "updateInfo": { + "sha512": "@sha512", + "size": "@size", + }, + }, + { + "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", + }, + }, + { + "file": "Test App ßW Setup 3.0.0-beta.2.exe.blockmap", + "safeArtifactName": "TestApp-Setup-3.0.0-beta.2.exe.blockmap", + "updateInfo": { + "sha512": "@sha512", + "size": "@size", + }, + }, + ], +} +`; + +exports[`winCodeSign: 1.0.0 > icon < 256 1`] = `"ERR_ICON_TOO_SMALL"`; + +exports[`winCodeSign: 1.0.0 > icon not an image 1`] = `"ERR_ICON_UNKNOWN_FORMAT"`; + +exports[`winCodeSign: 1.0.0 > legacy win-codesign 1`] = ` +{ + "win": [ + { + "arch": "x64", + "file": "Test App ßW-1.1.0-win.zip", + "safeArtifactName": "TestApp-1.1.0-win.zip", + }, + ], +} +`; + +exports[`winCodeSign: 1.0.0 > win zip 1`] = ` { "win": [ { @@ -62,7 +254,7 @@ exports[`win zip 1`] = ` } `; -exports[`win zip 2`] = ` +exports[`winCodeSign: 1.0.0 > win zip 2`] = ` [ "chrome_100_percent.pak", "chrome_200_percent.pak", @@ -89,7 +281,7 @@ exports[`win zip 2`] = ` ] `; -exports[`win zip 3`] = ` +exports[`winCodeSign: 1.0.0 > win zip 3`] = ` [ { "alg": "SHA256", @@ -109,7 +301,7 @@ exports[`win zip 3`] = ` ] `; -exports[`win zip 4`] = ` +exports[`winCodeSign: 1.0.0 > win zip 4`] = ` [ "chrome_100_percent.pak", "chrome_200_percent.pak", @@ -136,7 +328,7 @@ exports[`win zip 4`] = ` ] `; -exports[`win zip 5`] = ` +exports[`winCodeSign: 1.0.0 > win zip 5`] = ` [ { "alg": "SHA256", @@ -156,7 +348,7 @@ exports[`win zip 5`] = ` ] `; -exports[`zip artifactName 1`] = ` +exports[`winCodeSign: 1.0.0 > zip artifactName 1`] = ` { "win": [ { diff --git a/test/src/BuildTest.ts b/test/src/BuildTest.ts index 3bf8ad93554..1527d1de688 100644 --- a/test/src/BuildTest.ts +++ b/test/src/BuildTest.ts @@ -8,7 +8,7 @@ import * as path from "path" import { app, appTwo, appTwoThrows, assertPack, getFixtureDir, linuxDirTarget, modifyPackageJson, packageJson, toSystemIndependentPath } from "./helpers/packTester" import { ELECTRON_VERSION } from "./helpers/testConfig" import { verifySmartUnpack } from "./helpers/verifySmartUnpack" -import { PM } from "app-builder-lib/src/node-module-collector/packageManager" +import { PM } from "app-builder-lib/out/node-module-collector/packageManager" test.ifLinux("cli", ({ expect }) => { // because these methods are internal diff --git a/test/src/packageManagerTest.ts b/test/src/packageManagerTest.ts index 73e0703b2d3..ee5b12912f4 100644 --- a/test/src/packageManagerTest.ts +++ b/test/src/packageManagerTest.ts @@ -1,5 +1,5 @@ import { Platform } from "app-builder-lib" -import { PM } from "app-builder-lib/src/node-module-collector" +import { PM } from "app-builder-lib/out/node-module-collector" import { execSync } from "child_process" import { copyFile, outputFile, rm, writeFile } from "fs-extra" import * as path from "path" diff --git a/test/src/updater/blackboxUpdateTest.ts b/test/src/updater/blackboxUpdateTest.ts index f5873068e0a..167cb5d61e3 100644 --- a/test/src/updater/blackboxUpdateTest.ts +++ b/test/src/updater/blackboxUpdateTest.ts @@ -192,6 +192,9 @@ async function doBuild( oneClick: true, runAfterFinish: false, }, + win: { + winCodeSign: "1.0.0", + }, }, }, { diff --git a/test/src/windows/assistedInstallerTest.ts b/test/src/windows/assistedInstallerTest.ts index 5db6fabc9fb..39a3f4d3192 100644 --- a/test/src/windows/assistedInstallerTest.ts +++ b/test/src/windows/assistedInstallerTest.ts @@ -1,12 +1,12 @@ -import { Arch, archFromString, Platform } from "electron-builder" +import { Arch, Platform } from "electron-builder" import * as fs from "fs/promises" import * as path from "path" import { app, assertPack, copyTestAsset } from "../helpers/packTester" import { checkHelpers, doTest, expectUpdateMetadata } from "../helpers/winHelper" -const nsisTarget = Platform.WINDOWS.createTarget(["nsis"]) +const nsisTarget = Platform.WINDOWS.createTarget(["nsis"], Arch.x64) -test.ifNotCiMac("assisted", ({ expect }) => +test("assisted", ({ expect }) => app( expect, { @@ -35,10 +35,9 @@ test.ifNotCiMac("assisted", ({ expect }) => signedWin: true, projectDirCreated: projectDir => copyTestAsset("license.txt", path.join(projectDir, "build", "license.txt")), } - ) -) + )) -test.ifNotCiMac("allowElevation false, app requestedExecutionLevel admin", ({ expect }) => +test("allowElevation false, app requestedExecutionLevel admin", ({ expect }) => app(expect, { targets: nsisTarget, config: { @@ -60,10 +59,9 @@ test.ifNotCiMac("allowElevation false, app requestedExecutionLevel admin", ({ ex differentialPackage: false, }, }, - }) -) + })) -test.ifNotCiMac("assisted, MUI_HEADER", ({ expect }) => { +test("assisted, MUI_HEADER", ({ expect }) => { let installerHeaderPath: string | null = null return assertPack( expect, @@ -95,7 +93,7 @@ test.ifNotCiMac("assisted, MUI_HEADER", ({ expect }) => { ) }) -test.ifNotCiMac("assisted, MUI_HEADER as option", ({ expect }) => { +test("assisted, MUI_HEADER as option", ({ expect }) => { let installerHeaderPath: string | null = null return assertPack( expect, @@ -128,7 +126,7 @@ test.ifNotCiMac("assisted, MUI_HEADER as option", ({ expect }) => { ) }) -test.ifNotCiMac.skip("debug logging enabled", ({ expect }) => +test.skip("debug logging enabled", ({ expect }) => app(expect, { targets: nsisTarget, config: { @@ -141,10 +139,9 @@ test.ifNotCiMac.skip("debug logging enabled", ({ expect }) => }, }, }, - }) -) + })) -test.ifNotCiMac("assisted, only perMachine", ({ expect }) => +test("assisted, only perMachine", ({ expect }) => app(expect, { targets: nsisTarget, config: { @@ -153,10 +150,9 @@ test.ifNotCiMac("assisted, only perMachine", ({ expect }) => perMachine: true, }, }, - }) -) + })) -test.ifNotCiMac("assisted, only perMachine and elevated", ({ expect }) => +test("assisted, only perMachine and elevated", ({ expect }) => app(expect, { targets: nsisTarget, config: { @@ -166,11 +162,10 @@ test.ifNotCiMac("assisted, only perMachine and elevated", ({ expect }) => packElevateHelper: true, }, }, - }) -) + })) // test release notes also -test.ifNotCiMac("allowToChangeInstallationDirectory", ({ expect }) => +test("allowToChangeInstallationDirectory", ({ expect }) => app( expect, { @@ -194,10 +189,9 @@ test.ifNotCiMac("allowToChangeInstallationDirectory", ({ expect }) => await copyTestAsset("license.txt", path.join(projectDir, "build", "license.txt")) }, packed: async context => { - await expectUpdateMetadata(expect, context, archFromString(process.arch)) + await expectUpdateMetadata(expect, context, Arch.x64) await checkHelpers(expect, context.getResources(Platform.WINDOWS), true) await doTest(expect, context.outDir, false) }, } - ) -) + )) diff --git a/test/src/windows/winCodeSignTest.ts b/test/src/windows/winCodeSignTest.ts index ef3e33aa8a7..7f5b8704b77 100644 --- a/test/src/windows/winCodeSignTest.ts +++ b/test/src/windows/winCodeSignTest.ts @@ -23,6 +23,9 @@ test("sign nested asar unpacked executables", ({ expect }) => config: { publish: "never", asarUnpack: ["assets"], + win: { + winCodeSign: "1.0.0", + }, }, }, { @@ -35,7 +38,7 @@ test("sign nested asar unpacked executables", ({ expect }) => if (process.platform === "win32") { expect(error.message).toContain("This file format cannot be signed because it is not recognized.") } else { - expect(error.message).toContain("Unrecognized file type") + expect(error.message).toContain("Initialization error or unsupported input file type.") } } )) diff --git a/test/src/windows/winPackagerTest.ts b/test/src/windows/winPackagerTest.ts index 6c545c516ce..2a78f4ee3dc 100644 --- a/test/src/windows/winPackagerTest.ts +++ b/test/src/windows/winPackagerTest.ts @@ -1,138 +1,170 @@ -import { Arch, DIR_TARGET, Platform } from "electron-builder" +import { Arch, DIR_TARGET, Platform, WindowsConfiguration } from "electron-builder" import * as fs from "fs/promises" import * as path from "path" import { CheckingWinPackager } from "../helpers/CheckingPackager" import { app, appThrows, assertPack, platform } from "../helpers/packTester" -test( - "beta version", - ({ expect }) => - app( - expect, - { - targets: Platform.WINDOWS.createTarget(["nsis"], Arch.x64, Arch.arm64), - config: { - extraMetadata: { - version: "3.0.0-beta.2", +const winCodeSignVersions: WindowsConfiguration["winCodeSign"][] = ["0.0.0", "1.0.0"] + +for (const winCodeSign of winCodeSignVersions) { + describe(`winCodeSign: ${winCodeSign}`, () => { + test("beta version", { retry: 3 }, ({ expect }) => + app( + expect, + { + targets: Platform.WINDOWS.createTarget(["nsis"], Arch.x64, Arch.arm64), + config: { + extraMetadata: { + version: "3.0.0-beta.2", + }, + nsis: { + buildUniversalInstaller: false, + }, + win: { + winCodeSign, + }, }, - nsis: { - buildUniversalInstaller: false, + }, + { + signedWin: true, + } + ) + ) + + test("win zip", ({ expect }) => + app( + expect, + { + targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64, Arch.arm64), + config: { + extraResources: [ + { from: "build", to: "./", filter: "*.asar" }, + { from: "build/subdir", to: "./subdir", filter: "*.asar" }, + ], + win: { + winCodeSign, + }, + electronLanguages: "en", + downloadAlternateFFmpeg: true, + electronFuses: { + runAsNode: true, + enableCookieEncryption: true, + enableNodeOptionsEnvironmentVariable: true, + enableNodeCliInspectArguments: true, + enableEmbeddedAsarIntegrityValidation: true, + onlyLoadAppFromAsar: true, + loadBrowserProcessSpecificV8Snapshot: true, + grantFileProtocolExtraPrivileges: undefined, // unsupported on current electron version in our tests + }, }, }, - }, - { - signedWin: true, - } - ), - { retry: 3 } -) + { + signed: false, + projectDirCreated: async projectDir => { + await fs.mkdir(path.join(projectDir, "build", "subdir")) + await fs.copyFile(path.join(projectDir, "build", "extraAsar.asar"), path.join(projectDir, "build", "subdir", "extraAsar2.asar")) + }, + } + )) -test("win zip", ({ expect }) => - app( - expect, - { - targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64, Arch.arm64), - config: { - extraResources: [ - { from: "build", to: "./", filter: "*.asar" }, - { from: "build/subdir", to: "./subdir", filter: "*.asar" }, - ], - electronLanguages: "en", - downloadAlternateFFmpeg: true, - electronFuses: { - runAsNode: true, - enableCookieEncryption: true, - enableNodeOptionsEnvironmentVariable: true, - enableNodeCliInspectArguments: true, - enableEmbeddedAsarIntegrityValidation: true, - onlyLoadAppFromAsar: true, - loadBrowserProcessSpecificV8Snapshot: true, - grantFileProtocolExtraPrivileges: undefined, // unsupported on current electron version in our tests + test("zip artifactName", ({ expect }) => + app( + expect, + { + targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64), + config: { + //tslint:disable-next-line:no-invalid-template-strings + artifactName: "${productName}-${version}-${os}-${arch}.${ext}", + win: { + winCodeSign, + }, + }, }, - }, - }, - { - signed: false, - projectDirCreated: async projectDir => { - await fs.mkdir(path.join(projectDir, "build", "subdir")) - await fs.copyFile(path.join(projectDir, "build", "extraAsar.asar"), path.join(projectDir, "build", "subdir", "extraAsar2.asar")) - }, - } - )) + { + signedWin: true, + } + )) -test("zip artifactName", ({ expect }) => - app( - expect, - { - targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64), - config: { - //tslint:disable-next-line:no-invalid-template-strings - artifactName: "${productName}-${version}-${os}-${arch}.${ext}", - }, - }, - { - signedWin: true, - } - )) + test("legacy win-codesign", ({ expect }) => + app( + expect, + { + targets: Platform.WINDOWS.createTarget(["zip"], Arch.x64), + config: { + win: { + winCodeSign, + }, + }, + }, + { + signedWin: true, + } + )) -test("icon < 256", ({ expect }) => - appThrows(expect, platform(Platform.WINDOWS), { - projectDirCreated: projectDir => fs.rename(path.join(projectDir, "build", "incorrect.ico"), path.join(projectDir, "build", "icon.ico")), - })) + test("icon < 256", ({ expect }) => + appThrows(expect, platform(Platform.WINDOWS), { + projectDirCreated: projectDir => fs.rename(path.join(projectDir, "build", "incorrect.ico"), path.join(projectDir, "build", "icon.ico")), + })) -test("icon not an image", ({ expect }) => - appThrows(expect, platform(Platform.WINDOWS), { - projectDirCreated: async projectDir => { - const file = path.join(projectDir, "build", "icon.ico") - // because we use hardlinks - await fs.unlink(file) - await fs.writeFile(file, "foo") - }, - })) + test("icon not an image", ({ expect }) => + appThrows(expect, platform(Platform.WINDOWS), { + projectDirCreated: async projectDir => { + const file = path.join(projectDir, "build", "icon.ico") + // because we use hardlinks + await fs.unlink(file) + await fs.writeFile(file, "foo") + }, + })) -test.ifMac("custom icon", ({ expect }) => { - let platformPackager: CheckingWinPackager | null = null - return assertPack( - expect, - "test-app-one", - { - targets: Platform.WINDOWS.createTarget("squirrel", Arch.x64), - platformPackagerFactory: packager => (platformPackager = new CheckingWinPackager(packager)), - config: { - win: { - icon: "customIcon", + test.ifMac("custom icon", ({ expect }) => { + let platformPackager: CheckingWinPackager | null = null + return assertPack( + expect, + "test-app-one", + { + targets: Platform.WINDOWS.createTarget("squirrel", Arch.x64), + platformPackagerFactory: packager => (platformPackager = new CheckingWinPackager(packager)), + config: { + win: { + icon: "customIcon", + winCodeSign, + }, + }, }, - }, - }, - { - projectDirCreated: projectDir => fs.rename(path.join(projectDir, "build", "icon.ico"), path.join(projectDir, "customIcon.ico")), - packed: async context => { - expect(await platformPackager!.getIconPath()).toEqual(path.join(context.projectDir, "customIcon.ico")) - }, - } - ) -}) + { + projectDirCreated: projectDir => fs.rename(path.join(projectDir, "build", "icon.ico"), path.join(projectDir, "customIcon.ico")), + packed: async context => { + expect(await platformPackager!.getIconPath()).toEqual(path.join(context.projectDir, "customIcon.ico")) + }, + } + ) + }) -test("win icon from icns", ({ expect }) => { - let platformPackager: CheckingWinPackager | null = null - return app( - expect, - { - targets: Platform.WINDOWS.createTarget(DIR_TARGET, Arch.x64), - config: { - mac: { - icon: "icons/icon.icns", + test("win icon from icns", ({ expect }) => { + let platformPackager: CheckingWinPackager | null = null + return app( + expect, + { + targets: Platform.WINDOWS.createTarget(DIR_TARGET, Arch.x64), + config: { + mac: { + icon: "icons/icon.icns", + }, + win: { + winCodeSign, + }, + }, + platformPackagerFactory: packager => (platformPackager = new CheckingWinPackager(packager)), }, - }, - platformPackagerFactory: packager => (platformPackager = new CheckingWinPackager(packager)), - }, - { - projectDirCreated: projectDir => - Promise.all([fs.unlink(path.join(projectDir, "build", "icon.ico")), fs.rm(path.join(projectDir, "build", "icons"), { recursive: true, force: true })]), - packed: async () => { - const file = await platformPackager!.getIconPath() - expect(file).toBeDefined() - }, - } - ) -}) + { + projectDirCreated: projectDir => + Promise.all([fs.unlink(path.join(projectDir, "build", "icon.ico")), fs.rm(path.join(projectDir, "build", "icons"), { recursive: true, force: true })]), + packed: async () => { + const file = await platformPackager!.getIconPath() + expect(file).toBeDefined() + }, + } + ) + }) + }) +}