ci: sign Windows builds with Azure Artifact Signing
Build App (Preview) / compute-version (pull_request) Successful in 5s
Secret Scan / scan (pull_request) Successful in 4s
Secret Scan / scan (push) Successful in 6s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / build-macos (pull_request) Successful in 3m19s
Build App (Preview) / test (pull_request) Successful in 4m59s
Build App (Preview) / build-linux (pull_request) Successful in 5m18s
Build App (Preview) / build-windows (pull_request) Failing after 5m32s
Build App (Preview) / prune-previews (pull_request) Skipped
Build App (Preview) / compute-version (pull_request) Successful in 5s
Secret Scan / scan (pull_request) Successful in 4s
Secret Scan / scan (push) Successful in 6s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / build-macos (pull_request) Successful in 3m19s
Build App (Preview) / test (pull_request) Successful in 4m59s
Build App (Preview) / build-linux (pull_request) Successful in 5m18s
Build App (Preview) / build-windows (pull_request) Failing after 5m32s
Build App (Preview) / prune-previews (pull_request) Skipped
Releases and PR previews now sign the app binary, the MSI, the NSIS installer and its uninstaller. "Verify signatures" fails the job on any unsigned or untimestamped .exe/.msi, so an unsigned installer can't ship quietly. - windows-signing-setup.ps1 fetches Microsoft.ArtifactSigning.Client 1.0.128 and a job-local .NET 10.0.12 runtime, each pinned by hash. Nothing is installed on the build VM. It also writes the dlib metadata and exports TAURI_CONFIG with bundle.windows.signCommand. - windows-sign.ps1 runs the installed signtool with /dlib, SHA-256 and the Microsoft timestamp server, with retries. Credentials come only from the AZURE_* environment. The metadata excludes every credential type except EnvironmentCredential, because InteractiveBrowserCredential would hang a job running as SYSTEM. - The signing files go in the workspace, not %TEMP%, because the uninstaller is signed from 32-bit makensis and WOW64 redirects SYSTEM's %TEMP%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -836,6 +836,41 @@ verbatim copy yields a launcher entry that starts nothing. It keeps `StartupWMCl
|
||||
the bundle sets it, which is what lets the shell match the window to the entry. Extraction uses
|
||||
`--appimage-extract`, which needs no FUSE, so the script works before `fuse2` is installed.
|
||||
|
||||
### Windows code signing
|
||||
|
||||
Every Windows build — releases (`build-app.yml`) and PR previews (`build-app-preview.yml`) — is
|
||||
signed with **Azure Artifact Signing**: the app binary, the MSI, the NSIS installer and its
|
||||
uninstaller. Three scripts do it, and "Verify signatures" fails the job if any `.exe`/`.msi` is
|
||||
unsigned or untimestamped, so an unsigned installer cannot ship quietly.
|
||||
|
||||
- `scripts/windows-signing-setup.ps1` runs once per job. It downloads the signing client
|
||||
(`Microsoft.ArtifactSigning.Client`) and a .NET runtime into `.code-signing/` in the workspace,
|
||||
**each pinned by version and hash**, writes the dlib's `metadata.json`, and exports
|
||||
`TAURI_CONFIG` with `bundle.windows.signCommand`. Nothing is installed on the build VM. To bump
|
||||
a pin, take the hash from nuget.org / the .NET `releases.json`, never from your own download.
|
||||
- `scripts/windows-sign.ps1` is the sign command: `signtool sign /dlib` with SHA-256 and the
|
||||
Microsoft timestamp server, retried. Credentials never reach a command line — the dlib reads
|
||||
`AZURE_TENANT_ID` / `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET` from the environment.
|
||||
- `scripts/windows-verify-signatures.ps1` checks `signtool verify /pa` plus a timestamp.
|
||||
|
||||
Secrets (repository): the three `AZURE_*` above plus `ARTIFACT_SIGNING_ENDPOINT`,
|
||||
`ARTIFACT_SIGNING_ACCOUNT_NAME`, `ARTIFACT_SIGNING_PROFILE_NAME`. Four things are load-bearing:
|
||||
|
||||
- **`metadata.json` excludes every credential but `EnvironmentCredential`.** The dlib uses
|
||||
`DefaultAzureCredential`, whose chain ends in `InteractiveBrowserCredential`; the runners run as
|
||||
SYSTEM, where that waits forever for a browser.
|
||||
- **The "Build Tauri app" steps must not set `TAURI_CONFIG`.** A step-level value overrides the
|
||||
one the setup exported and drops the sign command without an error — "Verify signatures" is
|
||||
what would catch it.
|
||||
- **The signing files live in the workspace, not `%TEMP%`.** The NSIS uninstaller is signed from
|
||||
inside 32-bit `makensis`, which runs the sign command under 32-bit PowerShell; WOW64 redirects
|
||||
SYSTEM's `%TEMP%` (under System32) for that process. The workspace is under
|
||||
`systemprofile\.cache`, which the VM junctions for exactly this (see the comment on
|
||||
`build-app.yml`'s "Build Tauri app").
|
||||
- **The build VM is `WindowsBuilder` (VM 110 on the Proxmox host `pve4`)**, carrying both the
|
||||
`winvm-builder` and `virtual-builder` runners in host mode. It has the Windows SDK's
|
||||
`signtool` (10.0.26100) but no .NET — hence the job-local runtime.
|
||||
|
||||
## Testing
|
||||
|
||||
Frontend tests use Vitest with jsdom environment and React Testing Library. Setup file at `src/test/setup.ts`. Run a single test file:
|
||||
|
||||
Reference in New Issue
Block a user