Skip to content

Concurrency-bug when signing in GitHub Action #8615

Closed
@OlaAlsaker

Description

@OlaAlsaker
  • Electron-Builder Version: 26.0.0-alpha.3
  • Node Version: v20
  • Electron Version: ^28.0.0
  • Electron Type (current, beta, nightly):
  • Target: Windows

In my use-case, I have an Electron app with multiple .exe-resources, and I want to sign my app with Azure Trusted Signing inside of a GitHub Action.

This does however lead to an error:

Install-Package: Package 'Microsoft.Trusted.Signing.Client' failed to be installed because: The process cannot access the file
'C:\Users\runneradmin\AppData\Local\TrustedSigning\Microsoft.Trusted.Signing.Client\Microsoft.Trusted.Signing.Client.1.0
.53\bin\x64\Azure.CodeSigning.Dlib.dll' because it is being used by another process.

The reason for this is pretty simple: electron-builder signs multiple files at the same time by default. Since we are running this inside of a GitHub Action, we need to install the TrustedSigning module each time we run the Action.

await vm.exec(ps, ["-NoProfile", "-NonInteractive", "-Command", "Install-Module -Name TrustedSigning -RequiredVersion 0.4.1 -Force -Repository PSGallery -Scope CurrentUser"])

But since this installation-code is run on every sign (with Azure Trusted Signing), it means that it will run once for every file (in my case, 4 files) at the same time, meaning we will try to install the module 4 times at once. NuGet does not like this, and therefore throws an exception, making all the signing-processes fail.

Solution

As a test, I tried to monkey-patch these parts of app-builder-lib:

await BluebirdPromise.map(readdir(packContext.appOutDir), (file: string): any => {
if (file === exeFileName) {
return this.signAndEditResources(
path.join(packContext.appOutDir, exeFileName),
packContext.arch,
packContext.outDir,
path.basename(exeFileName, ".exe"),
this.platformSpecificBuildOptions.requestedExecutionLevel
)
} else if (this.shouldSignFile(file)) {
return this.sign(path.join(packContext.appOutDir, file))
}
return null
})

const filesPromise = (filepath: string[]) => {
const outDir = path.join(packContext.appOutDir, ...filepath)
return walk(outDir, (file, stat) => stat.isDirectory() || this.shouldSignFile(file))
}
const filesToSign = await Promise.all([filesPromise(["resources", "app.asar.unpacked"]), filesPromise(["swiftshader"])])
await BluebirdPromise.map(filesToSign.flat(1), file => this.sign(file), { concurrency: 4 })

I changed the code and made it await every this.sign – and that solved all my problems! 😄

NOTE! I also tried to change concurrency to 1, but that did not work.

I suppose there are multiple solutions to this, and I don't know which is best really.

Possible solutions

  1. Add an option in configuration to disable concurrency in signing and implement this behavior
  2. In Azure Trusted Signing, make sure that the initializeProviderModules is only run one time before the signing-processes start
  3. Disable concurrency in signing by default

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions