feat(ci): cross-compile and publish a Windows desktop build #63

Merged
nrupard merged 1 commit from feat/VAPP-56-desktop-windows-build into main 2026-06-29 17:55:39 +02:00
Owner

What

Adds a Windows x86_64 desktop build of vervain-app to CI, built and published the same way the Linux desktop build is. Resolves VAPP-56.

  • ci-build/Dockerfile.desktop-windows: extends the shared rust-builder-glibc image with mingw-w64 and the x86_64-pc-windows-gnu target, primes the cross-compiled dep graph, builds Tailwind, then runs dx build --release --platform windows --target x86_64-pc-windows-gnu and zips the dx app output (exe + asset bundle).
  • ci-build/win-gnu-link.sh: linker shim (see note below).
  • .forgejo/workflows/build-desktop-windows.yml: a copy of build-desktop-linux.yml with the same release/main/v* triggers, the same RUNS_ON_OPENSUSE_BASE_LATEST runner, the same GOV-13 version-from-trigger logic, and the same Generic Packages upload (DELETE-then-PUT, immutable vX.Y.Z vs moving latest paths). Artifact name: vervain-app-<version>-windows-x86_64.zip.

Why a linker shim

dx 0.7 always injects MSVC-style linker directives for Windows targets - /SUBSYSTEM:<CONSOLE|WINDOWS> and /ENTRY:mainCRTStartup - regardless of --windows-subsystem or the gnu triple. The mingw GNU linker cannot parse those and fails with cannot find /SUBSYSTEM:WINDOWS. The shim is wired in via CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER, rewrites those two directives to GNU ld syntax (-Wl,--subsystem,..., -Wl,--entry,...), and forwards everything else to mingw gcc. Default subsystem stays WINDOWS, so the result is a GUI app with no console window.

Decisions (from issue triage)

  • Cross-compile from the existing Linux runner (no new Windows runner). Native MSVC runner kept as the documented fallback if the gnu path ever breaks.
  • Artifact is a zip (exe + asset bundle), not a raw .exe.
  • New separate workflow file, not a matrix on the Linux one.

Validation

Clean docker build of ci-build/Dockerfile.desktop-windows on a single compile pass produced vervain-app-windows-x86_64.zip (13 MB) containing vervain-app.exe reported by file as PE32+ executable for MS Windows 5.02 (GUI), x86-64, plus the dx assets/ bundle. All deps (wry, webview2-com, tao, windows-*) compiled clean for x86_64-pc-windows-gnu.

No Rust/Cargo/Dioxus.toml changes, so the existing Linux desktop, web/OCI, and check workflows are untouched.

Assumption to confirm at runtime

The .exe is expected to launch on a Windows 10/11 x86_64 with the Evergreen WebView2 runtime present (no WebView2 bootstrapper is bundled). Not yet smoke-tested on a real Windows host.

## What Adds a Windows x86_64 desktop build of `vervain-app` to CI, built and published the same way the Linux desktop build is. Resolves VAPP-56. - `ci-build/Dockerfile.desktop-windows`: extends the shared `rust-builder-glibc` image with mingw-w64 and the `x86_64-pc-windows-gnu` target, primes the cross-compiled dep graph, builds Tailwind, then runs `dx build --release --platform windows --target x86_64-pc-windows-gnu` and zips the dx app output (exe + asset bundle). - `ci-build/win-gnu-link.sh`: linker shim (see note below). - `.forgejo/workflows/build-desktop-windows.yml`: a copy of `build-desktop-linux.yml` with the same `release`/`main`/`v*` triggers, the same `RUNS_ON_OPENSUSE_BASE_LATEST` runner, the same GOV-13 version-from-trigger logic, and the same Generic Packages upload (DELETE-then-PUT, immutable `vX.Y.Z` vs moving `latest` paths). Artifact name: `vervain-app-<version>-windows-x86_64.zip`. ## Why a linker shim dx 0.7 always injects MSVC-style linker directives for Windows targets - `/SUBSYSTEM:<CONSOLE|WINDOWS>` and `/ENTRY:mainCRTStartup` - regardless of `--windows-subsystem` or the gnu triple. The mingw GNU linker cannot parse those and fails with `cannot find /SUBSYSTEM:WINDOWS`. The shim is wired in via `CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER`, rewrites those two directives to GNU ld syntax (`-Wl,--subsystem,...`, `-Wl,--entry,...`), and forwards everything else to mingw gcc. Default subsystem stays WINDOWS, so the result is a GUI app with no console window. ## Decisions (from issue triage) - Cross-compile from the existing Linux runner (no new Windows runner). Native MSVC runner kept as the documented fallback if the gnu path ever breaks. - Artifact is a zip (exe + asset bundle), not a raw `.exe`. - New separate workflow file, not a matrix on the Linux one. ## Validation Clean `docker build` of `ci-build/Dockerfile.desktop-windows` on a single compile pass produced `vervain-app-windows-x86_64.zip` (13 MB) containing `vervain-app.exe` reported by `file` as `PE32+ executable for MS Windows 5.02 (GUI), x86-64`, plus the dx `assets/` bundle. All deps (wry, webview2-com, tao, windows-*) compiled clean for `x86_64-pc-windows-gnu`. No Rust/Cargo/Dioxus.toml changes, so the existing Linux desktop, web/OCI, and check workflows are untouched. ## Assumption to confirm at runtime The `.exe` is expected to launch on a Windows 10/11 x86_64 with the Evergreen WebView2 runtime present (no WebView2 bootstrapper is bundled). Not yet smoke-tested on a real Windows host.
feat(ci): cross-compile and publish a Windows desktop build
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 27s
Create release / Create release from merged PR (pull_request) Has been skipped
518760e113
Add a Windows x86_64 desktop build that mirrors the Linux pipeline, cross-compiled from the existing Linux Docker runner so no Windows runner is needed.

`ci-build/Dockerfile.desktop-windows` extends the shared rust-builder-glibc image with the mingw-w64 toolchain and the x86_64-pc-windows-gnu target, primes the cross-compiled dep graph, builds Tailwind, then runs `dx build --release --platform windows --target x86_64-pc-windows-gnu`. The whole dx app output (vervain-app.exe plus the asset bundle) is zipped to /build/artifacts/vervain-app-windows-x86_64.zip.

dx 0.7 always injects MSVC-style linker directives (/SUBSYSTEM:*, /ENTRY:mainCRTStartup) for Windows targets, regardless of `--windows-subsystem` or the gnu triple, and the mingw GNU linker cannot parse them. `ci-build/win-gnu-link.sh` is a linker shim wired in via CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER that rewrites those two directives to GNU ld syntax (-Wl,--subsystem,... and -Wl,--entry,...) and forwards everything else to mingw gcc. The result is a GUI-subsystem PE32+ binary (no console window). Verified end-to-end via a clean docker build: the zip contains a working PE32+ x86-64 GUI vervain-app.exe.

`.forgejo/workflows/build-desktop-windows.yml` is a copy of build-desktop-linux.yml: same release/main/tag triggers, same RUNS_ON_OPENSUSE_BASE_LATEST runner, same GOV-13 version-from-trigger resolution, and the same Generic Packages upload (DELETE-then-PUT, immutable vX.Y.Z vs moving latest paths) with the artifact named vervain-app-<version>-windows-x86_64.zip.

#VAPP-56

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch feat/VAPP-56-desktop-windows-build 2026-06-29 17:55:39 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!63
No description provided.