From 4c885c49028b9c67ef0bfe994544fc8835d4e393 Mon Sep 17 00:00:00 2001 From: ethernet Date: Sun, 9 Aug 2026 17:02:13 -0400 Subject: [PATCH] fix(ci): the signing dlib skips the imds probe that hangs hosted runners --- apps/desktop/electron-builder.config.cjs | 45 +++++++++++++++++++ apps/desktop/scripts/run-electron-builder.mjs | 32 ++++--------- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/apps/desktop/electron-builder.config.cjs b/apps/desktop/electron-builder.config.cjs index ddd14fb5c8600..2df88117c07a2 100644 --- a/apps/desktop/electron-builder.config.cjs +++ b/apps/desktop/electron-builder.config.cjs @@ -37,8 +37,53 @@ function isMachO(file) { return MACHO_MAGICS.has(buf.readUInt32BE(0)) } +// Windows signing with Azure Trusted Signing. Composed here, not as +// -c.win.sign.* CLI arguments, for two reasons: the publisherName holds +// spaces and commas that do not survive cmd.exe argument hops, and +// additionalMetadata.ExcludeCredentials is an ARRAY, which dot-notation +// cannot express. This file loads inside the electron-builder process, +// so the values pass from the environment verbatim. +// +// ExcludeCredentials keeps only AzureCliCredential — the one azure/login +// (OIDC) prepares in CI. The dlib otherwise walks the full +// DefaultAzureCredential chain, and on GitHub-hosted runners (Azure VMs) +// the ManagedIdentityCredential probe reaches a live IMDS endpoint that +// never grants a token — observed as signtool hanging on the arm64 +// runners. +function windowsSigning() { + if (!process.env.AZURE_SIGN_ENDPOINT || !process.env.AZURE_CLIENT_ID) { + return {} + } + return { + sign: { + type: "azure", + endpoint: process.env.AZURE_SIGN_ENDPOINT, + codeSigningAccountName: process.env.AZURE_SIGN_ACCOUNT, + certificateProfileName: process.env.AZURE_SIGN_PROFILE, + publisherName: process.env.AZURE_SIGN_PUBLISHER, + additionalMetadata: { + ExcludeCredentials: [ + "EnvironmentCredential", + "WorkloadIdentityCredential", + "ManagedIdentityCredential", + "SharedTokenCacheCredential", + "VisualStudioCredential", + "VisualStudioCodeCredential", + "AzurePowerShellCredential", + "AzureDeveloperCliCredential", + "InteractiveBrowserCredential", + ], + }, + }, + } +} + module.exports = { ...build, + win: { + ...build.win, + ...windowsSigning(), + }, mac: { ...build.mac, sign: { diff --git a/apps/desktop/scripts/run-electron-builder.mjs b/apps/desktop/scripts/run-electron-builder.mjs index 12baa81ad6f26..62686cd6fcfdf 100644 --- a/apps/desktop/scripts/run-electron-builder.mjs +++ b/apps/desktop/scripts/run-electron-builder.mjs @@ -1,6 +1,5 @@ -// Wraps the electron-builder CLI so config that cannot ride through cmd.exe -// argument hops (Windows signing values with spaces) is composed here, in the -// first spawn with no shell in between. +// Wraps the electron-builder CLI so the arguments compose in one place, in +// the first spawn with no shell in between. // // electron-builder downloads and extracts Electron itself (via electronVersion // + ELECTRON_MIRROR). Earlier revisions passed -c.electronDist to reuse the @@ -50,28 +49,13 @@ if (!args.includes("--publish") && !args.some((a) => a.startsWith("-p"))) { args.push("--publish", "never") } -// Windows signing config is composed HERE, from the AZURE_SIGN_* variables, -// not passed down as -c arguments. The publisherName contains spaces and -// commas, and no quoting survives the cmd.exe hops between the outer build -// script, npm's lifecycle spawn, and this script. This spawn is the first -// one with no shell in between, so values pass through verbatim. -// (win.sign.type=azure is the 27.x schema; 26.x called it azureSignOptions. -// 27 signs through signtool /dlib from the winCodeSign 1.3.0 toolset — no -// PowerShell TrustedSigning module, which froze the arm64 CI runner.) -if ( - args.includes("--win") && - process.env.AZURE_SIGN_ENDPOINT && - process.env.AZURE_CLIENT_ID && - !args.some((a) => a.includes("win.sign")) -) { +// Windows signing config lives in electron-builder.config.cjs, composed from +// the AZURE_SIGN_* variables. It cannot ride through -c arguments: the +// publisherName contains spaces and commas that die in cmd.exe hops, and +// additionalMetadata.ExcludeCredentials is an array, which dot-notation +// cannot express. This block only announces the decision in the log. +if (args.includes("--win") && process.env.AZURE_SIGN_ENDPOINT && process.env.AZURE_CLIENT_ID) { console.log(`[run-electron-builder] Windows signing: Azure Trusted Signing at ${process.env.AZURE_SIGN_ENDPOINT}`) - args.push( - "-c.win.sign.type=azure", - `-c.win.sign.endpoint=${process.env.AZURE_SIGN_ENDPOINT}`, - `-c.win.sign.codeSigningAccountName=${process.env.AZURE_SIGN_ACCOUNT}`, - `-c.win.sign.certificateProfileName=${process.env.AZURE_SIGN_PROFILE}`, - `-c.win.sign.publisherName=${process.env.AZURE_SIGN_PUBLISHER}` - ) } const result = spawnSync(process.execPath, [electronBuilderCli(), ...args], {