diff --git a/.changeset/sour-points-refuse.md b/.changeset/sour-points-refuse.md new file mode 100644 index 00000000000..be2775ca0b1 --- /dev/null +++ b/.changeset/sour-points-refuse.md @@ -0,0 +1,5 @@ +--- +"app-builder-lib": patch +--- + +fix: only sign concurrently when using local signtool. azure can't be in parallel due to resources being locked during usage diff --git a/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts b/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts index 392e8812fa5..037f79cd273 100644 --- a/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts +++ b/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts @@ -1,4 +1,4 @@ -import { log } from "builder-util" +import { log, retry } from "builder-util" import { WindowsConfiguration } from "../options/winOptions" import { VmManager } from "../vm/vm" import { WinPackager } from "../winPackager" @@ -11,7 +11,8 @@ export interface WindowsSignOptions { export async function signWindows(options: WindowsSignOptions, packager: WinPackager): Promise { if (options.options.azureSignOptions) { log.info({ path: log.filePath(options.path) }, "signing with Azure Trusted Signing (beta)") - return (await packager.azureSignManager.value).signUsingAzureTrustedSigning(options) + const packageManager = await packager.azureSignManager.value + return signWithRetry(async () => packageManager.signUsingAzureTrustedSigning(options)) } log.info({ path: log.filePath(options.path) }, "signing with signtool.exe") @@ -34,7 +35,24 @@ export async function signWindows(options: WindowsSignOptions, packager: WinPack if (fields.length) { log.warn({ fields, reason: "please move to win.signtoolOptions." }, `deprecated field`) } - return (await packager.signtoolManager.value).signUsingSigntool(options) + const packageManager = await packager.signtoolManager.value + return signWithRetry(async () => packageManager.signUsingSigntool(options)) +} + +function signWithRetry(signer: () => Promise): Promise { + return retry(signer, 3, 1000, 1000, 0, (e: any) => { + const message = e.message + if ( + // https://github.com/electron-userland/electron-builder/issues/1414 + message?.includes("Couldn't resolve host name") || + // https://github.com/electron-userland/electron-builder/issues/8615 + message?.includes("being used by another process.") + ) { + log.warn({ error: message }, "attempt to sign failed, another attempt will be made") + return true + } + return false + }) } export async function getPSCmd(vm: VmManager): Promise { diff --git a/packages/app-builder-lib/src/winPackager.ts b/packages/app-builder-lib/src/winPackager.ts index 5331705b392..a09a365610a 100644 --- a/packages/app-builder-lib/src/winPackager.ts +++ b/packages/app-builder-lib/src/winPackager.ts @@ -1,10 +1,11 @@ -import BluebirdPromise from "bluebird-lst" -import { Arch, InvalidConfigurationError, log, use, executeAppBuilder, CopyFileTransformer, FileTransformer, walk, retry } from "builder-util" +import { Arch, CopyFileTransformer, executeAppBuilder, FileTransformer, InvalidConfigurationError, use, walk } from "builder-util" import { createHash } from "crypto" import { readdir } from "fs/promises" import * as isCI from "is-ci" import { Lazy } from "lazy-val" import * as path from "path" +import { signWindows, WindowsSignOptions } from "./codeSign/windowsCodeSign" +import { WindowsSignAzureManager } from "./codeSign/windowsSignAzureManager" import { FileCodeSigningInfo, getSignVendorPath, WindowsSignToolManager } from "./codeSign/windowsSignToolManager" import { AfterPackContext } from "./configuration" import { DIR_TARGET, Platform, Target } from "./core" @@ -23,9 +24,6 @@ import { isBuildCacheEnabled } from "./util/flags" import { time } from "./util/timer" import { getWindowsVm, VmManager } from "./vm/vm" import { execWine } from "./wine" -import { signWindows } from "./codeSign/windowsCodeSign" -import { WindowsSignOptions } from "./codeSign/windowsCodeSign" -import { WindowsSignAzureManager } from "./codeSign/windowsSignAzureManager" export class WinPackager extends PlatformPackager { _iconPath = new Lazy(() => this.getOrConvertIcon("ico")) @@ -128,7 +126,7 @@ export class WinPackager extends PlatformPackager { options: this.platformSpecificBuildOptions, } - const didSignSuccessfully = await this.doSign(signOptions) + const didSignSuccessfully = await signWindows(signOptions, this) if (!didSignSuccessfully && this.forceCodeSigning) { throw new InvalidConfigurationError( `App is not signed and "forceCodeSigning" is set to true, please ensure that code signing configuration is correct, please see https://electron.build/code-signing` @@ -137,25 +135,6 @@ export class WinPackager extends PlatformPackager { return didSignSuccessfully } - private async doSign(options: WindowsSignOptions) { - return retry( - () => signWindows(options, this), - 3, - 500, - 500, - 0, - (e: any) => { - // https://github.com/electron-userland/electron-builder/issues/1414 - const message = e.message - if (message != null && message.includes("Couldn't resolve host name")) { - log.warn({ error: message }, `cannot sign`) - return true - } - return false - } - ) - } - async signAndEditResources(file: string, arch: Arch, outDir: string, internalName?: string | null, requestedExecutionLevel?: RequestedExecutionLevel | null) { const appInfo = this.appInfo @@ -267,9 +246,10 @@ export class WinPackager extends PlatformPackager { return false } - await BluebirdPromise.map(readdir(packContext.appOutDir), (file: string): any => { + const files = await readdir(packContext.appOutDir) + for (const file of files) { if (file === exeFileName) { - return this.signAndEditResources( + await this.signAndEditResources( path.join(packContext.appOutDir, exeFileName), packContext.arch, packContext.outDir, @@ -277,10 +257,9 @@ export class WinPackager extends PlatformPackager { this.platformSpecificBuildOptions.requestedExecutionLevel ) } else if (this.shouldSignFile(file)) { - return this.sign(path.join(packContext.appOutDir, file)) + await this.sign(path.join(packContext.appOutDir, file)) } - return null - }) + } if (!isAsar) { return true @@ -291,7 +270,9 @@ export class WinPackager extends PlatformPackager { return walk(outDir, (file, stat) => stat.isDirectory() || this.shouldSignFile(file)) } const filesToSign = await Promise.all([filesPromise(["resources", "app.asar.unpacked"]), filesPromise(["swiftshader"])]) - await BluebirdPromise.map(filesToSign.flat(1), file => this.sign(file), { concurrency: 4 }) + for (const file of filesToSign.flat(1)) { + await this.sign(file) + } return true }