Skip to content

Add optional Artifact Signing for Windows builds#2995

Open
mokagio wants to merge 11 commits intotrunkfrom
ainfra-2233-integrate-artifacts-signing-in-ci-for-studio-windows
Open

Add optional Artifact Signing for Windows builds#2995
mokagio wants to merge 11 commits intotrunkfrom
ainfra-2233-integrate-artifacts-signing-in-ci-for-studio-windows

Conversation

@mokagio
Copy link
Copy Markdown
Contributor

@mokagio mokagio commented Apr 7, 2026

Related issues

How AI was used in this PR

Implementation planned and written with Claude Code (Opus 4.6).
All code reviewed by @mokagio before committing.

Proposed Changes

Takes the work from #2709 and puts it behind a toggle env var.
The idea being that we can switch back to the previous signing implementation if something turns out to be wrong once released in the wild.

  • Add Azure Trusted Signing utilities (azure-signing.cjs, azure-sign-hook.js) for SHA256-only signing via signtool
  • Add windowsSign.ts module gated on USE_AZURE_TRUSTED_SIGNING env var — returns Azure hook config when set, undefined (PFX fallback) otherwise
  • Wire windowsSign into forge.config.ts for both packagerConfig and MakerSquirrel
  • Add conditional signing setup in build-for-windows.ps1 (Azure or PFX based on toggle)
  • Add conditional signing in package-appx.mjs for sideload AppX
  • Update CI toolkit version to include setup_azure_trusted_signing.ps1
  • Set USE_AZURE_TRUSTED_SIGNING=1 in both dev and release Windows build jobs in the pipeline

To revert to PFX signing, remove the USE_AZURE_TRUSTED_SIGNING env var from the pipeline YAML files.

Testing Instructions

I created #2996 to test the changes here and verify that both build paths work.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors? - N.A.

Posted by Claude Code (Opus 4.6) on behalf of @mokagio with approval.

@mokagio mokagio self-assigned this Apr 7, 2026
@wpmobilebot
Copy link
Copy Markdown
Collaborator

wpmobilebot commented Apr 7, 2026

📊 Performance Test Results

Comparing 6b940af vs trunk

app-size

Metric trunk 6b940af Diff Change
App Size (Mac) 1252.23 MB 1252.23 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 6b940af Diff Change
load 1724 ms 1879 ms +155 ms 🔴 9.0%

site-startup

Metric trunk 6b940af Diff Change
siteCreation 8175 ms 8178 ms +3 ms ⚪ 0.0%
siteStartup 4840 ms 4837 ms 3 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@mokagio mokagio changed the title Add Azure Trusted Signing toggle for Windows Add optional Artifact Signing for Windows builds Apr 7, 2026
# The ~> modifier is not currently used, but we check for it just in case
XCODE_VERSION=$(sed -E -n 's/^(~> )?(.*)/xcode-\2/p' .xcode-version)
CI_TOOLKIT_VERSION='6.0.1'
CI_TOOLKIT_VERSION='4411dd924c08dea251702db5760e741c9d81eff2'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting for confirmation on the tooling from a few more clients before shipping a new version of the plugin. A commit sha doesn't look as neat as a tag, but the pinning result is the same.

@mokagio
Copy link
Copy Markdown
Contributor Author

mokagio commented Apr 7, 2026

Mmm... I just noticed that the signed artifact in CI is called unsigned, which is confusing

image

Reverting to draft to address.

@mokagio mokagio marked this pull request as draft April 7, 2026 07:50
mokagio and others added 10 commits April 8, 2026 10:52
Shared config and signing hook for Azure Trusted Signing,
ported from the `test-artifact-signing` branch.

`azure-signing.cjs` builds signtool args and validates env vars.
`azure-sign-hook.js` is the custom `@electron/windows-sign` hook
that calls signtool with SHA256-only params (Azure doesn't
support the default SHA1+SHA256 dual-sign).

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Returns Azure signing hook config when `USE_AZURE_TRUSTED_SIGNING`
is set, `undefined` otherwise so Forge falls back to PFX certs.
Throws if the toggle is on but Azure env vars are missing.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Adds `windowsSign` to `packagerConfig` and `MakerSquirrel`.
When defined (Azure mode), uses the signing hook.
When undefined (PFX mode), uses `certificateFile`/`certificatePassword`.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Conditionally calls `setup_azure_trusted_signing.ps1` or
`setup_windows_code_signing.ps1` based on
`USE_AZURE_TRUSTED_SIGNING`.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
When `USE_AZURE_TRUSTED_SIGNING` is set, builds an unsigned
sideload AppX then signs it with Azure signtool.
Otherwise uses the existing PFX certificate path.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Points to the `add-azure-trusted-signing` branch which
includes `setup_azure_trusted_signing.ps1`.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
The `add-azure-trusted-signing` branch no longer exists.
Use the commit SHA directly.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Gio Lodi <giovanni.lodi42@gmail.com>
Set `USE_AZURE_TRUSTED_SIGNING=1` in both dev and release
Windows build jobs so Azure signing is active by default.
The toggle in `build-for-windows.ps1` still allows falling
back to PFX by unsetting the env var.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
`electron2appx` names the file "unsigned" when `devCert: 'nil'` is passed.
The Azure signing path builds unsigned then signs externally, so the file kept the misleading name.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
@mokagio mokagio force-pushed the ainfra-2233-integrate-artifacts-signing-in-ci-for-studio-windows branch from f1360a5 to 82cf136 Compare April 8, 2026 00:52
const renamedPath = path.join( appxOutputPathSigned, renamedFile );
await fs.rename( appxPath, renamedPath );
console.log( `Renamed to ${ renamedFile }` );
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

Given the signed AppX is not used by any of our distribution automation, no other update was needed.

Comment on lines +100 to +109
// Azure mode: use the custom signing hook that calls signtool
// with Azure Trusted Signing parameters.
// PFX mode: use the local certificate file and password.
...( windowsSign
? { windowsSign }
: {
certificateFile: path.join( repoRoot, 'certificate.pfx' ),
certificatePassword: process.env.WINDOWS_CODE_SIGNING_CERT_PASSWORD,
}
),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we'll have established that the new code signing is good, we'll be able to merge the checks done inside windowsSign.ts in this file, before defining this ForgeConfig instance.

As it stands, this code looks a bit ugly, but it's a temporary compromise.

@mokagio mokagio requested review from p-jackson and wojtekn April 8, 2026 03:32
@mokagio mokagio marked this pull request as ready for review April 8, 2026 03:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an opt-in Azure Trusted Signing path for Windows artifacts (Forge packaging, Squirrel installer, and sideload AppX), while retaining the existing PFX-based signing as a fallback controlled by USE_AZURE_TRUSTED_SIGNING.

Changes:

  • Introduces Azure Trusted Signing helpers (azure-signing.cjs, azure-sign-hook.js) and a windowsSign Forge config helper gated by USE_AZURE_TRUSTED_SIGNING.
  • Updates Windows AppX packaging to support Azure signing for the sideload (local testing) AppX path.
  • Updates Buildkite pipelines and Windows build script to set up Azure signing when the toggle is enabled and bumps CI toolkit ref.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
scripts/package-appx.mjs Adds Azure-vs-PFX branching for env validation and sideload AppX signing via signtool.
scripts/azure-signing.cjs Adds shared Azure signing config/env validation and signtool arg builder.
scripts/azure-sign-hook.js Adds a custom @electron/windows-sign hook to do SHA256-only signing via Azure.
apps/studio/windowsSign.ts Adds an env-gated Forge windowsSign hook configuration for Azure signing.
apps/studio/forge.config.ts Wires windowsSign into packager config and conditionally into MakerSquirrel config.
.buildkite/shared-pipeline-vars Updates CI toolkit ref to include Azure signing setup script.
.buildkite/release-build-and-distribute.yml Enables Azure signing for Windows release builds via USE_AZURE_TRUSTED_SIGNING=1.
.buildkite/pipeline.yml Enables Azure signing for Windows dev builds via USE_AZURE_TRUSTED_SIGNING=1.
.buildkite/commands/build-for-windows.ps1 Conditionally runs Azure or PFX signing setup based on USE_AZURE_TRUSTED_SIGNING.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +22
if ( ! process.env.AZURE_CODE_SIGNING_DLIB || ! process.env.AZURE_METADATA_JSON ) {
throw new Error(
'USE_AZURE_TRUSTED_SIGNING is set but Azure signing env vars ' +
'(AZURE_CODE_SIGNING_DLIB, AZURE_METADATA_JSON) are missing. ' +
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getWindowsSign() validates AZURE_CODE_SIGNING_DLIB and AZURE_METADATA_JSON but not SIGNTOOL_PATH, even though the Azure signing hook requires it. This can lead to a later failure during signing rather than failing fast when loading Forge config; consider validating SIGNTOOL_PATH here and including it in the error message to keep the required env var list consistent.

Suggested change
if ( ! process.env.AZURE_CODE_SIGNING_DLIB || ! process.env.AZURE_METADATA_JSON ) {
throw new Error(
'USE_AZURE_TRUSTED_SIGNING is set but Azure signing env vars ' +
'(AZURE_CODE_SIGNING_DLIB, AZURE_METADATA_JSON) are missing. ' +
if ( ! process.env.AZURE_CODE_SIGNING_DLIB || ! process.env.AZURE_METADATA_JSON || ! process.env.SIGNTOOL_PATH ) {
throw new Error(
'USE_AZURE_TRUSTED_SIGNING is set but Azure signing env vars ' +
'(AZURE_CODE_SIGNING_DLIB, AZURE_METADATA_JSON, SIGNTOOL_PATH) are missing. ' +

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@mokagio
Copy link
Copy Markdown
Contributor Author

mokagio commented Apr 8, 2026

@p-jackson @wojtekn can I ask you to take the artifacts from the Windows test build, in particular the Artifacts Signing ones for a spin to verify they work as expected?

Thanks!

@mokagio mokagio enabled auto-merge (squash) April 8, 2026 03:38
@wojtekn
Copy link
Copy Markdown
Contributor

wojtekn commented Apr 8, 2026

@mokagio both Azure and PFX build artifacts install fine, and show as properly signed on my test machine.

I noticed one difference, though - the PFX doesn't show dates.

Azure PFX
Zrzut ekranu 2026-04-08 161104 Zrzut ekranu 2026-04-08 160942

@mokagio
Copy link
Copy Markdown
Contributor Author

mokagio commented Apr 8, 2026

Thanks @wojtekn !

I don't know why the PFX doesn't show the date in the signature. Guess: Artifact Signing requires a more precise system.

Anyway, given Artifacts Signing is the way we want to go forward, I think it's okay to leave the question unanswered for the PFX in the interest of moving ahead.

@p-jackson
Copy link
Copy Markdown
Member

@mokagio I saw the same things as Wojtek, but it makes sense that if we have to move forward with PFX then the dates can be figured out later.
The installer worked as expected, updating Studio without any warnings about the certificate.

The usual stuff (installing, installing on top of existing installation, uninstalling) all work as expected.

@mokagio
Copy link
Copy Markdown
Contributor Author

mokagio commented Apr 9, 2026

Thanks @p-jackson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants