The SignPath Foundation production certificate does not sign on demand
the way the self-signed test certificate does. Every request has to be
approved by hand in the SignPath UI, because the Foundation verifies
what is being signed and which build produced it. The submitting action
waits for that approval with a default timeout of 600 seconds, which is
ample while the test policy approves in seconds and far too short once
the wait is a person noticing a tag went out. A tag pushed at night
would have failed the run ten minutes later with the installer already
compiled and thrown away.
The compile now ends in its own job, which uploads the unsigned artifact
and exposes its id. A second job downloads it, signs it, and does the
release-facing work, with the wait raised to an hour and the job timeout
sized to sit outside it. Separating them is what buys the recovery: the
artifact is uploaded before the wait begins and is addressed by id, so a
missed approval window costs a re-run of the second job alone rather
than a rebuild. Raising the timeout in place would not have given that.
The second job runs for unsigned builds too. Daily prereleases are
deliberately left unsigned to preserve the signing quota, and gating the
whole job on the signing decision would have meant a second copy of the
alias, artifact and release steps for them to run through.
The decision itself moves into a named step that echoes it, so a tag
that came out unsigned can be explained from the run log rather than by
re-reading the expression. It is one source of truth feeding both jobs,
which a job-level env could not be.
Every step body is otherwise unchanged. The property worth keeping is
that none of the alias, upload and release-attach steps carry always(),
so GitHub skips all three when signing fails or times out and an
unsigned .exe cannot reach a release; that is now written next to them,
because it is easy to break by adding a condition without noticing.
The policy slug stays at test-signing and the signature check stays
lenient -- the test certificate is self-signed and reports UnknownError,
so requiring Valid would fail every run until the production certificate
is imported. Both are the cutover. The restructure behaves identically
under the test policy, the request simply completing at once instead of
waiting, so it can be proven green beforehand.
Brings the Windows installer work from dev to main without merging
the rest of the 0.2.5b1 release content. Squashes 12 commits from
dev (8711c54e..7bb11df2) into a single net-effect commit on main.
Includes:
- installers/windows/ — Inno Setup .iss script, build.py, vendored
NSSM 2.24, bambuddy.ico (multi-resolution app icon), service
install/uninstall .bat files, build pipeline README
- backend/app/services/network_utils.py — Windows psutil branch so
the VP bind-IP dropdown enumerates interfaces; Linux/macOS path
unchanged
- .github/workflows/windows-installer.yml — reconciles main's
kludge-pushed copy with dev's accumulated changes (NSSM
vendoring, version-from-tag, unversioned alias step, etc.)
CHANGELOG and README entries for the Windows installer stay on
dev — they reference unreleased 0.2.5b1 release notes that aren't
on main yet.
Address CodeQL actions/missing-workflow-permissions finding. Least-
privilege at workflow level: contents: write is required by
softprops/action-gh-release to attach the installer .exe to a tag
release; all other steps are read-only.
windows-latest runners ship Inno Setup 6.7.1 pre-installed under the
same path we already hardcode for ISCC.exe; the choco install was trying
to downgrade to 6.2.2 and failing on the version mismatch.