Skip to content

Windows distribution matrix: ARM64, MSI, and signing prep#635

Draft
cwillisf wants to merge 8 commits into
developfrom
feature/windows-distribution-matrix
Draft

Windows distribution matrix: ARM64, MSI, and signing prep#635
cwillisf wants to merge 8 commits into
developfrom
feature/windows-distribution-matrix

Conversation

@cwillisf
Copy link
Copy Markdown
Contributor

Summary

Expands the Windows shipping matrix to seven artifacts across three formats and three architectures.

  • NSIS direct download: gains x64 and ARM64 (was previously ia32-only)
  • MSI: new target for managed deployment via Intune, SCCM, or Group Policy; x64
  • Store AppX: gains ARM64 alongside existing ia32 and x64

Full grid and rationale in docs/windows-build-matrix.md.

Draft

Two follow-up commits land before this is ready to merge:

  1. Azure Artifact Signing configuration (win.azureSignOptions in electron-builder.yaml)
  2. OIDC handshake step in release-candidate.yml

Both arrive once the underlying Azure tenant setup completes. The signing path is keyed on environment variables that aren't present in PR builds, so this PR's CI is exercising the unsigned matrix end-to-end.

Test plan

  • CI green on all platforms
  • Seven Windows artifacts produced (NSIS x ia32+x64+arm64, MSI x64, AppX x ia32+x64+arm64)
  • Manual install on Windows x64 and Windows-on-ARM
  • MSI silent install (msiexec /i Scratch-*.msi /quiet) succeeds; uninstall clean

@cwillisf cwillisf force-pushed the feature/windows-distribution-matrix branch 3 times, most recently from 161ec67 to 2b04fdd Compare May 29, 2026 22:08
cwillisf added 5 commits June 1, 2026 08:04
scratch-desktop is open source, but several identifiers in
electron-builder.yaml are organization-specific: a fork that
builds without changing them would publish artifacts that collide
with ours (same Store identityName, same publisher CN).

Move the AppX identifiers to environment variables sourced from
GitHub repository Variables. Both workflows export them at job
level so electron-builder's \${env.X} interpolation finds them
during build. Forks that don't set the variables get a build
failure rather than a silent collision with our identifiers.

Variables introduced:
- APPX_IDENTITY_NAME
- APPX_PUBLISHER
- APPX_PUBLISHER_DISPLAY_NAME
Windows now ships across three formats and three architectures:
- NSIS direct download: x64, arm64, and existing ia32
- MSI for managed deployment: x64 only
- Microsoft Store AppX: new arm64 alongside existing ia32 and x64

The wrapper organizes the Windows targets into two electron-builder
passes: AppX in one (intentionally unsigned, since the Microsoft
Store re-signs at certification), and NSIS+MSI together in a single
signed pass via the new windowsInstallers entry. The release-
candidate matrix mirrors this with two Windows runners.

NSIS artifactName now includes \${arch} so the three multi-arch
builds don't collide on the same filename. The ia32 NSIS filename
changes from "Scratch X.Y.Z Setup.exe" to "Scratch X.Y.Z ia32
Setup.exe"; the scratch-www download-page link will need updating.

MSI upgradeCode sources from a repository Variable (MSI_UPGRADE_CODE)
so a fork generates and uses its own GUID rather than inheriting
ours. Future versions upgrade in place when the GUID stays stable.
Initial arch scope is x64 only; ARM64 institutional deployments use
the Store AppX via Intune, avoiding the WiX 4 ARM64 limitation
electron-builder currently carries.
Captures the 7-artifact shipping matrix (NSIS x ia32+x64+arm64,
MSI x64, AppX x ia32+x64+arm64), the rationale for the empty cells
(no ARM64 or ia32 MSI), how the wrapper organizes Windows into two
electron-builder passes, and a short guide for identifying which
build a user has when triaging reports. Linked from README.
The previous release-candidate uploads bundled multiple installers
into single artifacts via a generic upload step driven by matrix
variables. Splitting per-artifact lets consumers grab just the cell
they care about instead of pulling a multi-installer zip.

Also sets compression-level: 0 on every upload. The artifact files
(.appx, .exe, .msi, .dmg, .pkg, .zip) are all already-compressed
containers; re-gzipping costs CPU for ~0% size reduction.
The PR-time CI workflow previously built the full Windows installer
matrix (NSIS x3 + MSI + AppX x3) on every push. That's a lot of
billed minutes for installer packaging that rarely changes.

Now each platform builds one representative installer that non-
developer teammates can grab and try from any PR: NSIS x64 on
Windows (via a new 'nsis-x64' wrapper shortname) and DMG on macOS.
Both platforms specify their target explicitly so the wrapper's
default behavior isn't load-bearing.

The full multi-arch matrix still runs on release-candidate dispatch
where the artifacts actually ship.

Estimated savings: Windows CI job drops from ~11 min to ~4 min, with
proportional reductions in billed compute minutes.
@cwillisf cwillisf force-pushed the feature/windows-distribution-matrix branch 3 times, most recently from e8d2bbf to 31cceda Compare June 1, 2026 18:49
@cwillisf cwillisf had a problem deploying to release-candidate June 1, 2026 19:03 — with GitHub Actions Failure
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 19:03 — with GitHub Actions Inactive
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 19:03 — with GitHub Actions Inactive
@cwillisf cwillisf had a problem deploying to release-candidate June 1, 2026 19:03 — with GitHub Actions Failure
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 19:03 — with GitHub Actions Inactive
Sign EXE and MSI via electron-builder's win.azureSignOptions, authorized by
OIDC federated credentials between GitHub Actions and Azure. The federated
credential is scoped to the release-candidate workflow's environment, so
the human-gated environment approval and the Azure auth are layered.

AppX is excluded via signExts: the Microsoft Store re-signs during
certification with a Store-issued publisher cert, and matching its CN at
build time isn't feasible.

The wrapper appends --c.win.signExecutable=false when --mode=dev, so PR-time
CI and local dev builds skip signing without needing Azure auth.

Removes the legacy WIN_CSC_LINK / WIN_CSC_KEY_PASSWORD flow.
@cwillisf cwillisf force-pushed the feature/windows-distribution-matrix branch from 31cceda to 8d7a65c Compare June 1, 2026 19:34
workflow_dispatch runs don't get a commit message in the GHA expression
context, so by default the run list shows only the workflow name. Setting
run-name to branch + SHA gives each manual dispatch a distinct, scannable
title without requiring the dispatcher to fill in an input.
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 19:39 — with GitHub Actions Inactive
@cwillisf cwillisf had a problem deploying to release-candidate June 1, 2026 19:39 — with GitHub Actions Failure
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 19:39 — with GitHub Actions Inactive
@cwillisf cwillisf had a problem deploying to release-candidate June 1, 2026 19:39 — with GitHub Actions Failure
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 19:39 — with GitHub Actions Inactive
Invoke-TrustedSigning checks AZURE_TENANT_ID directly in the process env
before delegating to Azure.Identity, so azure/login alone is not enough —
the action configures the az CLI but does not export those env vars itself.

Surfacing AZURE_TENANT_ID and AZURE_CLIENT_ID at job level satisfies the
pre-flight check. The DefaultAzureCredential chain then steps through
EnvironmentCredential (fails: no AZURE_CLIENT_SECRET) and falls through to
AzureCliCredential, which uses the az session that azure/login set up. No
long-lived client secret is involved.
@cwillisf cwillisf had a problem deploying to release-candidate June 1, 2026 21:19 — with GitHub Actions Failure
@cwillisf cwillisf deployed to release-candidate June 1, 2026 21:19 — with GitHub Actions Active
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 21:19 — with GitHub Actions Inactive
@cwillisf cwillisf had a problem deploying to release-candidate June 1, 2026 21:19 — with GitHub Actions Failure
@cwillisf cwillisf temporarily deployed to release-candidate June 1, 2026 21:19 — with GitHub Actions Inactive
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.

1 participant