Skip to content

ci: cross-compile Windows tests and CLI binaries on Linux with cargo-xwin#1824

Draft
fengmk2 wants to merge 4 commits into
mainfrom
ci/cross-compile-windows-tests
Draft

ci: cross-compile Windows tests and CLI binaries on Linux with cargo-xwin#1824
fengmk2 wants to merge 4 commits into
mainfrom
ci/cross-compile-windows-tests

Conversation

@fengmk2

@fengmk2 fengmk2 commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Build the Windows test binaries and the CLI/binding once on a Linux runner with cargo-xwin, then just run them on Windows (download-only). Same approach as voidzero-dev/vite-task#443.

Run Wall time
main (cache-miss) 34m45s
this PR 15m41s

Net: ~19m faster, about 55% less wall time. (The main baseline includes the main-only sfw job, and this PR's Linux cross-build was cold; a binding-cache hit makes it download-only and drops the Windows tail to ~8m.)

Why this is faster:

  • Linux is a much faster place to cross-compile the Windows binaries (cargo-xwin + clang-cl/lld-link against the cached MSVC CRT / Windows SDK), instead of every Windows job rebuilding the same ~24-minute release binding + CLI binaries.
  • Before, the run was gated by the single slowest Windows job (~30m, each doing its own full build); after, it is gated by the Linux cross-build (~10m cold, ~2m on a binding-cache hit) plus a run-only Windows tail (~5m).
  • The Windows jobs (Test, CLI E2E, CLI snap, sfw) now download a prebuilt artifact and skip native compilation, so they need no Rust toolchain or dev drive.
  • test's Windows leg runs from a nextest archive (build-windows-tests) with no toolchain at all.
  • Releases are unaffected and keep building natively on Windows (their cache keys differ via RELEASE_BUILD/VERSION). CI Windows binaries are clang-cl/lld-link-built; the e2e/snap suites still run them on real Windows.
flowchart LR
  subgraph Before["Before: every Windows job rebuilds the workspace"]
    B1["Windows jobs x5"] --> B2["Compile binding + vp on Windows<br/>~24m each"]
    B2 --> B3["Install vp"]
    B3 --> B4["Run e2e / snap / sfw"]
  end

  subgraph After["After: build once on Linux, run on Windows"]
    A1["Linux: build-windows-cli"] --> A2["vp.exe + binding artifact"]
    A2 --> A3["CLI E2E / snap / sfw (Windows)<br/>download + run, no toolchain"]
    A4["Linux: build-windows-tests"] --> A5["nextest archive"]
    A5 --> A6["Test (Windows)<br/>run-only"]
  end
Loading

@fengmk2 fengmk2 self-assigned this Jun 12, 2026
@netlify

netlify Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit fee2372
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a2cfda5e6877100080da011

fengmk2 added 3 commits June 13, 2026 14:38
…xwin

Windows jobs previously compiled the workspace (test job) and the
release NAPI binding plus vp binaries (cli-e2e-test, 3 cli-snap-test
shards, install-e2e-test-sfw) on slow windows-latest runners, costing
about 24 minutes per job on every NAPI binding cache miss.

Apply the approach from voidzero-dev/vite-task#443:

- build-windows-tests (Linux) cross-compiles workspace tests with
  cargo-xwin into a portable cargo-nextest archive and runs the
  Windows-target cargo check with -D warnings; the run-only
  test-windows job downloads the archive and runs it without a Rust
  toolchain (--test-threads 1 because serial_test in-process locks do
  not span nextest's process-per-test model).
- build-windows-cli (Linux) cross-builds the release binding via
  napi build -x and vp.exe/vp-shim.exe/vp-setup.exe via cargo xwin
  build, published as the windows-cli-binaries artifact; the Windows
  entries of cli-e2e-test, cli-snap-test, and install-e2e-test-sfw
  download it and skip native builds via build-upstream's new
  skip-native input.

Release builds are unaffected and keep building natively on Windows
(reusable-release-build cache keys differ via RELEASE_BUILD/VERSION).
The build-windows-cli job intentionally skips the TS builds, but
build.ts formats the generated index.cjs/index.d.cts with options
imported from the repo vite config, which itself imports the built
vite-plus dist and crashed with ERR_MODULE_NOT_FOUND after a successful
cross compile. Add a --skip-format flag to build.ts and pass it from
the job; formatting the generated bindings is cosmetic there.
The generated binding JS files (index.cjs, index.d.cts, index.js,
index.d.ts) are committed to the repo, and the cross-build job now
regenerates them unformatted (--skip-format). Shipping them in the
artifact overwrote the pristine committed copies in consumer checkouts
and made vp check fail on the Windows e2e job. Consumers only need the
.node binding and the vp/vp-shim/vp-setup executables; the committed
index files are already correct (Linux e2e's unexpected-file-changes
check guards their drift).
@fengmk2 fengmk2 force-pushed the ci/cross-compile-windows-tests branch from 76a1e12 to 954004c Compare June 13, 2026 06:39
- build-upstream: collapse the repeated `skip-native != 'true' &&
  cache-hit != 'true'` condition (12 step guards) into a single
  computed `native.build` output gating each step.
- Add reciprocal 'keep in sync' comments on the two copies of the
  hashFiles cache-key input list (build-upstream vs build-windows-cli)
  so they don't drift and serve a stale cross-built artifact.
- build.ts: extract the custom-flag list and use .includes() instead
  of a growing chain of != comparisons.
@fengmk2 fengmk2 added test: e2e Auto run e2e tests test: install-e2e run vite install e2e test test: create-e2e Run `vp create` e2e tests test: sfw labels Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test: create-e2e Run `vp create` e2e tests test: e2e Auto run e2e tests test: install-e2e run vite install e2e test test: sfw

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant