Skip to content

ci: run Windows tests from a Linux-built archive #2068

ci: run Windows tests from a Linux-built archive

ci: run Windows tests from a Linux-built archive #2068

Workflow file for this run

name: CI
permissions:
# Doing it explicitly because the default permission only includes metadata: read.
contents: read
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
push:
branches:
- main
paths-ignore:
- '**/*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
defaults:
run:
shell: bash
jobs:
detect-changes:
runs-on: namespace-profile-linux-x64-default
permissions:
contents: read
pull-requests: read
outputs:
code-changed: ${{ steps.filter.outputs.code }}
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
code:
- '!**/*.md'
clippy:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
name: Clippy
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Update submodules
run: git submodule update --init --recursive
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: clippy
components: clippy
- run: rustup target add x86_64-unknown-linux-musl
- run: pipx install cargo-zigbuild
# pipx isolates cargo-zigbuild in its own venv, so its ziglang dependency
# (which bundles zig) isn't on PATH. Install zig separately.
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
# --locked: verify Cargo.lock is up to date (replaces the removed `cargo check --locked`)
- run: cargo clippy --locked --all-targets --all-features -- -D warnings
test:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
name: Test (${{ matrix.shard }})
strategy:
fail-fast: false
matrix:
include:
- os: namespace-profile-linux-x64-default
target: x86_64-unknown-linux-gnu
cargo_cmd: cargo-zigbuild
build_target: x86_64-unknown-linux-gnu.2.17
shard: linux-gnu
- os: namespace-profile-mac-default
target: aarch64-apple-darwin
cargo_cmd: cargo
build_target: aarch64-apple-darwin
shard: macos-arm64
- os: namespace-profile-mac-default
target: x86_64-apple-darwin
cargo_cmd: cargo
build_target: x86_64-apple-darwin
shard: macos-x64
runs-on: ${{ matrix.os }}
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Update submodules
run: git submodule update --init --recursive
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: test
- run: rustup target add ${{ matrix.target }}
- run: rustup target add x86_64-unknown-linux-musl
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
- run: pipx install cargo-zigbuild
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
# pipx isolates cargo-zigbuild in its own venv, so its ziglang dependency
# (which bundles zig) isn't on PATH. Install zig separately.
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
- name: Build tests
run: ${{ matrix.cargo_cmd }} test --no-run --target ${{ matrix.build_target }}
# Default `cargo test` runs only tests that need nothing beyond the
# Rust toolchain; this step verifies that contract before Node.js
# and pnpm enter the picture.
- name: Run tests
run: ${{ matrix.cargo_cmd }} test --target ${{ matrix.build_target }}
# x86_64-apple-darwin runs on arm64 runner under Rosetta; install x64 Node
# so fspy's x86_64 preload dylib can be injected into spawned node procs.
- uses: oxc-project/setup-node@ab97f03642370d79a7e96dd286bd02a1be40e0ba # v1.3.0
with:
architecture: ${{ matrix.target == 'x86_64-apple-darwin' && 'x64' || '' }}
- name: Run ignored tests
run: ${{ matrix.cargo_cmd }} test --target ${{ matrix.build_target }} -- --ignored
# Windows tests are cross-compiled on a fast Linux runner with cargo-xwin
# (clang-cl + lld-link against the xwin-downloaded MSVC CRT/Windows SDK)
# and packed into a portable nextest archive. The test-windows shards then
# only download the archive and run it — no Rust toolchain or compilation
# on the slow Windows runners. Windows e2e fixtures dominate wall-clock
# (60s per PTY step vs 20s on Unix), so the run is split across shards via
# nextest's count partitioning, which spreads tests round-robin.
build-windows-tests:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
name: Build Windows tests
runs-on: namespace-profile-linux-x64-default
env:
XWIN_ACCEPT_LICENSE: '1'
# The MSVC STL from xwin's VS17 manifest static-asserts a minimum Clang
# version newer than what runner images ship. Microsoft's documented
# escape hatch lets Detours (the only C++ in the workspace, and far older
# than any STL/clang concern here) compile with the system clang-cl.
# cargo-xwin folds a pre-set CXXFLAGS into the env it generates.
CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Update submodules
run: git submodule update --init --recursive
# llvm-tools provides llvm-ar in the toolchain's rustlib bin dir;
# cargo-xwin symlinks the MSVC-style llvm-lib/llvm-dlltool (used by cc-rs
# to archive Detours) and lld-link from there when the runner image has
# no system LLVM.
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: windows-cross
components: llvm-tools
- run: rustup target add x86_64-pc-windows-msvc
- uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10
with:
tool: cargo-nextest,cargo-xwin
# Downloading and unpacking the MSVC CRT/Windows SDK dominates this
# job's wall time (~10 minutes); the unpacked result is immutable for a
# given manifest version, so cache it. Bump the key when bumping the
# xwin version.
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
id: xwin-cache
with:
path: ~/.cache/cargo-xwin
key: cargo-xwin-msvc-17
- name: Build test archive
run: |
# cargo-xwin resolves the rustflags configured in .cargo/config.toml,
# appends its -Lnative Windows SDK paths, and re-exports the result as
# CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS, so the plain cargo
# that nextest invokes cross-compiles exactly like `cargo xwin` would.
eval "$(cargo xwin env --target x86_64-pc-windows-msvc | grep '^export ')"
# `cargo xwin env` renders its intended *removal* of RUSTFLAGS as
# `export RUSTFLAGS="";` — actually remove it so it cannot shadow the
# target-specific rustflags set above.
unset RUSTFLAGS
cargo nextest archive --workspace --target x86_64-pc-windows-msvc --archive-file windows-tests.tar.zst
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: windows-test-archive
path: windows-tests.tar.zst
retention-days: 7
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
if: steps.xwin-cache.outputs.cache-hit != 'true'
with:
path: ~/.cache/cargo-xwin
key: cargo-xwin-msvc-17
test-windows:
needs: build-windows-tests
name: Test (windows-${{ matrix.partition }})
strategy:
fail-fast: false
matrix:
partition: [1, 2, 3, 4, 5, 6]
runs-on: windows-latest
env:
PARTITION: count:${{ matrix.partition }}/6
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
# The Detours submodule is needed at run time: fspy_detours_sys's
# bindings test re-generates bindgen bindings against the checked-out
# headers.
- name: Update submodules
run: git submodule update --init --recursive
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: windows-test-archive
- uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10
with:
tool: cargo-nextest
# The default (non-ignored) test set needs nothing beyond the test
# binaries themselves; this step verifies that contract before Node.js
# and pnpm enter the picture. `cargo-nextest` is invoked directly so the
# job never depends on the runner's Rust toolchain.
- name: Run tests
run: cargo-nextest nextest run --archive-file windows-tests.tar.zst --workspace-remap . --partition "$PARTITION"
- uses: oxc-project/setup-node@ab97f03642370d79a7e96dd286bd02a1be40e0ba # v1.3.0
# --no-tests=pass: a shard's partition of the (much smaller) ignored
# test set may legitimately come up empty.
- name: Run ignored tests
run: cargo-nextest nextest run --archive-file windows-tests.tar.zst --workspace-remap . --partition "$PARTITION" --run-ignored ignored-only --no-tests pass
test-musl:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
name: Test (musl)
runs-on: namespace-profile-linux-x64-default
container:
image: node:25-alpine3.21
options: --shm-size=256m # shm_io tests need bigger shared memory
env:
# Override all rustflags to skip the zig cross-linker from .cargo/config.toml.
# Alpine's cc is already musl-native, so no custom linker is needed.
# Must mirror [build].rustflags and target rustflags from .cargo/config.toml
# (RUSTFLAGS env var overrides both levels).
# -crt-static: vite-task is shipped as a NAPI module in vite+, and musl Node
# with native modules links to musl libc dynamically, so we must do the same.
RUSTFLAGS: --cfg tokio_unstable -D warnings -C target-feature=-crt-static
# On musl, concurrent PTY operations can trigger SIGSEGV in musl internals.
# Run test threads sequentially to avoid the race.
RUST_TEST_THREADS: 1
steps:
- name: Install Alpine dependencies
shell: sh {0}
run: apk add --no-cache bash curl git musl-dev gcc g++ python3
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Update submodules
run: git submodule update --init --recursive
- name: Install rustup
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Install Rust toolchain
run: rustup show
- name: Build tests
run: cargo test --no-run
# Default `cargo test` runs only tests that need nothing beyond the
# Rust toolchain; this step verifies that contract before pnpm
# populates `packages/tools/node_modules`.
- name: Run tests
run: cargo test
- uses: oxc-project/setup-node@ab97f03642370d79a7e96dd286bd02a1be40e0ba # v1.3.0
- name: Run ignored tests
run: cargo test -- --ignored
fmt:
name: Format and Check Deps
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Update submodules
run: git submodule update --init --recursive
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: fmt
tools: cargo-shear@1.11.1,cargo-autoinherit@0.1.6
components: clippy rust-docs rustfmt
- uses: oxc-project/setup-node@ab97f03642370d79a7e96dd286bd02a1be40e0ba # v1.3.0
- run: pnpm exec vp fmt --check
- run: cargo autoinherit && git diff --exit-code
- run: cargo shear --deny-warnings
- run: cargo fmt --check
- run: RUSTDOCFLAGS='-D warnings' cargo doc --no-deps --document-private-items
- uses: crate-ci/typos@37bb98842b0d8c4ffebdb75301a13db0267cef89 # v1.47.2
with:
files: .
- name: Deduplicate dependencies
run: pnpm dedupe --check
- name: Check vite-task-client types are not stale
run: |
pnpm build-vite-task-client-types
git diff --exit-code packages/vite-task-client/src/index.d.ts
done:
runs-on: namespace-profile-linux-x64-default
if: always()
needs:
- clippy
- test
- test-musl
- build-windows-tests
- test-windows
- fmt
steps:
- run: exit 1
# Thank you, next https://github.com/vercel/next.js/blob/canary/.github/workflows/build_and_test.yml#L379
if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}