ci: run Windows tests from a Linux-built archive #2068
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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')) }} |