electron-builder icon indicating copy to clipboard operation
electron-builder copied to clipboard

Support for Azure Trusted Signing in Electron Builder

Open iliakolev opened this issue 1 year ago • 9 comments

  • Electron-Builder Version: 24.13.3
  • Node Version: 18.16.0
  • Electron Version: 31.0.2
  • Electron Type (current, beta, nightly): electron-updater: 6.2.1
  • Target: Windows

I'm inquiring about the possibility of integrating Azure Trusted Signing within the Electron Builder workflow. Currently, my build process involves:

  1. Signing the executable using the azure/[email protected] GitHub Action.
  2. Packaging and releasing the signed executable, ensuring auto-update functionality with s3 provider.

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.

iliakolev avatar Jun 26 '24 09:06 iliakolev

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?

mmaietta avatar Jul 06 '24 00:07 mmaietta

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:

  1. on the executable files after they have been built
  2. on the installer executable after everything has been packaged in

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?

jmeinke avatar Jul 16 '24 11:07 jmeinke

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?

MikeJerred avatar Jul 18 '24 11:07 MikeJerred

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)
            }
        ))
    },
}

mmaietta avatar Jul 18 '24 21:07 mmaietta

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 avatar Jul 29 '24 15:07 MikeJerred

@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' });

jmeinke avatar Jul 31 '24 11:07 jmeinke

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.

OrganicChem avatar Aug 08 '24 22:08 OrganicChem

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.

jmeinke avatar Aug 12 '24 20:08 jmeinke

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.

OrganicChem avatar Aug 12 '24 21:08 OrganicChem

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.

mmaietta avatar Sep 09 '24 17:09 mmaietta

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 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.

MikeJerred avatar Sep 09 '24 17:09 MikeJerred

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

mmaietta avatar Sep 09 '24 20:09 mmaietta

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)

MikeJerred avatar Sep 11 '24 15:09 MikeJerred

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

mmaietta avatar Sep 12 '24 00:09 mmaietta

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

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:

MikeJerred avatar Sep 12 '24 07:09 MikeJerred

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/

mmaietta avatar Sep 12 '24 14:09 mmaietta

@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 🙃

mmaietta avatar Sep 13 '24 14:09 mmaietta

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!

Bartel-C8 avatar Sep 13 '24 15:09 Bartel-C8

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).

mmaietta avatar Sep 13 '24 15:09 mmaietta

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

mmaietta avatar Sep 13 '24 22:09 mmaietta

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

MikeJerred avatar Sep 14 '24 08:09 MikeJerred

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

mmaietta avatar Sep 14 '24 16:09 mmaietta

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: [email protected]
npm WARN Found: [email protected]
npm WARN node_modules/dmg-builder
npm WARN   dmg-builder@"^25" from [email protected]        
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 [email protected]
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: [email protected]
npm WARN Found: peer dmg-builder@"^25.1.025.1.0" from [email protected]
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 [email protected]
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: [email protected]
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 [email protected]
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 [email protected]
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: [email protected]
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 [email protected]
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.

MikeJerred avatar Sep 14 '24 17:09 MikeJerred

Kk. I've redeployed the monorepo to resync all the workspace versions. Please try 25.1.2

mmaietta avatar Sep 14 '24 22:09 mmaietta

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 avatar Sep 16 '24 12:09 MikeJerred

@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.

mmaietta avatar Sep 16 '24 15:09 mmaietta

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

MikeJerred avatar Sep 16 '24 16:09 MikeJerred

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

mmaietta avatar Sep 17 '24 17:09 mmaietta

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?

MikeJerred avatar Sep 18 '24 14:09 MikeJerred

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.

mmaietta avatar Sep 18 '24 15:09 mmaietta