CI: sign Windows releases with Azure Artifact Signing (#62)
Build App / compute-version (push) Successful in 17s
Secret Scan / scan (push) Successful in 7s
Build App / build-macos (push) Successful in 3m6s
Build App / build-linux (push) Successful in 5m15s
Build App / build-windows (push) Successful in 5m52s
Build App / create-tag (push) Successful in 5s
Build App / sync-to-github (push) Successful in 1m50s

Windows releases are now signed with Azure Artifact Signing: the app binary, the MSI, the NSIS installer and its uninstaller, 5 signatures per release. Build-time WiX and NSIS DLLs are skipped. "Verify signatures" fails a release unless the installers, the binaries inside the MSI, and the logged app-binary and uninstaller signatures are all valid and timestamped.

Previews are not signed and don't reference the signing secrets. The sign command reaches Tauri through `--config`; the v2 CLI never read TAURI_CONFIG, so the old inline override was a no-op.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #62.
This commit is contained in:
2026-09-24 02:09:48 +00:00
co-authored by Claude Opus 5.5
parent 85901d8a80
commit f8e3ec1150
7 changed files with 503 additions and 6 deletions
+16 -3
View File
@@ -7,6 +7,8 @@ name: Build App (Preview)
# This is also the **PR build check**: it compiles Linux, macOS and Windows, so
# a push that breaks any of them fails here. Its `test` job runs vitest and
# `cargo test` too, so a push that breaks either suite fails here as well.
# Previews are not code-signed (releases are, in build-app.yml): see the
# comment on the Windows job's "Build Tauri app" step.
# build-app.yml used to do the build-check job in parallel and publish nothing,
# which meant six OS builds per push and one unreachable set of bundles; it is
# now releases-only.
@@ -669,7 +671,10 @@ jobs:
- name: Install Tauri CLI via cargo
run: |
set "PATH=%USERPROFILE%\.cargo\bin;C:\Program Files\nodejs;%PATH%"
cargo install tauri-cli --version "^2"
rem Pinned to the @tauri-apps/cli version in app/package-lock.json, which
rem the Linux and macOS jobs run, and kept identical to build-app.yml so
rem a preview is built by the same bundler as the release it previews.
cargo install tauri-cli --version "=2.11.0" --locked
- name: Fix npm platform detection
run: |
@@ -692,13 +697,21 @@ jobs:
- name: Build Tauri app
working-directory: ./app
# Previews are not code-signed: signing is metered, previews are built
# on every PR push, and a PR's workflow runs the PR's own code - so the
# signing secrets stay out of this workflow entirely. Releases are
# signed in build-app.yml.
#
# beforeBuildCommand is blanked through --config because the frontend
# was built in the step above. Not TAURI_CONFIG: the v2 CLI never
# reads that variable, and the inline one this step used to set was a
# no-op.
env:
TAURI_CONFIG: "{\"build\":{\"beforeBuildCommand\":\"\"}}"
# See the matching comment on the Linux job's "Build Tauri app" step.
TRIPLE_C_BUILD_SUFFIX: ${{ needs.compute-version.outputs.suffix }}
run: |
set "PATH=%USERPROFILE%\.cargo\bin;C:\Program Files\nodejs;%PATH%"
cargo tauri build
cargo tauri build --config "{\"build\":{\"beforeBuildCommand\":\"\"}}"
- name: Collect artifacts
run: |
+42 -3
View File
@@ -7,6 +7,7 @@ on:
- "app/**"
- "VERSION"
- ".gitea/workflows/build-app.yml"
- "scripts/windows-*.ps1"
workflow_dispatch:
# Deliberately **not** on pull_request. Every publishing step here is gated on
@@ -611,7 +612,11 @@ jobs:
- name: Install Tauri CLI via cargo
run: |
set "PATH=%USERPROFILE%\.cargo\bin;C:\Program Files\nodejs;%PATH%"
cargo install tauri-cli --version "^2"
rem Pinned to the @tauri-apps/cli version in app/package-lock.json, which
rem the Linux and macOS jobs run: the Windows code-signing path (sign
rem command, NSIS uninstaller signing) was verified against it, and "^2"
rem would change it underneath the pipeline on any Tauri release.
cargo install tauri-cli --version "=2.11.0" --locked
- name: Fix npm platform detection
run: |
@@ -632,10 +637,31 @@ jobs:
set "PATH=%USERPROFILE%\.cargo\bin;C:\Program Files\nodejs;%PATH%"
npm run build
# Releases are signed with Azure Artifact Signing (scripts/windows-*.ps1).
# The setup fetches the signing client and a job-local .NET runtime, and
# writes the Tauri config holding the sign command, which "Build Tauri
# app" passes with --config. A missing secret fails here, before the build.
- name: Prepare code signing
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
ARTIFACT_SIGNING_ENDPOINT: ${{ secrets.ARTIFACT_SIGNING_ENDPOINT }}
ARTIFACT_SIGNING_ACCOUNT_NAME: ${{ secrets.ARTIFACT_SIGNING_ACCOUNT_NAME }}
ARTIFACT_SIGNING_PROFILE_NAME: ${{ secrets.ARTIFACT_SIGNING_PROFILE_NAME }}
run: powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File scripts\windows-signing-setup.ps1
- name: Build Tauri app
working-directory: ./app
# The sign command comes in through --config, from the file "Prepare
# code signing" wrote. Not TAURI_CONFIG: the v2 CLI never reads that
# variable (the inline one this step used to set was a no-op), and
# "Verify signatures" is what caught it.
env:
TAURI_CONFIG: "{\"build\":{\"beforeBuildCommand\":\"\"}}"
# Read by the signing dlib itself, never passed on a command line.
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: |
set "PATH=%USERPROFILE%\.cargo\bin;C:\Program Files\nodejs;%PATH%"
rem Every Tauri bundler it downloads - candle.exe, light.exe and
@@ -650,7 +676,20 @@ jobs:
rem systemprofile\AppData\Local\tauri and systemprofile\.cache to the
rem System32 originals, which makes the redirected view resolve. A
rem runner running as a normal user needs no such patch.
cargo tauri build --bundles msi,nsis
cargo tauri build --bundles msi,nsis --config "%TRIPLE_C_TAURI_SIGN_CONFIG%"
- name: Verify signatures
run: >-
powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass
-File scripts\windows-verify-signatures.ps1
app\src-tauri\target\release\bundle\msi\*.msi
app\src-tauri\target\release\bundle\nsis\*.exe
# Tauri reports a failed sign command as just "failed to run powershell";
# windows-sign.ps1 keeps its own transcript, signtool /debug included.
- name: Show signing output
if: failure()
run: if exist .code-signing\sign-output.log type .code-signing\sign-output.log
- name: Collect artifacts
run: |