Skip to content

Add the Windows release-publishing path - #425

Merged
coneilen merged 1 commit into
mainfrom
coneilen-microsoft-windows-release-publishing
Sep 23, 2026
Merged

coneilen merged 1 commit into
mainfrom
coneilen-microsoft-windows-release-publishing

Conversation

@coneilen

Copy link
Copy Markdown
Collaborator

Windows packaging and Authenticode signing were already implemented and tested (Tools/windows/package.ps1, Packaging.Signing.Tests.ps1, Packaging.ScriptSigning.Tests.ps1). What was missing is release plumbing: nothing in CI ever ran package.ps1, and every release back through v0.1.73 carries exactly one asset, graphcode-macos-arm64.dmg. This adds the publishing path, without a certificate.

How macOS actually publishes

Manually, from a maintainer Mac. make release-dmg builds/signs/notarizes locally and tap-bumps own comment says to run it "after gh release create, never before". There is no macOS release workflow to mirror, so the Windows path is deliberately maintainer-triggered too rather than firing on tags.

What this adds

  • Tools/windows/release.ps1 — the one decision point between package.ps1 and a release asset: tag to version, import signing material only when supplied in full, pin the imported certificate to the declared thumbprint, re-verify the package against the expected publisher, and refuse to continue when the built package metadata.json contradicts what the run did.
  • .github/workflows/windows-release.ymlworkflow_dispatch only, publish and allow_unsigned_publish both default false, pinned action SHAs, always uploads an inspectable workflow artifact.
  • Tools/windows/stage-swift-products.ps1 — stages graphcoded/graphcode plus Swift runtime DLLs where package.ps1 looks for release inputs, reusing validate.ps1s toolchain resolution instead of duplicating it in YAML.
  • Tools/windows/Tests/Release.Tests.ps1, wired into validate.ps1 -Task packaging.

Signing honesty

Signed builds publish as graphcode-windows-x86_64.zip (versionless, matching the macOS DMG so releases/latest/download/ resolves). Unsigned builds are named -unsigned and are refused publication without an explicit opt-in. Supplying only some signing secrets is a hard failure, never a silent downgrade. Secrets are documented in PACKAGING.md: WINDOWS_SIGNING_CERTIFICATE (base64 PFX), _PASSWORD, _THUMBPRINT (40 hex, validated by package.ps1), _TIMESTAMP_URL. A base64 PFX is required because Windows CI runs on ephemeral GitHub-hosted runners, which have no certificate store to pre-provision. No certificate or secret is included here.

Scope

Prerequisite only. The Install progress and Relaunch prompt ledger rows stay Blocked; their evidence now states precisely what this does and does not enable. No updater download/install path, no GraphCanvas.zig/Sidebar.zig, nothing under graphcode-windows/src/.

The workflow itself can only be proven by a maintainer dispatch, since it needs the hosted runner to complete a full pinned build.

Evidence

RED: pwsh Tools/windows/Tests/Release.Tests.ps1 before release.ps1 existed -> exit 1, "Windows release orchestrator is missing"; then five mutations of release.ps1 (unsigned asset renamed to the signed name; unsigned-publish gate bypassed; thumbprint pin bypassed; signing-state honesty check bypassed; publisher pin dropped from Verify) -> all five exit 1 CAUGHT; four mutations of the workflow (added an automatic published-release trigger; publish default flipped to true; unpinned actions/checkout; dropped a signing secret from the step env) -> all four exit 1 CAUGHT.

GREEN: pwsh Tools/windows/Tests/Release.Tests.ps1 -> exit 0, all eight contracts PASS (tag resolution, unsigned labeling, unsigned publication gate, signing-material completeness and thumbprint pinning, signed publication and certificate cleanup, signing-state honesty, build/upload failure propagation, workflow contract); the signed path round-trips a real ephemeral self-signed PFX through Cert:\CurrentUser\My and leaves 0 certificates behind.

REGRESSION: pwsh Tools/windows/Tests/ValidationRunner.Tests.ps1 -> PASS (required-PR-check triggers unaffected by the new dispatch-only workflow); pwsh Tools/windows/validate.ps1 -Task privacy -> "Privacy checks passed"; python -c "yaml.safe_load(...)" on windows-release.yml -> parses with workflow_dispatch as its only trigger and both publishing inputs defaulting to false.

@coneilen
coneilen force-pushed the coneilen-microsoft-windows-release-publishing branch from f216601 to 8636add Compare September 23, 2026 02:25
Windows packaging and Authenticode signing were already implemented and
tested, but nothing ever ran them: no workflow referenced package.ps1, and
every published release carries only graphcode-macos-arm64.dmg. The gap was
release plumbing, not cryptography, and it can be built without a certificate.

Tools/windows/release.ps1 is the single decision point between package.ps1 and
a release asset. It resolves a release tag to a package version, imports
signing material only when it is supplied in full, pins the imported
certificate to the declared thumbprint, re-verifies the built package against
the expected publisher, and refuses to continue when the package's own
metadata.json contradicts what the run actually did. Signed builds publish as
graphcode-windows-x86_64.zip, mirroring the versionless macOS DMG; unsigned
builds are named -unsigned and cannot be attached to a release without an
explicit opt-in. Partial signing secrets fail the run instead of silently
downgrading to unsigned.

windows-release.yml is workflow_dispatch-only, matching the maintainer-driven
macOS process (make release-dmg plus a manual gh release create); it never
fires on a tag push or a published release, defaults publish to false, and
always uploads the build as an inspectable workflow artifact.

This is a prerequisite only. No certificate is provisioned, so the workflow
currently produces a clearly labeled unsigned development artifact, and no
in-app updater download/install path is added. The Install progress and
Relaunch prompt ledger rows stay Blocked.

Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@coneilen
coneilen force-pushed the coneilen-microsoft-windows-release-publishing branch from 8636add to a460d4b Compare September 23, 2026 03:16
@coneilen
coneilen merged commit 414a84e into main Sep 23, 2026
10 checks passed
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