Add the Windows release-publishing path - #425
Merged
Merged
Conversation
coneilen
force-pushed
the
coneilen-microsoft-windows-release-publishing
branch
from
September 23, 2026 02:25
f216601 to
8636add
Compare
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
force-pushed
the
coneilen-microsoft-windows-release-publishing
branch
from
September 23, 2026 03:16
8636add to
a460d4b
Compare
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 ranpackage.ps1, and every release back throughv0.1.73carries 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-dmgbuilds/signs/notarizes locally andtap-bumps own comment says to run it "aftergh 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 betweenpackage.ps1and 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.yml—workflow_dispatchonly,publishandallow_unsigned_publishboth default false, pinned action SHAs, always uploads an inspectable workflow artifact.Tools/windows/stage-swift-products.ps1— stagesgraphcoded/graphcodeplus Swift runtime DLLs wherepackage.ps1looks for release inputs, reusingvalidate.ps1s toolchain resolution instead of duplicating it in YAML.Tools/windows/Tests/Release.Tests.ps1, wired intovalidate.ps1 -Task packaging.Signing honesty
Signed builds publish as
graphcode-windows-x86_64.zip(versionless, matching the macOS DMG soreleases/latest/download/resolves). Unsigned builds are named-unsignedand are refused publication without an explicit opt-in. Supplying only some signing secrets is a hard failure, never a silent downgrade. Secrets are documented inPACKAGING.md:WINDOWS_SIGNING_CERTIFICATE(base64 PFX),_PASSWORD,_THUMBPRINT(40 hex, validated bypackage.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 progressandRelaunch promptledger rows stay Blocked; their evidence now states precisely what this does and does not enable. No updater download/install path, noGraphCanvas.zig/Sidebar.zig, nothing undergraphcode-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.ps1beforerelease.ps1existed -> exit 1, "Windows release orchestrator is missing"; then five mutations ofrelease.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;publishdefault flipped to true; unpinnedactions/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 throughCert:\CurrentUser\Myand 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(...)"onwindows-release.yml-> parses withworkflow_dispatchas its only trigger and both publishing inputs defaulting to false.