Closed iliakolev closed 5 days ago
I guess a lot of those powershell commands could be copy-pasted/translated into electron-builder's signing workflow, but if any updates happen to that github action, they won't be propagated into electron-builder without someone opening an issue here. Would that be a problem?
We're also interested in this. There is a nice guide for implementation of trusted EV code signing using Azure here: https://melatonin.dev/blog/code-signing-on-windows-with-azure-trusted-signing/
Basically, we'd need to call the action multiple times:
Currently, it seems that steps 1 and 2 are done in one action and therefore it's not possible to execute code signing after each of the steps using the GitHub action, right?
From the github action code it looks like a powershell script that installs the TrustedSigning module and then invokes it with appropriate params is all that is required:
Install-Module -Name TrustedSigning -RequiredVersion 0.4.1 -Force -Repository PSGallery
Invoke-TrustedSigning @params
This should be possible to add to electron-builder with some extra env vars?
I quickly put together a basic copy of the action code for an afterSign
hook, which maybe you'd be able to test with.
Basically, we skip the signing stage for the regular signing process. Instead, we leverage the afterSign
for running the powershell commands.
Would you be willing to do some investigating from there? Not sure if the params need to be passed in as a var or as a literal string. I don't have any azure account to test this myself
// electron-builder-config.ts
import { AfterPackContext, Configuration } from "app-builder-lib"
import { CustomWindowsSignTaskConfiguration } from "app-builder-lib/out/codeSign/windowsCodeSign"
import { WinPackager } from "app-builder-lib/out/winPackager"
import { execFile } from "child_process"
// Configuration object
export default {
// base configuration
win: {
// block signing
sign: (_configuration: CustomWindowsSignTaskConfiguration, _packager: WinPackager | undefined) => {
return Promise.resolve()
}
},
afterSign: async (context: AfterPackContext) => {
await new Promise((resolve, reject) => execFile(
`chcp 65001 >NUL & powershell.exe`,
["-Name", "TrustedSigning", "-RequiredVersion", "0.4.1", "-Force", "-Repository", "PSGallery",],
{
shell: true,
timeout: 60 * 1000,
},
(error, stdout, stderr) => {
if (error || stderr) {
reject(error?.message || stderr)
}
resolve(stdout)
}
))
// requires the following env vars also set:
// AZURE_TENANT_ID
// AZURE_CLIENT_ID
// AZURE_CLIENT_SECRET
// AZURE_CLIENT_CERTIFICATE_PATH
// AZURE_CLIENT_SEND_CERTIFICATE_CHAIN
// AZURE_USERNAME
// AZURE_PASSWORD
const params: any = {}
// Variables you can define
const trustedSigningAccountName = undefined
const certificateProfileName = undefined
const files = undefined
const filesFolder = undefined
const filesFolderFilter = undefined
const filesFolderRecurse = false // boolean
const filesFolderDepth = undefined // integer
const filesCatalog = undefined
const fileDigest = undefined
const timestampRfc3161 = undefined
const timestampDigest = undefined
const appendSignature = false // boolean
const description = undefined
const descriptionUrl = undefined
const generateDigestPath = undefined
const generateDigestXml = false // boolean
const ingestDigestPath = undefined
const signDigest = false // boolean
const generatePageHashes = false // boolean
const suppressPageHashes = false // boolean
const generatePkcs7 = false // boolean
const pkcs7Options = undefined
const pkcs7Oid = undefined
const enhancedKeyUsage = undefined
const excludeEnvironmentCredential = false // boolean
const excludeWorkloadIdentityCredential = false // boolean
const excludeManagedIdentityCredential = false // boolean
const excludeSharedTokenCacheCredential = false // boolean
const excludeVisualStudioCredential = false // boolean
const excludeVisualStudioCodeCredential = false // boolean
const excludeAzureCliCredential = false // boolean
const excludeAzurePowerShellCredential = false // boolean
const excludeAzureDeveloperCliCredential = false // boolean
const excludeInteractiveBrowserCredential = false // boolean
const timeout = undefined // integer
const batchSize = undefined // integer
const notNullOrEmptyString = (value: any) => {
if (typeof value === 'string') {
return value.trim().length !== 0
}
return value !== undefined || value !== null
}
if (notNullOrEmptyString(trustedSigningAccountName)) {
params.CodeSigningAccountName = trustedSigningAccountName
}
if (notNullOrEmptyString(certificateProfileName)) {
params.CertificateProfileName = certificateProfileName
}
if (notNullOrEmptyString(files)) {
params.Files = files
}
if (notNullOrEmptyString(filesFolder)) {
params.FilesFolder = filesFolder
}
if (notNullOrEmptyString(filesFolderFilter)) {
params.FilesFolderFilter = filesFolderFilter
}
if (notNullOrEmptyString(filesFolderRecurse)) {
params.FilesFolderRecurse = filesFolderRecurse // boolean
}
if (notNullOrEmptyString(filesFolderDepth)) {
params.FilesFolderDepth = filesFolderDepth // integer
}
if (notNullOrEmptyString(filesCatalog)) {
params.FilesCatalog = filesCatalog
}
if (notNullOrEmptyString(fileDigest)) {
params.FileDigest = fileDigest
}
if (notNullOrEmptyString(timestampRfc3161)) {
params.TimestampRfc3161 = timestampRfc3161
}
if (notNullOrEmptyString(timestampDigest)) {
params.TimestampDigest = timestampDigest
}
if (notNullOrEmptyString(appendSignature)) {
params.AppendSignature = appendSignature // boolean
}
if (notNullOrEmptyString(description)) {
params.Description = description
}
if (notNullOrEmptyString(descriptionUrl)) {
params.DescriptionUrl = descriptionUrl
}
if (notNullOrEmptyString(generateDigestPath)) {
params.GenerateDigestPath = generateDigestPath
}
if (notNullOrEmptyString(generateDigestXml)) {
params.GenerateDigestXml = generateDigestXml // boolean
}
if (notNullOrEmptyString(ingestDigestPath)) {
params.IngestDigestPath = ingestDigestPath
}
if (notNullOrEmptyString(signDigest)) {
params.SignDigest = signDigest // boolean
}
if (notNullOrEmptyString(generatePageHashes)) {
params.GeneratePageHashes = generatePageHashes // boolean
}
if (notNullOrEmptyString(suppressPageHashes)) {
params.SuppressPageHashes = suppressPageHashes // boolean
}
if (notNullOrEmptyString(generatePkcs7)) {
params.GeneratePkcs7 = generatePkcs7 // boolean
}
if (notNullOrEmptyString(pkcs7Options)) {
params.Pkcs7Options = pkcs7Options
}
if (notNullOrEmptyString(pkcs7Oid)) {
params.Pkcs7Oid = pkcs7Oid
}
if (notNullOrEmptyString(enhancedKeyUsage)) {
params.EnhancedKeyUsage = enhancedKeyUsage
}
if (notNullOrEmptyString(excludeEnvironmentCredential)) {
params.ExcludeEnvironmentCredential = excludeEnvironmentCredential // boolean
}
if (notNullOrEmptyString(excludeWorkloadIdentityCredential)) {
params.ExcludeWorkloadIdentityCredential = excludeWorkloadIdentityCredential // boolean
}
if (notNullOrEmptyString(excludeManagedIdentityCredential)) {
params.ExcludeManagedIdentityCredential = excludeManagedIdentityCredential // boolean
}
if (notNullOrEmptyString(excludeSharedTokenCacheCredential)) {
params.ExcludeSharedTokenCacheCredential = excludeSharedTokenCacheCredential // boolean
}
if (notNullOrEmptyString(excludeVisualStudioCredential)) {
params.ExcludeVisualStudioCredential = excludeVisualStudioCredential // boolean
}
if (notNullOrEmptyString(excludeVisualStudioCodeCredential)) {
params.ExcludeVisualStudioCodeCredential = excludeVisualStudioCodeCredential // boolean
}
if (notNullOrEmptyString(excludeAzureCliCredential)) {
params.ExcludeAzureCliCredential = excludeAzureCliCredential // boolean
}
if (notNullOrEmptyString(excludeAzurePowerShellCredential)) {
params.ExcludeAzurePowerShellCredential = excludeAzurePowerShellCredential // boolean
}
if (notNullOrEmptyString(excludeAzureDeveloperCliCredential)) {
params.ExcludeAzureDeveloperCliCredential = excludeAzureDeveloperCliCredential // boolean
}
if (notNullOrEmptyString(excludeInteractiveBrowserCredential)) {
params.ExcludeInteractiveBrowserCredential = excludeInteractiveBrowserCredential // boolean
}
if (notNullOrEmptyString(timeout)) {
params.Timeout = timeout // integer
}
if (notNullOrEmptyString(batchSize)) {
params.BatchSize = batchSize // integer
}
await new Promise((resolve, reject) => execFile(
`chcp 65001 >NUL & powershell.exe`,
["Invoke-TrustedSigning", params],
{
shell: true,
timeout: 60 * 1000,
},
(error, stdout, stderr) => {
if (error || stderr) {
reject(error?.message || stderr)
}
resolve(stdout)
}
))
},
}
Can confirm that this approach works! Here is what I ended up using:
electron-builder.yml
:
...
afterSign: ./scripts/after-sign.js
win:
sign: ./scripts/nop.js
scripts/after-sign.js
:
const { spawnSync } = require('node:child_process');
exports.default = async function sign(context) {
spawnSync(
'powershell.exe',
['Install-Module', '-Name', 'TrustedSigning', '-RequiredVersion', '0.4.1', '-Force', '-Repository', 'PSGallery'],
{ shell: true, stdio: 'inherit' },
);
const params = {
Endpoint: 'https://eus.codesigning.azure.net/',
CodeSigningAccountName: '<code signing account name>',
CertificateProfileName: '<certificate profile name>',
FilesFolder: context.appOutDir,
FilesFolderFilter: 'exe,dll',
FileDigest: 'SHA256',
TimestampRfc3161: 'http://timestamp.acs.microsoft.com',
TimestampDigest: 'SHA256',
};
spawnSync('powershell.exe', ['Invoke-TrustedSigning', params], { shell: true, stdio: 'inherit' });
};
scripts/nop.js
:
exports.default = async function nop() {};
@MikeJerred Wouldn't your proposed solution result in only the packaged executables being signed, not the installer (e.g. NSIS executable file) that results from the packaging process? It seems that the after-sign.js
is not executed after the installer has been compiled.
Another problem I've spotted: When testing your script, I received the following output + Invoke-TrustedSigning [object Object]
plus an error about missing mandatory parameters. It seems to me that passing a compiled string might work better (but still have to test it myself):
const paramsString = Object.keys(params).map(key => ` -${key} ${params[key]}`).join('');
spawnSync('powershell.exe', ['Invoke-TrustedSigning', paramsString], { shell: true, stdio: 'inherit' });
I just received confirmation from MS that I have approval for the trusted signing cert. Based on some of the above hacks, I may have to skip Windows signing and use a signtool manually since installers as well as app needs to be signed.
Signing the installer after the electron-builder packing process during and extra build step results in wrong sha512 hashes in the resulting update YAML files.
I've managed to get this signed with my own script during the build...all executables checkout out nicely. Gone are the days of EV certs.
I'm looking into implementing this in electron-builder, but won't have a way to test (as I don't have any Azure account). So if anyone is willing, I'd be happy to supply a patch-package patch for testing out my implementation.
What are the required params for Invoke-TrustedSigning
?
Just these?
const params = {
Endpoint: 'https://eus.codesigning.azure.net/',
CodeSigningAccountName: '<code signing account name>',
CertificateProfileName: '<certificate profile name>',
FilesFolder: context.appOutDir,
FilesFolderFilter: 'exe,dll',
FileDigest: 'SHA256',
TimestampRfc3161: 'http://timestamp.acs.microsoft.com',
TimestampDigest: 'SHA256',
};
I also see this example configuration here: https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations
"Endpoint": "<Trusted Signing account endpoint>",
"CodeSigningAccountName": "<Trusted Signing account name>",
"CertificateProfileName": "<Certificate profile name>",
Reason I ask is to see if there are any default values I can apply or using enums (for things like TimestampDigest
) where it probably doesn't have to be a basic string
property.
I'm looking into implementing this in electron-builder, but won't have a way to test (as I don't have any Azure account). So if anyone is willing, I'd be happy to supply a patch-package patch for testing out my implementation.
What are the required params for
Invoke-TrustedSigning
? Just these?const params = { Endpoint: 'https://eus.codesigning.azure.net/', CodeSigningAccountName: '<code signing account name>', CertificateProfileName: '<certificate profile name>', FilesFolder: context.appOutDir, FilesFolderFilter: 'exe,dll', FileDigest: 'SHA256', TimestampRfc3161: 'http://timestamp.acs.microsoft.com', TimestampDigest: 'SHA256', };
I also see this example configuration here: https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations
"Endpoint": "<Trusted Signing account endpoint>", "CodeSigningAccountName": "<Trusted Signing account name>", "CertificateProfileName": "<Certificate profile name>",
Reason I ask is to see if there are any default values I can apply or using enums (for things like
TimestampDigest
) where it probably doesn't have to be a basicstring
property.
I am happy to help with testing. Those params (plus the azure auth env vars) were enough for the signing to complete without errors when I tried it.
Okay, nvm, the patch is too large. My best recommendation is cloning electron-builder, pulling this PR https://github.com/electron-userland/electron-builder/pull/8458 via gh pr checkout 8458
or checkout branch azure-signing
, compile with pnpm compile
, and copy the compiled files into your project directly.
Example setup: https://github.com/electron-userland/electron-builder/blob/master/CONTRIBUTING.md#to-setup-a-local-dev-environment
From there, the configuration is within win.azureOptions
(other name suggestions are welcome). I took the minimal required fields I could find in the Azure docs, then left it open with [k: string]: string
for custom usage scenarios
https://github.com/electron-userland/electron-builder/blob/0d24b78d43ce12f74f5bc073478688c7ad814034/packages/app-builder-lib/src/options/winOptions.ts#L178-L202
Not sure if my local dev setup is correct because I get an error when doing this:
$ npx electron-builder --dir
Error: Cannot find module 'resedit'
Require stack:
- D:\dev\projects\glint\electron\.yalc\app-builder-lib\out\electron\electronWin.js
- D:\dev\projects\glint\electron\.yalc\app-builder-lib\out\electron\ElectronFramework.js
- D:\dev\projects\glint\electron\.yalc\app-builder-lib\out\packager.js
- D:\dev\projects\glint\electron\.yalc\app-builder-lib\out\index.js
- D:\dev\projects\glint\electron\.yalc\electron-builder\out\builder.js
- D:\dev\projects\glint\electron\.yalc\electron-builder\out\cli\cli.js
- D:\dev\projects\glint\electron\.yalc\electron-builder\cli.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1145:15)
at Function.Module._load (node:internal/modules/cjs/loader:986:27)
at Module.require (node:internal/modules/cjs/loader:1233:19)
at require (node:internal/modules/helpers:179:18)
at Object.<anonymous> (D:\dev\projects\glint\electron\.yalc\app-builder-lib\src\electron\electronWin.ts:3:1)
at Module._compile (node:internal/modules/cjs/loader:1358:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
at Module.load (node:internal/modules/cjs/loader:1208:32)
at Function.Module._load (node:internal/modules/cjs/loader:1024:12)
at Module.require (node:internal/modules/cjs/loader:1233:19)
at require (node:internal/modules/helpers:179:18)
at Object.<anonymous> (D:\dev\projects\glint\electron\.yalc\app-builder-lib\src\electron\ElectronFramework.ts:13:1)
at Module._compile (node:internal/modules/cjs/loader:1358:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
at Module.load (node:internal/modules/cjs/loader:1208:32)
at Function.Module._load (node:internal/modules/cjs/loader:1024:12)
You'll either need to start with the base v25.0.5 installed in your package.json (as that includes most recent dependencies to be resolved during install
) before using yalc or you can temporarily add "resedit": "^1.7.0",
to your devDependencies
https://github.com/electron-userland/electron-builder/blob/c081df8e04494645028c4160bcc1376f029cbca5/packages/app-builder-lib/package.json#L76
You'll either need to start with the base v25.0.5 installed in your package.json (as that includes most recent dependencies to be resolved during
install
) before using yalc or you can temporarily add"resedit": "^1.7.0",
to yourdevDependencies
That has fixed that issue, however it still doesn't work:
$ npx electron-builder --dir
• electron-builder version=25.0.6 os=10.0.19045
• loaded configuration file=D:\dev\projects\glint\electron\electron-builder.yml
⨯ Invalid configuration object. electron-builder 25.0.6 has been initialized using a configuration object that does not match the API schema.
- configuration.win has an unknown property 'azureOptions'. These properties are valid:
Looks like yalc
isn't copying over the newly updated scheme.json.
https://github.com/electron-userland/electron-builder/blob/52df0604c998f38324640a9d93f884824edd7691/packages/app-builder-lib/scheme.json
You can copy paste it manually in your node modules path or take the hard-copy approach (instead of using yalc
) that I typically use instead using rsync
. cp
also would work, but I just prefer the logging/update-only/include args that rsync provides
rsync -upaRv --include='*.js' --include='*.d.ts' --include='*.nsi' --include='*.json' --include='*/' --include='*.py*' --include='*.tiff' --exclude='*' ~/Development/electron-builder/packages/./* node_modules/
@MikeJerred I'm thinking of releasing the refactored signing code as part of 25.0.7 with logging that azure signing is in beta
.
Previous signing configurations will still work, but logging has been added to note deprecated fields and where they've been moved to (probably within signtoolOptions
)
Once released, I would like additional volunteers to test it though with DEBUG=electron-builder
env var for console logs to make sure everything is kosher and can un-tag it as a beta feature. CC @OrganicChem @jmeinke @iliakolev 🙃
You could also possibly release it on the beta release channel, if the changes are too impactful? But as only warnings will show, this shouldn't be a problem?
Anyway, also switching here to Azure Trusted Signing, as our previous certificate was expired. As soon as everything is in place on Azure (verifying company etc) I also will test this out!
Excellent! Thank you
Previous logic is all in place for using signtool.exe
, however, the new config has been moved to within a dedicated property signtoolOptions
so a bit of refactoring also took place to keep the implementation clean (and avoid installing azure signing provider+modules on every signing request)
It'll be default released to next
tag (as opposed to latest
).
Alrighty. Beta signing implementation has been released in ^25.1
, please give it a shot with DEBUG=electron-builder
and report back!
I'm expecting bug reports, so I also request patience as I get this implementation fully functional. 🙃 Also, not sure if the cmd line debug logs will need any info redacted before posting them here since it's a verbatim log of the powershell Invoke-TrustedSigning
command (double check any password/tokens provided aren't present)
From my local testing, I got this working up until the point it does Invoke-TrustedSigning
as then the parallels VM prompts for Endpoint
(since I didn't pass it in as an argument for test purposes), as I don't have an Azure account to test with. Requires NuGet
package provider to be installed and TrustedSigning
module, but both also required "-Scope", "CurrentUser"
since the cmd prompt that is automatically executed within a parallels VM is not elevated to admin.
Logs below with DEBUG=electron-builder
• signing file=dist/win-unpacked/electron-quick-start-typescript.exe certificateFile=Foo Bar.pfx
• signing with Azure Trusted Signing path=/Users/dev/Development/electron-builder-test-2/dist/win-unpacked/electron-quick-start-typescript.exe
• executing file=prlctl args=list -i -s name
• executing file=prlctl args=exec {6db0fa46-4f04-432a-a546-f8584beac98f} --current-user powershell.exe -NoProfile -NonInteractive -Command Get-Command pwsh.exe
• ensure that 'Share folders' is set to 'All Disks', see https://goo.gl/E6XphP
• unable to find pwsh.exe, falling back to powershell.exe
• executing file=prlctl args=exec {6db0fa46-4f04-432a-a546-f8584beac98f} --current-user powershell.exe Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
• executed file=prlctl stdout=
Name Version Source Summary
---- ------- ------ -------
nuget 2.8.5.208 https://onege... NuGet provider for the OneGet meta-package manager
• executing file=prlctl args=exec {6db0fa46-4f04-432a-a546-f8584beac98f} --current-user powershell.exe Install-Module -Name TrustedSigning -RequiredVersion 0.4.1 -Force -Repository PSGallery -Scope CurrentUser
• executed file=prlctl
• executing file=prlctl args=exec {6db0fa46-4f04-432a-a546-f8584beac98f} --current-user powershell.exe Invoke-TrustedSigning -Files /Users/dev/Development/electron-builder-test-2/dist/win-unpacked/electron-quick-start-typescript.exe
Implementation details: https://github.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/codeSign/windowsSignAzureManager.ts
Configuration details: https://github.com/electron-userland/electron-builder/blob/b3ce7f788cccf87ba841c9189a3b8758cd7c27c2/packages/app-builder-lib/src/options/winOptions.ts#L88-L91 https://github.com/electron-userland/electron-builder/blob/b3ce7f788cccf87ba841c9189a3b8758cd7c27c2/packages/app-builder-lib/src/options/winOptions.ts#L190-L210
I installed 25.1.0 but doing DEBUG=electron-builder npx electron-builder --dir
gives an error:
Error: Cannot find module 'app-builder-lib/out/util/config/load'
Require stack:
- D:\dev\projects\glint\electron\node_modules\electron-builder\out\cli\cli.js
- D:\dev\projects\glint\electron\node_modules\electron-builder\cli.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1144:15)
at Function.Module._load (node:internal/modules/cjs/loader:985:27)
The app-builder-lib in node_modules is v25.0.5, and it doesn't have a config
folder under out/util
Thanks for checking.
Hmmm, it sounds like it desynced the release versioning during the CI/CD. It's been acting finicky lately. Can you try force installing app-builder-lib: 25.1.1
in your package.json?
For some reason a 25.1.0 version wasn't published, but a 25.1.1 was https://www.npmjs.com/package/app-builder-lib?activeTab=versions
I'll look into the dependency resolution issue
I added "app-builder-lib": "25.1.1"
to my package.json
but I get this error on npm install
:
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: app-builder-lib@25.1.1
npm WARN Found: dmg-builder@25.0.5
npm WARN node_modules/dmg-builder
npm WARN dmg-builder@"^25" from electron-builder@25.1.0
npm WARN node_modules/electron-builder
npm WARN dev electron-builder@"^25.1.0" from the root project
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer dmg-builder@"^25.1.025.1.0" from app-builder-lib@25.1.1
npm WARN node_modules/app-builder-lib
npm WARN dev app-builder-lib@"25.1.1" from the root project
npm WARN 1 more (electron-builder)
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: app-builder-lib@25.1.1
npm WARN Found: peer dmg-builder@"^25.1.025.1.0" from app-builder-lib@25.1.1
npm WARN node_modules/app-builder-lib
npm WARN dev app-builder-lib@"25.1.1" from the root project
npm WARN 1 more (electron-builder)
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer dmg-builder@"^25.1.025.1.0" from app-builder-lib@25.1.1
npm WARN node_modules/app-builder-lib
npm WARN dev app-builder-lib@"25.1.1" from the root project
npm WARN 1 more (electron-builder)
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: app-builder-lib@25.1.1
npm WARN Found: electron-builder-squirrel-windows@undefined
npm WARN node_modules/electron-builder-squirrel-windows
npm WARN peer electron-builder-squirrel-windows@"^25.1.025.1.0" from app-builder-lib@25.1.1
npm WARN node_modules/app-builder-lib
npm WARN dev app-builder-lib@"25.1.1" from the root project
npm WARN 1 more (electron-builder)
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer electron-builder-squirrel-windows@"^25.1.025.1.0" from app-builder-lib@25.1.1
npm WARN node_modules/app-builder-lib
npm WARN dev app-builder-lib@"25.1.1" from the root project
npm WARN 1 more (electron-builder)
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: app-builder-lib@25.1.1
npm WARN Found: electron-builder-squirrel-windows@undefined
npm WARN node_modules/electron-builder-squirrel-windows
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer electron-builder-squirrel-windows@"^25.1.025.1.0" from app-builder-lib@25.1.1
npm WARN node_modules/app-builder-lib
npm WARN dev app-builder-lib@"25.1.1" from the root project
npm WARN 1 more (electron-builder)
npm ERR! code ETARGET
npm ERR! notarget No matching version found for dmg-builder@^25.1.025.1.0.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
Kk. I've redeployed the monorepo to resync all the workspace versions. Please try 25.1.2
Kk. I've redeployed the monorepo to resync all the workspace versions. Please try
25.1.2
OK this version installs properly. It should also be noted that the docs are stating to use win.azureOptions
but actually it should be win.azureSignOptions
, regardless I am not seeing any error but the executable is not being signed.
This is my electron-builder.yml
:
win:
publisherName: Logic Over Snacks Ltd.
azureSignOptions:
endpoint: https://eus.codesigning.azure.net/
certificateProfileName: ...
codeSigningAccountName: ...
I also have set the 3 env vars AZURE_TENANT_ID
, AZURE_CLIENT_ID
, and AZURE_CLIENT_SECRET
.
I run this command: DEBUG=electron-builder npx electron-builder --dir
which completes without errors, but the executable created does not have a digital signature.
@MikeJerred Can you please upload/send the logs for the azure signing steps? Should start after the line
• signing with Azure Trusted Signing path=/Users/dev/Development/electron-builder-test-2/dist/win-unpacked/electron-quick-start-typescript.exe
Please make sure to redact any sensitive info from the logs if present. Also am happy to discuss further via discord (@onegoldfish) to streamline debugging/implementing this feature.
This is the log, I redacted some long bits that I don't think are relevant:
$ DEBUG=electron-builder npx electron-builder --dir
• electron-builder version=25.1.2 os=10.0.19045
• loaded configuration file=D:\dev\projects\glint\electron\electron-builder.yml
• effective config config=directories
...
<contents of electron-builder.yml>
...
• writing effective config file=packaged\builder-effective-config.yaml
• skipped dependencies rebuild reason=npmRebuild is set to false
• packaging platform=win32 arch=x64 electron=29.1.4 appOutDir=packaged\win-unpacked
• spawning command=D:\dev\projects\glint\electron\node_modules\app-builder-bin\win\x64\app-builder.exe unpack-electron --configuration [{"platform":"win32","arch":"x64","version":"29.1.4"}] --output D:\dev\projects\glint\electron\packaged\win-unpacked --distMacOsAppName Electron.app
• map async taskCount=2
• map async taskCount=1
• map async taskCount=73
• exited command=app-builder.exe code=0 pid=31416
• asar usage is disabled — this is strongly not recommended solution=enable asar and use asarUnpack to unpack files that must be externally available
• spawning command=D:\dev\projects\glint\electron\node_modules\app-builder-bin\win\x64\app-builder.exe node-dep-tree --dir D:\dev\projects\glint\electron --flatten
• unresolved deps unresolved=lowercase-keys nodeModuleDir=D:\dev\projects\glint\node_modules round=0
• unresolved deps unresolved=lowercase-keysresponselike nodeModuleDir=D:\dev\projects\glint\node_modules round=0
...
<a lot of "unresolved deps">
...
• exited command=app-builder.exe code=0 pid=6080 out=[{...<lots of packages>...}]
• asar usage is disabled — this is strongly not recommended solution=enable asar and use asarUnpack to unpack files that must be externally available
• spawning command=D:\dev\projects\glint\electron\node_modules\app-builder-bin\win\x64\app-builder.exe icon --format ico --root D:\dev\projects\glint\electron\build --root D:\dev\projects\glint\electron --out D:\dev\projects\glint\electron\packaged\.icon-ico
• path resolved path=D:\dev\projects\glint\electron\build\icon.ico outputFormat=ico
• exited command=app-builder.exe code=0 pid=35164 out={"icons":[{"file":"D:\\dev\\projects\\glint\\electron\\build\\icon.ico","size":256}],"isFallback":false}
• spawning command=D:\dev\projects\glint\electron\node_modules\app-builder-bin\win\x64\app-builder.exe rcedit --args ["D:\\dev\\projects\\glint\\electron\\packaged\\win-unpacked\\Glint.exe","--set-version-string","FileDescription","An interface tool for git","--set-version-string","ProductName","Glint","--set-version-string","LegalCopyright","Copyright © 2024 Logic Over Snacks Ltd.","--set-file-version","1.8.9","--set-product-version","1.8.9.0","--set-version-string","InternalName","Glint","--set-version-string","OriginalFilename","","--set-version-string","CompanyName","Logic Over Snacks Ltd.","--set-icon","D:\\dev\\projects\\glint\\electron\\build\\icon.ico"]
• found existing path=C:\Users\mjerr\AppData\Local\electron-builder\Cache\winCodeSign\winCodeSign-2.6.0
• execute command command='C:\Users\mjerr\AppData\Local\electron-builder\Cache\winCodeSign\winCodeSign-2.6.0\rcedit-x64.exe' 'D:\dev\projects\glint\electron\packaged\win-unpacked\Glint.exe' --set-version-string FileDescription 'An interface tool for git' --set-version-string ProductName Glint --set-version-string LegalCopyright 'Copyright © 2024 Logic Over Snacks Ltd.' --set-file-version 1.8.9 --set-product-version 1.8.9.0 --set-version-string InternalName Glint --set-version-string OriginalFilename '' --set-version-string CompanyName 'Logic Over Snacks Ltd.' --set-icon 'D:\dev\projects\glint\electron\build\icon.ico' workingDirectory=
• command executed executable=C:\Users\mjerr\AppData\Local\electron-builder\Cache\winCodeSign\winCodeSign-2.6.0\rcedit-x64.exe
• exited command=app-builder.exe code=0 pid=30740
wine&sign: 0s 390ms
Well that's super odd, it's hitting neither this line https://github.com/electron-userland/electron-builder/blob/5e21509a3f40d1a21f6f9ec9bf1d9d72c7149a21/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts#L13 nor https://github.com/electron-userland/electron-builder/blob/5e21509a3f40d1a21f6f9ec9bf1d9d72c7149a21/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts#L17
The code in my node_modules has those lines, it looks like I have the correct packages installed as far as I can see. Am I setting the debug flag correctly to enable the feature?
Honestly, I'm confused because those are log.info
commands, so it should be showing up without even having the DEBUG env var present. I tested the signtool implementation locally and it worked correctly too (in addition to the correct logging). Not sure why nothing is showing up for your logs, but it does have me worried that the signing refactor broke something for end-users.
Also not working here. At least not signing.
I see that this line is not returning valid data: https://github.com/electron-userland/electron-builder/blob/d1cb6bdbf8111156bb16839f501bdd9e6d477338/packages/app-builder-lib/src/winPackager.ts#L131
As when I set forceCodeSigning to true
, I hit the error message: 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
Effective config being print:
win:
signtoolOptions:
publisherName: "Manufacturer"
azureSignOptions:
endpoint: https://weu.codesigning.azure.net/
certificateProfileName: "profilenamehere"
forceCodeSigning: true
Also, is it normal that I had to had to run it in a elevated cmd prompt? I got this error message:
errorOut=ERROR: Cannot create symbolic link : A required privilege is not held by the client.
I don't know how this MemoryLazy thing works. But if I split it up, the signtoolManager resolves fine, but the cscInfo does not. But there is a value in "selected" which looks like to contain some sort of data?
checking value
checking cscInfo
Edit: Isn't is just because of no certificate file is being set? Which is not applicable in the azure case? So there should be a code path when no csc info is being set? (as now that part always returns?: https://github.com/electron-userland/electron-builder/blob/d1cb6bdbf8111156bb16839f501bdd9e6d477338/packages/app-builder-lib/src/winPackager.ts#L140 )
Great investigate work! Thank you :) Checking this asap. Will post results when I have more info
FWIW, I'm honestly shocked that this was not caught in the code signing unit tests. I'm also struggling to reproduce this locally in my test project with config
win: {
target: [{
target: 'zip',
arch: 'x64'
}],
signtoolOptions: {
certificateFile: 'Foo Bar.pfx',
publisherName: "Foo Bar",
},
forceCodeSigning: true
},
Logs of successful build using signtool:
• building target=nsis file=dist/electron-quick-start-typescript Setup 1.0.4.exe archs=arm64 oneClick=true perMachine=true
• signing file=dist/win-arm64-unpacked/resources/elevate.exe certificateFile=CN=Foo Bar, O=Foo Bar.pfx
• signing with signtool.exe path=dist/win-arm64-unpacked/resources/elevate.exe
• signing NSIS uninstaller file=dist/__uninstaller-nsis-electron-quick-start-typescript.exe certificateFile=CN=Foo Bar, O=Foo Bar.pfx
• signing with signtool.exe path=dist/__uninstaller-nsis-electron-quick-start-typescript.exe
• signing file=dist/electron-quick-start-typescript Setup 1.0.4.exe certificateFile=CN=Foo Bar, O=Foo Bar.pfx
• signing with signtool.exe path=dist/electron-quick-start-typescript Setup 1.0.4.exe
• building block map blockMapFile=dist/electron-quick-start-typescript Setup 1.0.4.exe.blockmap
I'll see if I can mock a way to do the azure signing method without calling Invoke-TrustedSigning since I can't get an azure test account for free AFAIK, but at least it could test the initial logic in that electron-builder flow? I'll see what I can do
Okay, I did a bit more refactoring and moved some of the signtool logic that was still in winPackager into the signtool manager class (https://github.com/electron-userland/electron-builder/pull/8524) If you're willing to test this @Bartel-C8 @MikeJerred, I have a patch-package file you can leverage on top of 25.1.4. Please let me know if it resolves the issue for you 🙂
patches/app-builder-lib+25.1.4.patch
[EDIT], patch didn't work, removing from comment to reduce verbosity of this thread/GH issue
Thanks for your changes @mmaietta , will try them out tonight.
FWIW, I'm honestly shocked that this was not caught in the code signing unit tests. I'm also struggling to reproduce this locally in my test project with config
But, there was probably no problem with normal signing. I am only testing with a azure signing config entry...
The problem is that electron-builder expects a code-signing file, as in your example as well, certificateFile
in the config.
For Azure signing there is no certificate file... So the signing code-flow also should happen when no certificate file is present in the config?
Anyway, I will test the patch provided. But it would be best to create a unit-test only containing some (dummy) Azure config, and see if the code signing path is hit (probably with errors, as no valid credentials are given)?
Great callout. I've added a unit test in the PR that throws Invalid Configuration when none of the required env var combinations are detected. I made the check occur after installing the nuget package provider and trusted signing module so that it is also covered in the CI tests. Good thing too, as GH runners use pwsh.exe, which differs from my VM of powershell.exe. There's some -Command differences between the two usages that I'm trying to iron out
Currently stuck on this which seems unique to GH runners even when I try locally with pwsh.exe
, which is proving to make my iterative debugging quite slow
"Exit code: 1. Command failed: pwsh.exe -NoProfile -NonInteractive -Command Get-PackageProvider | where name -eq 'nuget' | Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
Install-PackageProvider: No match was found for the specified search criteria for the provider 'NuGet'. The package provider requires 'PackageManagement' and 'Provider' tags. Please check if the specified package has the tags.
OK with that patch I get this:
$ npx electron-builder --dir
• electron-builder version=25.1.4 os=10.0.19045
• loaded configuration file=D:\dev\projects\glint\electron\electron-builder.yml
• writing effective config file=packaged\builder-effective-config.yaml
• skipped dependencies rebuild reason=npmRebuild is set to false
• packaging platform=win32 arch=x64 electron=29.1.4 appOutDir=packaged\win-unpacked
• asar usage is disabled — this is strongly not recommended solution=enable asar and use asarUnpack to unpack files that must be externally available
• signing with Azure Trusted Signing (beta) path=packaged\win-unpacked\resources\app\node_modules\@git-glint\nodegit\vendor\pageant_sha1.exe
• signing with Azure Trusted Signing (beta) path=packaged\win-unpacked\resources\app\node_modules\@git-glint\nodegit\vendor\pageant.exe
• installing required package provider (NuGet) and module (TrustedSigning) with scope CurrentUser
• Above command failed, retrying 3 more times
• Above command failed, retrying 3 more times
⨯ Cannot cleanup:
Error #1 --------------------------------------------------------------------------------
Error: Exit code: 64. Command failed: pwsh.exe -NoProfile -NonInteractive -Command Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
The argument '-Command Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser' is not recognized as the name of a script file. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
Usage: pwsh[.exe] [-Login] [[-File] <filePath> [args]]
[-Command { - | <script-block> [-args <arg-array>]
| <string> [<CommandParameters>] } ]
[-CommandWithArgs <string> [<CommandParameters>]
[-ConfigurationName <string>] [-ConfigurationFile <filePath>]
[-CustomPipeName <string>] [-EncodedCommand <Base64EncodedCommand>]
[-ExecutionPolicy <ExecutionPolicy>] [-InputFormat {Text | XML}]
[-Interactive] [-MTA] [-NoExit] [-NoLogo] [-NonInteractive] [-NoProfile]
[-NoProfileLoadTime] [-OutputFormat {Text | XML}]
[-SettingsFile <filePath>] [-SSHServerMode] [-STA]
[-Version] [-WindowStyle <style>]
[-WorkingDirectory <directoryPath>]
pwsh[.exe] -h | -Help | -? | /?
PowerShell Online Help https://aka.ms/powershell-docs
All parameters are case-insensitive.
Yep, I discovered that as well. Got it working out locally, but haven't posted a patch for the updated code yet. Thanks for checking folks!
Alright, got tests passing on GH runners covering invalid configuration + module installation in https://github.com/electron-userland/electron-builder/actions/runs/10998788786/job/30537499953?pr=8524
Patch file app-builder-lib+25.1.4.patch
diff --git a/node_modules/app-builder-lib/out/codeSign/windowsSignAzureManager.js b/node_modules/app-builder-lib/out/codeSign/windowsSignAzureManager.js
index 760e8ea..ee30ff2 100644
--- a/node_modules/app-builder-lib/out/codeSign/windowsSignAzureManager.js
+++ b/node_modules/app-builder-lib/out/codeSign/windowsSignAzureManager.js
@@ -10,9 +10,16 @@ class WindowsSignAzureManager {
async initializeProviderModules() {
const vm = await this.packager.vm.value;
const ps = await (0, windowsCodeSign_1.getPSCmd)(vm);
- builder_util_1.log.debug(null, "installing required package provider (NuGet) and module (TrustedSigning) with scope CurrentUser");
- await vm.exec(ps, ["Install-PackageProvider", "-Name", "NuGet", "-MinimumVersion", "2.8.5.201", "-Force", "-Scope", "CurrentUser"]);
- await vm.exec(ps, ["Install-Module", "-Name", "TrustedSigning", "-RequiredVersion", "0.4.1", "-Force", "-Repository", "PSGallery", "-Scope", "CurrentUser"]);
+ builder_util_1.log.info(null, "installing required module (TrustedSigning) with scope CurrentUser");
+ try {
+ await vm.exec(ps, ["-NoProfile", "-NonInteractive", "-Command", "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser"]);
+ }
+ catch (error) {
+ // Might not be needed, seems GH runners already have NuGet set up.
+ // Logging to debug just in case users run into this. If NuGet isn't present, Install-Module -Name TrustedSigning will fail, so we'll get the logs at that point
+ builder_util_1.log.debug({ message: error.message || error.stack }, "unable to install PackageProvider Nuget. Might be a false alarm though as some systems already have it installed");
+ }
+ await vm.exec(ps, ["-NoProfile", "-NonInteractive", "-Command", "Install-Module -Name TrustedSigning -RequiredVersion 0.4.1 -Force -Repository PSGallery -Scope CurrentUser"]);
// Preemptively check env vars once during initialization
// Options: https://learn.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential?view=azure-dotnet#definition
builder_util_1.log.info(null, "verifying env vars for authenticating to Microsoft Entra ID");
@@ -70,10 +77,12 @@ class WindowsSignAzureManager {
CertificateProfileName: certificateProfileName,
Files: options.path,
};
- const paramsString = Object.entries(params).reduce((res, [field, value]) => {
+ const paramsString = Object.entries(params)
+ .reduce((res, [field, value]) => {
return [...res, `-${field}`, value];
- }, []);
- await vm.exec(ps, ["Invoke-TrustedSigning", ...paramsString]);
+ }, [])
+ .join(" ");
+ await vm.exec(ps, ["-NoProfile", "-NonInteractive", "-Command", `Invoke-TrustedSigning ${paramsString}`]);
return true;
}
}
diff --git a/node_modules/app-builder-lib/out/codeSign/windowsSignToolManager.js b/node_modules/app-builder-lib/out/codeSign/windowsSignToolManager.js
index 54e5074..9773295 100644
--- a/node_modules/app-builder-lib/out/codeSign/windowsSignToolManager.js
+++ b/node_modules/app-builder-lib/out/codeSign/windowsSignToolManager.js
@@ -120,10 +120,35 @@ class WindowsSignToolManager {
else {
hashes = Array.isArray(hashes) ? hashes : [hashes];
}
- const cscInfo = await this.cscInfo.value;
const name = this.packager.appInfo.productName;
const site = await this.packager.appInfo.computePackageUrl();
const customSign = await (0, resolve_1.resolveFunction)(this.packager.appInfo.type, (0, platformPackager_1.chooseNotNull)((_b = options.options.signtoolOptions) === null || _b === void 0 ? void 0 : _b.sign, options.options.sign), "sign");
+ const cscInfo = await this.cscInfo.value;
+ if (cscInfo) {
+ let logInfo = {
+ file: builder_util_1.log.filePath(options.path),
+ };
+ if ("file" in cscInfo) {
+ logInfo = {
+ ...logInfo,
+ certificateFile: cscInfo.file,
+ };
+ }
+ else {
+ logInfo = {
+ ...logInfo,
+ subject: cscInfo.subject,
+ thumbprint: cscInfo.thumbprint,
+ store: cscInfo.store,
+ user: cscInfo.isLocalMachineStore ? "local machine" : "current user",
+ };
+ }
+ builder_util_1.log.info(logInfo, "signing");
+ }
+ else if (!customSign) {
+ builder_util_1.log.error({ signHook: customSign, cscInfo }, "no signing info identified, signing is skipped");
+ return false;
+ }
const executor = customSign || ((config, packager) => this.doSign(config, packager));
let isNest = false;
for (const hash of hashes) {
diff --git a/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js b/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js
index 5f59773..81368cc 100644
--- a/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js
+++ b/node_modules/app-builder-lib/out/targets/nsis/NsisTarget.js
@@ -347,7 +347,7 @@ class NsisTarget extends core_1.Target {
else {
await (0, wine_1.execWine)(installerPath, null, [], { env: { __COMPAT_LAYER: "RunAsInvoker" } });
}
- await packager.sign(uninstallerPath, "signing NSIS uninstaller");
+ await packager.sign(uninstallerPath);
delete defines.BUILD_UNINSTALLER;
// platform-specific path, not wine
defines.UNINSTALLER_OUT_FILE = uninstallerPath;
diff --git a/node_modules/app-builder-lib/out/winPackager.js b/node_modules/app-builder-lib/out/winPackager.js
index a7f21dc..8a0bc5c 100644
--- a/node_modules/app-builder-lib/out/winPackager.js
+++ b/node_modules/app-builder-lib/out/winPackager.js
@@ -96,47 +96,16 @@ class WinPackager extends platformPackager_1.PlatformPackager {
var _a;
return (0, platformPackager_1.chooseNotNull)((0, platformPackager_1.chooseNotNull)((0, platformPackager_1.chooseNotNull)((_a = this.platformSpecificBuildOptions.signtoolOptions) === null || _a === void 0 ? void 0 : _a.certificatePassword, this.platformSpecificBuildOptions.certificatePassword), process.env.WIN_CSC_KEY_PASSWORD), super.doGetCscPassword());
}
- async sign(file, logMessagePrefix) {
- var _a;
+ async sign(file) {
const signOptions = {
path: file,
options: this.platformSpecificBuildOptions,
};
- const cscInfo = await (await this.signtoolManager.value).cscInfo.value;
- if (cscInfo == null) {
- if ((0, platformPackager_1.chooseNotNull)((_a = this.platformSpecificBuildOptions.signtoolOptions) === null || _a === void 0 ? void 0 : _a.sign, this.platformSpecificBuildOptions.sign) != null) {
- return (0, windowsCodeSign_1.signWindows)(signOptions, this);
- }
- else if (this.forceCodeSigning) {
- throw new builder_util_1.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`);
- }
- return false;
+ const didSignSuccessfully = await this.doSign(signOptions);
+ if (!didSignSuccessfully && this.forceCodeSigning) {
+ throw new builder_util_1.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`);
}
- if (logMessagePrefix == null) {
- logMessagePrefix = "signing";
- }
- if ("file" in cscInfo) {
- builder_util_1.log.info({
- file: builder_util_1.log.filePath(file),
- certificateFile: cscInfo.file,
- }, logMessagePrefix);
- }
- else {
- const info = cscInfo;
- builder_util_1.log.info({
- file: builder_util_1.log.filePath(file),
- subject: info.subject,
- thumbprint: info.thumbprint,
- store: info.store,
- user: info.isLocalMachineStore ? "local machine" : "current user",
- }, logMessagePrefix);
- }
- return this.doSign({
- ...signOptions,
- options: {
- ...this.platformSpecificBuildOptions,
- },
- });
+ return didSignSuccessfully;
}
async doSign(options) {
return (0, builder_util_1.retry)(() => (0, windowsCodeSign_1.signWindows)(options, this), 3, 500, 500, 0, (e) => {
OK amazing, that is working perfectly now!
It should be noted that I needed to include the "FileDigest" option, so maybe have that one as mandatory in the config or provide a default value (I used fileDigest: 'SHA256'
)
Thanks for testing @MikeJerred ! Re: fileDigest
, is that an enum I can use in the schema (as opposed to generic string?
) or should we just hardcode it as default but overridable in the code. i.e.
const params = {
FileDigest: "SHA256",
...extraSigningArgs, // allows overriding FileDigest if provided in config
Endpoint: endpoint,
CertificateProfileName: certificateProfileName,
Files: options.path,
}
Thanks for testing @MikeJerred ! Re:
fileDigest
, is that an enum I can use in the schema (as opposed to genericstring?
) or should we just hardcode it as default but overridable in the code. i.e.const params = { FileDigest: "SHA256", ...extraSigningArgs, // allows overriding FileDigest if provided in config Endpoint: endpoint, CertificateProfileName: certificateProfileName, Files: options.path, }
Not sure what options you can put, or why someone would want to change it, so I would say put it as defaulted to SHA256 allowing overrides - as in your comment there.
Merged PR with fixes https://github.com/electron-userland/electron-builder/pull/8524
Deploying the release now. It'll be in 25.1.5
Already a step further in my case, it starts attempting signing now. But still get an error. Will also check/investigate why:
• exited command=app-builder.exe code=0 pid=10304
• signing with Azure Trusted Signing (beta) path=dist\win-unpacked\KLSTR.ctrl.exe
• executing file=powershell.exe args=-NoProfile -NonInteractive -Command Get-Command pwsh.exe
• unable to find pwsh.exe, falling back to powershell.exe
• installing required module (TrustedSigning) with scope CurrentUser
• executing file=powershell.exe args=-NoProfile -NonInteractive -Command Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
• executed file=powershell.exe stdout=
Name Version Source Summary
---- ------- ------ -------
nuget 2.8.5.208 https://onege... NuGet provider for the OneGet meta-package manager
• executing file=powershell.exe args=-NoProfile -NonInteractive -Command Install-Module -Name TrustedSigning -RequiredVersion 0.4.1 -Force -Repository PSGallery -Scope CurrentUser
• executed file=powershell.exe
• verifying env vars for authenticating to Microsoft Entra ID
• executing file=powershell.exe args=-NoProfile -NonInteractive -Command Get-Command pwsh.exe
• unable to find pwsh.exe, falling back to powershell.exe
• executing file=powershell.exe args=-NoProfile -NonInteractive -Command Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesigning.azure.net/ -CertificateProfileName <profileName> -Files C:\Users\bartel\git\klstr-ctrl\dist\win-unpacked\KLSTR.ctrl.exe
• Above command failed, retrying 3 more times
⨯ Exit code: 1. Command failed: powershell.exe -NoProfile -NonInteractive -Command Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesigning.azure.net/ -CertificateProfileName <profileName> -Files C:\Users\bartel\git\klstr-ctrl\dist\win-unpacked\KLSTR.ctrl.exe
Invoke-TrustedSigning : The 'Invoke-TrustedSigning' command was found in the module 'TrustedSigning', but the module could not be loaded. For more information, run 'Import-Module
TrustedSigning'.
At line:1 char:1
+ Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesi ...
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Invoke-TrustedSigning:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
Invoke-TrustedSigning : The 'Invoke-TrustedSigning' command was found in the module 'TrustedSigning', but the module could not be loaded. For more information, run 'Import-Module
TrustedSigning'.
At line:1 char:1
+ Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesi ...
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Invoke-TrustedSigning:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
failedTask=build stackTrace=Error: Exit code: 1. Command failed: powershell.exe -NoProfile -NonInteractive -Command Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesigning.azure.net/ -CertificateProfileName <profileName> -Files C:\Users\bartel\git\klstr-ctrl\dist\win-unpacked\KLSTR.ctrl.exe
Invoke-TrustedSigning : The 'Invoke-TrustedSigning' command was found in the module 'TrustedSigning', but the module could not be loaded. For more information, run 'Import-Module
TrustedSigning'.
At line:1 char:1
+ Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesi ...
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Invoke-TrustedSigning:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
Invoke-TrustedSigning : The 'Invoke-TrustedSigning' command was found in the module 'TrustedSigning', but the module could not be loaded. For more information, run 'Import-Module
TrustedSigning'.
At line:1 char:1
+ Invoke-TrustedSigning -FileDigest SHA256 -Endpoint https://weu.codesi ...
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Invoke-TrustedSigning:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
at C:\Users\bartel\git\klstr-ctrl\node_modules\builder-util\src\util.ts:138:18
at ChildProcess.exithandler (node:child_process:429:5)
at ChildProcess.emit (node:events:519:28)
at maybeClose (node:internal/child_process:1104:16)
at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5)
From previous event:
at processImmediate (node:internal/timers:491:21)
From previous event:
at WinPackager.signApp (C:\Users\bartel\git\klstr-ctrl\node_modules\app-builder-lib\src\winPackager.ts:270:27)
at WinPackager.doSignAfterPack (C:\Users\bartel\git\klstr-ctrl\node_modules\app-builder-lib\src\platformPackager.ts:346:32)
at WinPackager.doPack (C:\Users\bartel\git\klstr-ctrl\node_modules\app-builder-lib\src\platformPackager.ts:331:7)
at WinPackager.pack (C:\Users\bartel\git\klstr-ctrl\node_modules\app-builder-lib\src\platformPackager.ts:138:5)
at Packager.doBuild (C:\Users\bartel\git\klstr-ctrl\node_modules\app-builder-lib\src\packager.ts:459:9)
at executeFinally (C:\Users\bartel\git\klstr-ctrl\node_modules\builder-util\src\promise.ts:12:14)
at Packager.build (C:\Users\bartel\git\klstr-ctrl\node_modules\app-builder-lib\src\packager.ts:393:31)
at executeFinally (C:\Users\bartel\git\klstr-ctrl\node_modules\builder-util\src\promise.ts:12:14)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Must note I am trying this on a Windows on ARM (on a Mac with Parallels). So could be it influences some things...
Update:
C:\Users\bartel\git\klstr-ctrl>powershell.exe Import-Module TrustedSigning
Import-Module : File \\Mac\Home\Documents\WindowsPowerShell\Modules\TrustedSigning\0.4.1\FileFormat\FileFormat.psm1 cannot be loaded because
running scripts is disabled on this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ Import-Module TrustedSigning
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [Import-Module], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess,Microsoft.PowerShell.Commands.ImportModuleCommand
Powershell environment:
PS C:\Users\bartel\git\klstr-ctrl> powershell.exe Get-Host
Name : ConsoleHost
Version : 5.1.22621.2506
PS C:\Users\bartel\git\klstr-ctrl> powershell.exe Get-ExecutionPolicy
Restricted
Update2:
A step further with changing the execution policy, but is this the end-user's responsibility? If yes, I think it it's best to mention it in the docs:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
But then I get the message:
Invoke-TrustedSigning : Cannot process command because of one or more missing mandatory parameters: CodeSigningAccountName.
So I will set the configuration entry codeSigningAccountName
as well, but if it's required, I think it's best to add it in the type interface as well?
Update3: After adding codeSigningAccountName (thus which is required), I get successfully to the signtool.exe execution. But currently fails due to using Windows ARM... I get an exit code 3. Most likely because https://www.nuget.org/packages/Microsoft.Trusted.Signing.Client does not exist for ARM... Will retest on a non-ARM machine.
Also, might be very useful to add this resource to the documentation for Azure, as it is gold: https://melatonin.dev/blog/code-signing-on-windows-with-azure-trusted-signing/
@Bartel-C8 thank you for the deep debugging and report! Is this on local PC or a GH runner (or other platform)? I ask because my unit tests are not able to replicate the initialization setup error you're receiving on GH runner or on my local Parallels VM. (That being said, Parallels VM only needs to be set up once so there is the chance it already had the correct setup, but I don't recall ever setting ExecutionPolicy
myself.)
Re: CodeSigningAccountName
. Looks like it indeed is required per https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations#create-a-json-file. I recall seeing elsewhere that it wasn't present in a sample config json, but I can't find the resource atm. I'll get a PR set up for CodeSigningAccountName shortly. I couldn't test the final Invoke-TrustedSigning step specifically due to no free azure accounts for open-source projects, so thank you again for reporting back with your findings.
Released v25.1.6 with CodeSigningAccountName
property requirement.
I'm going to close this issue since Azure Trusted Signing is now supported in electron-builder per the OP request and this thread is super long.
If you encounter any issues from here, please open a new GH Issue and I'll be happy to take a look.
I'm inquiring about the possibility of integrating Azure Trusted Signing within the Electron Builder workflow. Currently, my build process involves:
If you could provide guidance on how to integrate Azure Trusted Signing more seamlessly with Electron Builder or if there are plans to support this in the future, it would be greatly appreciated.
Thank you for your time and assistance.