Skip to content

Windows arm64: Velopack code-signing fails on the windows-11-arm runner #190

Description

@tig

Follow-up to #187 (Windows arm64 build + installer, added in #188). The arm64 build works; only Velopack's code-signing step fails on the arm64 runner. Tracking the remaining work here so #187's build can ship for real.

Current state (works)

  • The win-arm64 release leg builds natively on the windows-11-arm runner:
    • ✅ AOT TUI (win-arm64/wp.dll → apphost) — also gated in PR CI (aot-publish (win-arm64))
    • ✅ MAUI/WinUI3 GUI (win-arm64/WinPrint.dll)
  • The leg is experimental: true (continue-on-error) in release.yml, so it cannot block a release — x64/macOS/Linux ship normally; the arm64 Setup.exe is simply absent until this is fixed.

The failure

Pack with Velopack[FTL] Signing command failed. Specify --verbose argument to print signing command. with empty output. With --verbose, the command is:

...\vpk\1.1.1\vendor\signing\signtool.exe sign /fd SHA256 /tr http://timestamp.acs.microsoft.com /v /debug /td SHA256 /dlib Azure.CodeSigning.Dlib.dll /dmdf azure-trusted-signing.json <files...>

i.e. Velopack invokes its bundled x64 signtool.exe + Azure.CodeSigning.Dlib.dll, which fails on the arm64 host and writes nothing to stdout/stderr (so the real error is invisible without further probing). Same Azure Trusted Signing setup signs win-x64 fine every release, so it's specific to the arm64 runner / x64-signtool interplay. Related upstream: velopack/velopack#85.

Tried, did NOT fix it

  • Added vpk --verbose — prints the command but signtool's own output is empty.
  • Installed the x64 .NET 8 runtime on the arm64 runner (-Architecture x64 -Runtime dotnet -Channel 8.0 -InstallDir "$env:ProgramFiles\dotnet\x64") per Velopack's note that the x64 signtool/dlib need the x64 runtime — no change, identical empty-output failure. (So it isn't simply a missing x64 runtime.)

Recommended fix: cross-sign on the x64 runner

Build arm64 where it must be native, but pack + sign on the x64 runner where Velopack's bundled x64 signtool is proven:

  1. In the win-arm64 (arm64-runner) leg, upload the artifacts/publish/win-arm64 directory as an artifact (currently only the velopack output is uploaded, which never gets produced).
  2. Add a job on windows-latest (x64) that needs the arm64 build, downloads that publish dir, does the Azure OIDC login + metadata, runs vpk pack --runtime win-arm64 --packDir <downloaded> --channel win-arm64 ... --azureTrustedSignFile artifacts/azure-trusted-signing.json, and uploads the result. (Velopack supports cross-compiling/packing: https://docs.velopack.io/packaging/cross-compiling)

Alternative fix: arm64-native signtool via vpk --signTemplate

Keep everything on the arm64 runner but bypass Velopack's bundled x64 signtool:

  • Use the arm64 signtool.exe from the Windows SDK (...\Windows Kits\10\bin\<ver>\arm64\signtool.exe).
  • Use the arm64 Azure.CodeSigning.Dlib.dll from the Microsoft.Trusted.Signing.Client NuGet (bin/arm64/...).
  • Pass vpk pack ... --signTemplate '"<arm64 signtool>" sign /fd SHA256 /tr http://timestamp.acs.microsoft.com /td SHA256 /dlib "<arm64 dlib>" /dmdf "<json>" "{{file}}"' instead of --azureTrustedSignFile.

How to iterate (can't be tested locally — no Windows/arm64)

  • release.yml's publish/winget/brew are gated to tag pushes, so a workflow_dispatch from develop runs ONLY the build+sign matrix as a safe dry-run (no release, no tap, no winget; Azure OIDC federated creds match develop):
    gh workflow run release.yml --ref develop
  • Inspect the Package win-arm64Pack with Velopack step.

Acceptance criteria

  • A release produces a signed Kindel.WinPrint-win-arm64-Setup.exe.
  • Remove the experimental: true flag from the win-arm64 matrix leg so it ships first-class.
  • Add the arm64 installer to the winget manifest (regex already widened to win-(x64|arm64)).

Notes / cleanup

  • The non-working x64-runtime install step and the temporary --verbose were reverted from release.yml when parking this; re-add --verbose while debugging.
  • Keep the publish tag-guard (enables the safe dispatch dry-run) and the experimental flag (keeps releases unblocked) until this is done.

Dry-run evidence: failing runs 28295536229 (v2.8.8 release leg), 28299089065 + 28299484579 (develop dispatches).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions