From 8104b70210deabc8edc63ddb8b71e955fb8c65c3 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 12 Jun 2026 22:53:16 +0800 Subject: [PATCH 1/7] ci: cross-compile Windows tests and CLI binaries on Linux with cargo-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). --- .github/actions/build-upstream/action.yml | 32 +- .github/workflows/ci.yml | 338 +++++++++++++++++++--- 2 files changed, 320 insertions(+), 50 deletions(-) diff --git a/.github/actions/build-upstream/action.yml b/.github/actions/build-upstream/action.yml index dc0461b9a8..ef45e1814c 100644 --- a/.github/actions/build-upstream/action.yml +++ b/.github/actions/build-upstream/action.yml @@ -8,6 +8,13 @@ inputs: description: 'Print the output after the build' required: false default: 'false' + skip-native: + description: >- + Skip the NAPI binding cache and all native cargo/napi build steps; the + native binaries must already be in place (e.g. downloaded from the + windows-cli-binaries artifact produced by the build-windows-cli job). + required: false + default: 'false' runs: using: 'composite' @@ -38,6 +45,7 @@ runs: # Cache NAPI bindings and Rust CLI binary (the slow parts, especially on Windows) - name: Restore NAPI binding cache id: cache-restore + if: inputs.skip-native != 'true' uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | @@ -69,14 +77,14 @@ runs: # Install zig + cargo-zigbuild for musl cross-compilation (napi-cross only supports gnu) - name: Add musl Rust target - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') shell: bash run: rustup target add ${INPUTS_TARGET} env: INPUTS_TARGET: ${{ inputs.target }} - name: Setup zig (musl) - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 with: version: 0.15.2 @@ -87,7 +95,7 @@ runs: # the pinned git commit. TODO: revert to taiki-e/install-action once # cargo-zigbuild > 0.22.3 is released. - name: Install cargo-zigbuild (musl) - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') shell: bash run: cargo install --locked --git https://github.com/rust-cross/cargo-zigbuild --rev 7e791b4be71b9870e0abccedf7885486803cd923 cargo-zigbuild @@ -95,7 +103,7 @@ runs: # Must run before vite-plus TypeScript builds which depend on the bindings - name: Build NAPI bindings (Linux gnu) shell: bash - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') run: | pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} --use-napi-cross env: @@ -106,7 +114,7 @@ runs: - name: Build NAPI bindings (Linux musl) shell: bash - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') run: | pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} -x env: @@ -117,7 +125,7 @@ runs: - name: Build NAPI bindings (non-Linux targets) shell: bash - if: steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux') run: | pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} env: @@ -125,7 +133,7 @@ runs: INPUTS_TARGET: ${{ inputs.target }} - name: Build Rust CLI binary (Linux gnu) - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') shell: bash run: | pnpm exec napi build --use-napi-cross --target ${INPUTS_TARGET} --release -p vite_global_cli @@ -136,7 +144,7 @@ runs: INPUTS_TARGET: ${{ inputs.target }} - name: Build Rust CLI binary (Linux musl) - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') shell: bash run: | pnpm exec napi build -x --target ${INPUTS_TARGET} --release -p vite_global_cli @@ -147,28 +155,28 @@ runs: INPUTS_TARGET: ${{ inputs.target }} - name: Build Rust CLI binary (non-Linux targets) - if: steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux') shell: bash run: cargo build --release --target ${INPUTS_TARGET} -p vite_global_cli env: INPUTS_TARGET: ${{ inputs.target }} - name: Build trampoline shim binary (Windows only) - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows') shell: bash run: cargo build --release --target ${INPUTS_TARGET} -p vite_trampoline env: INPUTS_TARGET: ${{ inputs.target }} - name: Build installer binary (Windows only) - if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows') + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows') shell: bash run: cargo build --release --target ${INPUTS_TARGET} -p vite_installer env: INPUTS_TARGET: ${{ inputs.target }} - name: Save NAPI binding cache - if: steps.cache-restore.outputs.cache-hit != 'true' + if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 581416380d..2f92dd7acb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,8 +76,6 @@ jobs: include: - os: namespace-profile-linux-x64-default target: x86_64-unknown-linux-gnu - - os: windows-latest - target: x86_64-pc-windows-msvc - os: namespace-profile-mac-default target: aarch64-apple-darwin runs-on: ${{ matrix.os }} @@ -85,22 +83,11 @@ jobs: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - - name: Setup Dev Drive - if: runner.os == 'Windows' - uses: samypr100/setup-dev-drive@30f0f98ae5636b2b6501e181dfb3631b9974818d # v4.0.0 - with: - drive-size: 12GB - drive-format: ReFS - env-mapping: | - CARGO_HOME,{{ DEV_DRIVE }}/.cargo - RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup - - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) with: save-cache: ${{ github.ref_name == 'main' }} cache-key: test tools: just - target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }} - run: cargo check --all-targets --all-features env: @@ -108,6 +95,136 @@ jobs: - run: just test + # 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 job then + # only downloads the archive and runs it: no Rust toolchain, dev drive, or + # compilation on the slow Windows runner. Same approach as + # voidzero-dev/vite-task#443. + 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 (C++, via the fspy git dependency) compile + # with the runner's clang-cl; cargo-xwin folds a pre-set CXXFLAGS into + # the per-target CXXFLAGS it generates. + CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH + steps: + - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 + - uses: ./.github/actions/clone + + # 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 C/C++ deps) and lld-link from there when the runner + # image has no matching system LLVM. + - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + 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 cold + # wall time (~10 minutes); the unpacked result is immutable for a given + # manifest version, so cache it. Bump the key when bumping cargo-xwin. + - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: xwin-cache + with: + path: ~/.cache/cargo-xwin + key: cargo-xwin-msvc-17 + + # `cargo xwin env` resolves the rustflags configured in + # .cargo/config.toml (including the cfg-based Windows section), appends + # its -Lnative Windows SDK paths, and re-exports the result as + # CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS, so plain cargo + # invocations cross-compile exactly like `cargo xwin` would. It renders + # its intended *removal* of RUSTFLAGS as `export RUSTFLAGS="";`, which + # must become a real unset: a set RUSTFLAGS takes precedence over the + # target-specific rustflags and would shadow them. + - name: Check Windows target + run: | + eval "$(cargo xwin env --target x86_64-pc-windows-msvc | grep '^export ')" + unset RUSTFLAGS + # Fail fast if cargo-xwin ever stops folding the repo's cfg-based + # Windows rustflags (--cfg tokio_unstable, /STACK:8388608) into the + # target env var; a dropped /STACK would be a silent runtime + # difference in the shipped test binaries. + case "$CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS" in + *tokio_unstable*STACK:8388608*) ;; + *) + echo "::error::cargo-xwin dropped the .cargo/config.toml Windows rustflags: $CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS" + exit 1 + ;; + esac + export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="$CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS -D warnings" + cargo check --all-targets --all-features --target x86_64-pc-windows-msvc + + # Keep the package selection in sync with the `test` recipe in justfile. + - name: Build test archive + run: | + eval "$(cargo xwin env --target x86_64-pc-windows-msvc | grep '^export ')" + unset RUSTFLAGS + cargo nextest archive $(for d in crates/*/; do echo -n "-p $(basename $d) "; done) -p vite-plus-cli \ + --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) + runs-on: windows-latest + steps: + - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 + - uses: ./.github/actions/clone + + - 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 + + # `cargo-nextest` is invoked directly so the job never depends on the + # runner's Rust toolchain. + # --test-threads=1: nextest runs each test in its own process, so + # serial_test's in-process locks no longer serialize tests that contend + # on cross-process shared state (fixed temp paths, e.g. + # `vp-test-custom-home` in vite_shared::home tests). The whole suite is + # only ~1 minute of test time, so full serialization is cheap + # insurance; revisit if it becomes the bottleneck. + - name: Run tests + run: cargo-nextest nextest run --archive-file windows-tests.tar.zst --workspace-remap . --test-threads 1 + env: + RUST_MIN_STACK: '8388608' + # Keep Windows env parity with the `test` recipe in justfile. + __COMPAT_LAYER: RunAsInvoker + # Snapshot assertions resolve paths from the Linux build machine's + # workspace root baked in at compile time; point insta at this + # checkout instead (without it, insta shells out to a `cargo` that + # is not installed here). + INSTA_WORKSPACE_ROOT: ${{ github.workspace }} + test-musl: needs: detect-changes if: needs.detect-changes.outputs.code-changed == 'true' @@ -185,10 +302,137 @@ jobs: - name: Deduplicate dependencies run: pnpm dedupe --check || pnpm dedupe --check + # Cross-compiles the Windows release NAPI binding (vite-plus .node) and CLI + # binaries (vp.exe, vp-shim.exe, vp-setup.exe) on a fast Linux runner with + # cargo-xwin, then publishes them as the windows-cli-binaries artifact. The + # Windows entries of cli-e2e-test, cli-snap-test, and install-e2e-test-sfw + # download that artifact instead of each spending ~24 minutes rebuilding the + # same binaries on slow windows-latest runners whenever build-upstream's + # NAPI binding cache misses. Release builds are unaffected: they keep + # building natively on Windows via reusable-release-build.yml (their cache + # keys differ through RELEASE_BUILD/VERSION). + build-windows-cli: + needs: detect-changes + if: needs.detect-changes.outputs.code-changed == 'true' + name: Build Windows CLI binaries + runs-on: namespace-profile-linux-x64-default + env: + XWIN_ACCEPT_LICENSE: '1' + # See build-windows-tests for why this Detours escape hatch is needed. + CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH + steps: + - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 + - uses: ./.github/actions/clone + + # Same input set as build-upstream's NAPI binding cache key, but under + # a distinct prefix: this cache stores Linux-relative paths and must + # never mix with the caches build-upstream saves on other runners. + - name: Compute binaries cache key + id: cache-key + run: | + echo "key=windows-cli-xwin-v1-${{ hashFiles('packages/tools/.upstream-versions.json', 'rust-toolchain.toml', 'Cargo.lock', 'crates/**/*.rs', 'crates/*/Cargo.toml', 'packages/cli/binding/**/*.rs', 'packages/cli/binding/Cargo.toml', 'Cargo.toml', '.cargo/config.toml', 'packages/cli/package.json', 'packages/cli/build.ts', 'packages/cli/tsdown.config.ts') }}" >> $GITHUB_OUTPUT + + - name: Restore binaries cache + id: binaries-cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + packages/cli/binding/*.node + packages/cli/binding/index.js + packages/cli/binding/index.d.ts + packages/cli/binding/index.cjs + packages/cli/binding/index.d.cts + target/x86_64-pc-windows-msvc/release/vp.exe + target/x86_64-pc-windows-msvc/release/vp-shim.exe + target/x86_64-pc-windows-msvc/release/vp-setup.exe + key: ${{ steps.cache-key.outputs.key }} + + # llvm-tools provides llvm-ar in the toolchain's rustlib bin dir; + # cargo-xwin symlinks the MSVC-style llvm-lib/llvm-dlltool and lld-link + # from there when the runner image has no matching system LLVM. + - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + if: steps.binaries-cache.outputs.cache-hit != 'true' + with: + save-cache: ${{ github.ref_name == 'main' }} + cache-key: windows-cli-cross + components: llvm-tools + + - run: rustup target add x86_64-pc-windows-msvc + if: steps.binaries-cache.outputs.cache-hit != 'true' + + # Preinstall cargo-xwin so `napi build -x` (which shells out to + # `cargo xwin build` for MSVC targets) doesn't fall back to a slow + # `cargo install` of it. + - uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10 + if: steps.binaries-cache.outputs.cache-hit != 'true' + with: + tool: cargo-xwin + + # Shared with build-windows-tests; see that job for details. + - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + if: steps.binaries-cache.outputs.cache-hit != 'true' + id: xwin-cache + with: + path: ~/.cache/cargo-xwin + key: cargo-xwin-msvc-17 + + - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 + if: steps.binaries-cache.outputs.cache-hit != 'true' + + - name: Build NAPI binding + if: steps.binaries-cache.outputs.cache-hit != 'true' + run: pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x + env: + DEBUG: 'napi:*' + + - name: Build Rust CLI binaries + if: steps.binaries-cache.outputs.cache-hit != 'true' + run: cargo xwin build --release --target x86_64-pc-windows-msvc -p vite_global_cli -p vite_trampoline -p vite_installer + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: windows-cli-binaries + # Both path roots are workspace-relative, so the artifact preserves + # the repo layout and a plain download into the workspace places + # every file where bootstrap-cli:ci (tool install-global-cli) + # expects it. + path: | + packages/cli/binding/*.node + packages/cli/binding/index.js + packages/cli/binding/index.d.ts + packages/cli/binding/index.cjs + packages/cli/binding/index.d.cts + target/x86_64-pc-windows-msvc/release/vp.exe + target/x86_64-pc-windows-msvc/release/vp-shim.exe + target/x86_64-pc-windows-msvc/release/vp-setup.exe + if-no-files-found: error + retention-days: 7 + + - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + if: steps.binaries-cache.outputs.cache-hit != 'true' + with: + path: | + packages/cli/binding/*.node + packages/cli/binding/index.js + packages/cli/binding/index.d.ts + packages/cli/binding/index.cjs + packages/cli/binding/index.d.cts + target/x86_64-pc-windows-msvc/release/vp.exe + target/x86_64-pc-windows-msvc/release/vp-shim.exe + target/x86_64-pc-windows-msvc/release/vp-setup.exe + key: ${{ steps.cache-key.outputs.key }} + + - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + if: steps.binaries-cache.outputs.cache-hit != 'true' && steps.xwin-cache.outputs.cache-hit != 'true' + with: + path: ~/.cache/cargo-xwin + key: cargo-xwin-msvc-17 + cli-e2e-test: name: CLI E2E test needs: - download-previous-rolldown-binaries + - build-windows-cli strategy: fail-fast: false matrix: @@ -204,21 +448,13 @@ jobs: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - - name: Setup Dev Drive - if: runner.os == 'Windows' - uses: samypr100/setup-dev-drive@30f0f98ae5636b2b6501e181dfb3631b9974818d # v4.0.0 - with: - drive-size: 12GB - drive-format: ReFS - env-mapping: | - CARGO_HOME,{{ DEV_DRIVE }}/.cargo - RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup - + # Windows runs against the prebuilt binaries from build-windows-cli, + # so it needs no Rust toolchain or dev drive here. - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + if: runner.os != 'Windows' with: save-cache: ${{ github.ref_name == 'main' }} cache-key: cli-e2e-test-${{ matrix.target }} - target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }} - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 @@ -231,10 +467,22 @@ jobs: path: ./rolldown/packages/rolldown/src merge-multiple: true + # Prebuilt by build-windows-cli (Linux + cargo-xwin); the artifact + # paths are workspace-relative, so this places the binding into + # packages/cli/binding/ and the CLI binaries into + # target/x86_64-pc-windows-msvc/release/ where + # `tool install-global-cli` finds them. + - name: Download prebuilt Windows binaries + if: runner.os == 'Windows' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: windows-cli-binaries + - name: Build with upstream uses: ./.github/actions/build-upstream with: target: ${{ matrix.target }} + skip-native: ${{ runner.os == 'Windows' && 'true' || 'false' }} - name: Ensure no unexpected file changes after build if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} @@ -724,6 +972,7 @@ jobs: name: CLI snap test (${{ matrix.target }}, ${{ matrix.shard }}/${{ matrix.shardTotal }}) needs: - download-previous-rolldown-binaries + - build-windows-cli strategy: fail-fast: false matrix: @@ -785,8 +1034,6 @@ jobs: # during `vp fmt --write` in the # `new-create-vite-migrates-eslint-prettier` snap test). env-mapping: | - CARGO_HOME,{{ DEV_DRIVE }}/.cargo - RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup TEMP,{{ DEV_DRIVE }}/Temp TMP,{{ DEV_DRIVE }}/Temp @@ -799,11 +1046,14 @@ jobs: shell: bash run: mkdir -p "$TEMP" "$TMP" + # Windows runs against the prebuilt binaries from build-windows-cli, + # so it needs no Rust toolchain here (the Dev Drive above stays for + # the TEMP/TMP routing). - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + if: runner.os != 'Windows' with: save-cache: ${{ github.ref_name == 'main' }} cache-key: cli-snap-test-${{ matrix.target }} - target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }} - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 @@ -816,10 +1066,18 @@ jobs: path: ./rolldown/packages/rolldown/src merge-multiple: true + # See the cli-e2e-test job for details on the prebuilt binaries. + - name: Download prebuilt Windows binaries + if: runner.os == 'Windows' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: windows-cli-binaries + - name: Build with upstream uses: ./.github/actions/build-upstream with: target: ${{ matrix.target }} + skip-native: ${{ runner.os == 'Windows' && 'true' || 'false' }} - name: Install Global CLI vp run: | @@ -987,6 +1245,7 @@ jobs: name: Local CLI `vp install` E2E test (Socket Firewall Free) needs: - download-previous-rolldown-binaries + - build-windows-cli # Run if: not a PR (push-to-main / workflow_dispatch), OR PR has 'test: sfw' label. # Heavy job (3 OSes × real registry traffic) — gated to avoid running on every PR. if: >- @@ -1024,21 +1283,13 @@ jobs: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - - name: Setup Dev Drive - if: runner.os == 'Windows' - uses: samypr100/setup-dev-drive@30f0f98ae5636b2b6501e181dfb3631b9974818d # v4.0.0 - with: - drive-size: 12GB - drive-format: ReFS - env-mapping: | - CARGO_HOME,{{ DEV_DRIVE }}/.cargo - RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup - + # Windows runs against the prebuilt binaries from build-windows-cli, + # so it needs no Rust toolchain or dev drive here. - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + if: runner.os != 'Windows' with: save-cache: ${{ github.ref_name == 'main' }} cache-key: install-e2e-test-sfw-${{ matrix.os }} - target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }} - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 @@ -1048,10 +1299,18 @@ jobs: path: ./rolldown/packages/rolldown/src merge-multiple: true + # See the cli-e2e-test job for details on the prebuilt binaries. + - name: Download prebuilt Windows binaries + if: runner.os == 'Windows' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: windows-cli-binaries + - name: Build with upstream uses: ./.github/actions/build-upstream with: target: ${{ matrix.target }} + skip-native: ${{ runner.os == 'Windows' && 'true' || 'false' }} - name: Build CLI run: | @@ -1113,6 +1372,9 @@ jobs: if: always() needs: - test + - build-windows-tests + - test-windows + - build-windows-cli - test-musl - lint - cli-e2e-test From 9b0508ff7b5c8bed925303f87eb74817cb367bbb Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 12 Jun 2026 23:25:43 +0800 Subject: [PATCH 2/7] ci: skip generated-binding formatting in the Windows cross build 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. --- .github/workflows/ci.yml | 5 ++++- packages/cli/build.ts | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f92dd7acb..631a2a7eb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -379,9 +379,12 @@ jobs: - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 if: steps.binaries-cache.outputs.cache-hit != 'true' + # --skip-format: formatting the generated index.cjs/index.d.cts needs + # the repo vite config, which imports the built vite-plus dist; this + # job skips the TS builds on purpose. - name: Build NAPI binding if: steps.binaries-cache.outputs.cache-hit != 'true' - run: pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x + run: pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x --skip-format env: DEBUG: 'napi:*' diff --git a/packages/cli/build.ts b/packages/cli/build.ts index cd395f3b2f..3b4d8aa54c 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -37,11 +37,12 @@ const TEST_PACKAGE_NAME = '@voidzero-dev/vite-plus-test'; const CORE_PACKAGE_NAME = '@voidzero-dev/vite-plus-core'; const { - values: { ['skip-native']: skipNative, ['skip-ts']: skipTs }, + values: { ['skip-native']: skipNative, ['skip-ts']: skipTs, ['skip-format']: skipFormat }, } = parseArgs({ options: { ['skip-native']: { type: 'boolean', default: false }, ['skip-ts']: { type: 'boolean', default: false }, + ['skip-format']: { type: 'boolean', default: false }, }, strict: false, }); @@ -49,7 +50,7 @@ const { // Filter out custom flags before passing to NAPI CLI const napiArgs = process.argv .slice(2) - .filter((arg) => arg !== '--skip-native' && arg !== '--skip-ts'); + .filter((arg) => arg !== '--skip-native' && arg !== '--skip-ts' && arg !== '--skip-format'); if (!skipTs) { buildWithTsdown(); @@ -101,6 +102,13 @@ async function buildNapiBinding() { }); const outputs = await task; + // --skip-format: importing the repo vite config pulls in the built + // vite-plus dist, which doesn't exist in CI jobs that cross-compile only + // the native binding (build-windows-cli); formatting the generated + // bindings is cosmetic there. + if (skipFormat) { + return; + } const viteConfig = await import('../../vite.config.js'); for (const output of outputs) { if (output.kind !== 'node') { From 7b520c86f974a9eb24276bc378ed447903508c10 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 12 Jun 2026 23:42:04 +0800 Subject: [PATCH 3/7] ci: ship only native binaries in the windows-cli-binaries artifact 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). --- .github/workflows/ci.yml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 631a2a7eb9..292f1e04a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -338,10 +338,6 @@ jobs: with: path: | packages/cli/binding/*.node - packages/cli/binding/index.js - packages/cli/binding/index.d.ts - packages/cli/binding/index.cjs - packages/cli/binding/index.d.cts target/x86_64-pc-windows-msvc/release/vp.exe target/x86_64-pc-windows-msvc/release/vp-shim.exe target/x86_64-pc-windows-msvc/release/vp-setup.exe @@ -398,13 +394,12 @@ jobs: # Both path roots are workspace-relative, so the artifact preserves # the repo layout and a plain download into the workspace places # every file where bootstrap-cli:ci (tool install-global-cli) - # expects it. + # expects it. The generated binding JS (index.cjs etc.) is NOT + # included: those files are committed, and the unformatted copies + # this job produces (--skip-format) must not overwrite them in the + # consumer checkouts (vp check would flag them). path: | packages/cli/binding/*.node - packages/cli/binding/index.js - packages/cli/binding/index.d.ts - packages/cli/binding/index.cjs - packages/cli/binding/index.d.cts target/x86_64-pc-windows-msvc/release/vp.exe target/x86_64-pc-windows-msvc/release/vp-shim.exe target/x86_64-pc-windows-msvc/release/vp-setup.exe @@ -416,10 +411,6 @@ jobs: with: path: | packages/cli/binding/*.node - packages/cli/binding/index.js - packages/cli/binding/index.d.ts - packages/cli/binding/index.cjs - packages/cli/binding/index.d.cts target/x86_64-pc-windows-msvc/release/vp.exe target/x86_64-pc-windows-msvc/release/vp-shim.exe target/x86_64-pc-windows-msvc/release/vp-setup.exe From 7ef815840e47616e551f5e8ab10266baf3d4da93 Mon Sep 17 00:00:00 2001 From: MK Date: Sat, 13 Jun 2026 14:50:06 +0800 Subject: [PATCH 4/7] refactor(ci): dedup the native-build guards and cache-key cross-refs - 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. --- .github/actions/build-upstream/action.yml | 35 +++++++++++++++-------- .github/workflows/ci.yml | 9 ++++-- packages/cli/build.ts | 5 ++-- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.github/actions/build-upstream/action.yml b/.github/actions/build-upstream/action.yml index ef45e1814c..7cd228627b 100644 --- a/.github/actions/build-upstream/action.yml +++ b/.github/actions/build-upstream/action.yml @@ -28,6 +28,8 @@ runs: # Compute cache key once before any builds modify files # (packages/cli/package.json is modified by syncTestPackageExports during build-ts) # Include env vars (RELEASE_BUILD, DEBUG, VERSION) to ensure cache miss on release builds + # Keep the hashFiles input set in sync with ci.yml's build-windows-cli + # "Compute binaries cache key" step. - name: Compute NAPI binding cache key id: cache-key shell: bash @@ -60,6 +62,15 @@ runs: ${{ steps.rust-target.outputs.dir }}/${{ inputs.target }}/release/vp-setup.exe key: ${{ steps.cache-key.outputs.key }} + # Single source of truth for "build the native binaries in this job": + # false when the caller supplied prebuilt ones (skip-native) or the cache + # already has them. Every native build step (and the cache save) gates on + # this one flag instead of repeating the two-part condition. + - name: Decide native build + id: native + shell: bash + run: echo "build=${{ inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' }}" >> "$GITHUB_OUTPUT" + # Apply Vite+ branding patches to vite source (CI checks out # upstream vite which doesn't have branding patches) - name: Brand vite @@ -77,14 +88,14 @@ runs: # Install zig + cargo-zigbuild for musl cross-compilation (napi-cross only supports gnu) - name: Add musl Rust target - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') shell: bash run: rustup target add ${INPUTS_TARGET} env: INPUTS_TARGET: ${{ inputs.target }} - name: Setup zig (musl) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 with: version: 0.15.2 @@ -95,7 +106,7 @@ runs: # the pinned git commit. TODO: revert to taiki-e/install-action once # cargo-zigbuild > 0.22.3 is released. - name: Install cargo-zigbuild (musl) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') shell: bash run: cargo install --locked --git https://github.com/rust-cross/cargo-zigbuild --rev 7e791b4be71b9870e0abccedf7885486803cd923 cargo-zigbuild @@ -103,7 +114,7 @@ runs: # Must run before vite-plus TypeScript builds which depend on the bindings - name: Build NAPI bindings (Linux gnu) shell: bash - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') run: | pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} --use-napi-cross env: @@ -114,7 +125,7 @@ runs: - name: Build NAPI bindings (Linux musl) shell: bash - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') run: | pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} -x env: @@ -125,7 +136,7 @@ runs: - name: Build NAPI bindings (non-Linux targets) shell: bash - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux') + if: steps.native.outputs.build == 'true' && !contains(inputs.target, 'linux') run: | pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} env: @@ -133,7 +144,7 @@ runs: INPUTS_TARGET: ${{ inputs.target }} - name: Build Rust CLI binary (Linux gnu) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') shell: bash run: | pnpm exec napi build --use-napi-cross --target ${INPUTS_TARGET} --release -p vite_global_cli @@ -144,7 +155,7 @@ runs: INPUTS_TARGET: ${{ inputs.target }} - name: Build Rust CLI binary (Linux musl) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') shell: bash run: | pnpm exec napi build -x --target ${INPUTS_TARGET} --release -p vite_global_cli @@ -155,28 +166,28 @@ runs: INPUTS_TARGET: ${{ inputs.target }} - name: Build Rust CLI binary (non-Linux targets) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux') + if: steps.native.outputs.build == 'true' && !contains(inputs.target, 'linux') shell: bash run: cargo build --release --target ${INPUTS_TARGET} -p vite_global_cli env: INPUTS_TARGET: ${{ inputs.target }} - name: Build trampoline shim binary (Windows only) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'windows') shell: bash run: cargo build --release --target ${INPUTS_TARGET} -p vite_trampoline env: INPUTS_TARGET: ${{ inputs.target }} - name: Build installer binary (Windows only) - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows') + if: steps.native.outputs.build == 'true' && contains(inputs.target, 'windows') shell: bash run: cargo build --release --target ${INPUTS_TARGET} -p vite_installer env: INPUTS_TARGET: ${{ inputs.target }} - name: Save NAPI binding cache - if: inputs.skip-native != 'true' && steps.cache-restore.outputs.cache-hit != 'true' + if: steps.native.outputs.build == 'true' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 292f1e04a4..5191165536 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -324,9 +324,12 @@ jobs: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - # Same input set as build-upstream's NAPI binding cache key, but under - # a distinct prefix: this cache stores Linux-relative paths and must - # never mix with the caches build-upstream saves on other runners. + # Must use the same hashFiles input set as build-upstream's "Compute + # NAPI binding cache key" step (keep the two lists in sync, or a stale + # cross-built artifact is served on a cache that should have missed), + # but under a distinct prefix: this cache stores Linux-relative paths + # and must never mix with the caches build-upstream saves on other + # runners. - name: Compute binaries cache key id: cache-key run: | diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 3b4d8aa54c..e8eafcf6e9 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -48,9 +48,8 @@ const { }); // Filter out custom flags before passing to NAPI CLI -const napiArgs = process.argv - .slice(2) - .filter((arg) => arg !== '--skip-native' && arg !== '--skip-ts' && arg !== '--skip-format'); +const customFlags = new Set(['--skip-native', '--skip-ts', '--skip-format']); +const napiArgs = process.argv.slice(2).filter((arg) => !customFlags.has(arg)); if (!skipTs) { buildWithTsdown(); From 790571de28e76c5270565cbfeca19ad63d25d9f1 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 15 Jun 2026 14:42:14 +0800 Subject: [PATCH 5/7] ci: cross-compile e2e-test Windows packages on Linux; share setup-xwin The e2e-test build job's windows-latest entry built the NAPI binding + vp binaries natively (~24m on cache miss); the e2e-test run job was already artifact-only. Cross-compile that build on Linux with cargo-xwin the same way ci.yml does, keeping only the run job on Windows. Extract the xwin toolchain setup (setup-rust + llvm-tools, rustup target, cargo-xwin install, MSVC SDK cache) into a shared .github/actions/setup-xwin composite, now used by build-windows-tests, build-windows-cli, and the e2e-test build, instead of a third copy. The composite uses the combined actions/cache (restore + post-save) for the SDK, replacing the split restore/save pairs. --- .github/actions/setup-xwin/action.yml | 48 +++++++++++++++++++++ .github/workflows/ci.yml | 60 ++------------------------- .github/workflows/e2e-test.yml | 43 +++++++++++++++---- 3 files changed, 86 insertions(+), 65 deletions(-) create mode 100644 .github/actions/setup-xwin/action.yml diff --git a/.github/actions/setup-xwin/action.yml b/.github/actions/setup-xwin/action.yml new file mode 100644 index 0000000000..b944b47c6a --- /dev/null +++ b/.github/actions/setup-xwin/action.yml @@ -0,0 +1,48 @@ +name: 'Setup cargo-xwin' +description: >- + Install the Rust toolchain and cargo-xwin for cross-compiling to + x86_64-pc-windows-msvc on Linux, with the MSVC CRT / Windows SDK cached. + +inputs: + cache-key: + description: 'setup-rust cache-key prefix; pass a distinct value per caller.' + required: true + save-cache: + description: 'Whether setup-rust should save its cache (usually only on main).' + required: false + default: 'false' + tools: + description: >- + Extra comma-separated taiki-e/install-action tools to install alongside + cargo-xwin (e.g. "cargo-nextest"). + required: false + default: '' + +runs: + using: 'composite' + steps: + # 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 + # C/C++ deps) and lld-link from there when the runner image has no matching + # system LLVM. + - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + with: + save-cache: ${{ inputs.save-cache }} + cache-key: ${{ inputs.cache-key }} + components: llvm-tools + + - run: rustup target add x86_64-pc-windows-msvc + shell: bash + + - uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10 + with: + tool: ${{ inputs.tools != '' && format('{0},cargo-xwin', inputs.tools) || 'cargo-xwin' }} + + # Downloading and unpacking the MSVC CRT/Windows SDK dominates cold wall + # time (~10 minutes); the unpacked result is immutable for a given manifest + # version, so cache it (combined restore + post-job save). Bump the key when + # bumping cargo-xwin. + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ~/.cache/cargo-xwin + key: cargo-xwin-msvc-17 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5191165536..fa2a47d9ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,30 +118,11 @@ jobs: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - # 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 C/C++ deps) and lld-link from there when the runner - # image has no matching system LLVM. - - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + - uses: ./.github/actions/setup-xwin 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 cold - # wall time (~10 minutes); the unpacked result is immutable for a given - # manifest version, so cache it. Bump the key when bumping cargo-xwin. - - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - id: xwin-cache - with: - path: ~/.cache/cargo-xwin - key: cargo-xwin-msvc-17 + tools: cargo-nextest # `cargo xwin env` resolves the rustflags configured in # .cargo/config.toml (including the cfg-based Windows section), appends @@ -183,12 +164,6 @@ jobs: 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) @@ -346,34 +321,11 @@ jobs: target/x86_64-pc-windows-msvc/release/vp-setup.exe key: ${{ steps.cache-key.outputs.key }} - # llvm-tools provides llvm-ar in the toolchain's rustlib bin dir; - # cargo-xwin symlinks the MSVC-style llvm-lib/llvm-dlltool and lld-link - # from there when the runner image has no matching system LLVM. - - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + - uses: ./.github/actions/setup-xwin if: steps.binaries-cache.outputs.cache-hit != 'true' with: save-cache: ${{ github.ref_name == 'main' }} cache-key: windows-cli-cross - components: llvm-tools - - - run: rustup target add x86_64-pc-windows-msvc - if: steps.binaries-cache.outputs.cache-hit != 'true' - - # Preinstall cargo-xwin so `napi build -x` (which shells out to - # `cargo xwin build` for MSVC targets) doesn't fall back to a slow - # `cargo install` of it. - - uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10 - if: steps.binaries-cache.outputs.cache-hit != 'true' - with: - tool: cargo-xwin - - # Shared with build-windows-tests; see that job for details. - - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - if: steps.binaries-cache.outputs.cache-hit != 'true' - id: xwin-cache - with: - path: ~/.cache/cargo-xwin - key: cargo-xwin-msvc-17 - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 if: steps.binaries-cache.outputs.cache-hit != 'true' @@ -419,12 +371,6 @@ jobs: target/x86_64-pc-windows-msvc/release/vp-setup.exe key: ${{ steps.cache-key.outputs.key }} - - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - if: steps.binaries-cache.outputs.cache-hit != 'true' && steps.xwin-cache.outputs.cache-hit != 'true' - with: - path: ~/.cache/cargo-xwin - key: cargo-xwin-msvc-17 - cli-e2e-test: name: CLI E2E test needs: diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 6bcd13c694..afdc180fdc 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -63,36 +63,47 @@ jobs: build: name: Build vite-plus packages (${{ matrix.os }}) - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runner }} permissions: contents: read packages: read needs: - download-previous-rolldown-binaries + env: + XWIN_ACCEPT_LICENSE: '1' + # See ci.yml's build-windows-tests for why this Detours escape hatch is + # needed (harmless for the native Linux entry). + CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH strategy: fail-fast: false matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu + runner: ubuntu-latest + # The Windows packages are cross-compiled on Linux with cargo-xwin; + # only the e2e-test run job below needs a real Windows runner. `os` + # stays windows-latest so the artifact name (consumed by e2e-test) + # is unchanged. - os: windows-latest target: x86_64-pc-windows-msvc + runner: namespace-profile-linux-x64-default steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - # Disable Windows Defender real-time scanning to speed up I/O-heavy builds (~30-50% faster) - - name: Disable Windows Defender - if: runner.os == 'Windows' - continue-on-error: true - shell: powershell - run: Set-MpPreference -DisableRealtimeMonitoring $true - - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + if: ${{ !contains(matrix.target, 'windows') }} with: save-cache: ${{ github.ref_name == 'main' }} cache-key: e2e-build-${{ matrix.os }} + - uses: ./.github/actions/setup-xwin + if: contains(matrix.target, 'windows') + with: + save-cache: ${{ github.ref_name == 'main' }} + cache-key: e2e-windows-cross + - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -101,10 +112,26 @@ jobs: path: ./rolldown/packages/rolldown/src merge-multiple: true + # Cross-compile the Windows binding + vp/vp-shim binaries on Linux before + # build-upstream's skip-native pass packs them. `napi build -x` and + # `cargo xwin build` drive cargo-xwin directly, so no `cargo xwin env` + # eval is needed. --skip-format skips formatting the generated + # index.cjs/index.d.cts (which would need the not-yet-built vite-plus + # dist); the unformatted loader is harmless in the e2e tgz since these + # runs never lint vite-plus's own binding. + - name: Cross-build Windows binaries + if: contains(matrix.target, 'windows') + env: + DEBUG: 'napi:*' + run: | + pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x --skip-format + cargo xwin build --release --target x86_64-pc-windows-msvc -p vite_global_cli -p vite_trampoline + - name: Build with upstream uses: ./.github/actions/build-upstream with: target: ${{ matrix.target }} + skip-native: ${{ contains(matrix.target, 'windows') && 'true' || 'false' }} - name: Pack packages into tgz run: | From d2d1f41fd6d90d6cb125ad86745d8282b70b1154 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 15 Jun 2026 14:59:25 +0800 Subject: [PATCH 6/7] ci: revert e2e-test Windows cross-build (keep native build) Moving the whole e2e-test Windows build to Linux is infeasible: in non-release CI builds @voidzero-dev/vite-plus-core bundles the rolldown native binding for the *host* platform, so building on Linux ships the Linux rolldown binding in the Windows tgz and every Windows e2e job fails at config load (rolldown returns null -> "Cannot convert undefined or null to object"). ci.yml's Phase 2 avoids this because its Windows jobs still run core's bundling on Windows; only the vite-plus .node is cross-built. Revert the e2e build job to the native Windows build. The setup-xwin composite and the ci.yml refactor (both green) stay. --- .github/workflows/e2e-test.yml | 43 +++++++--------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index afdc180fdc..6bcd13c694 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -63,47 +63,36 @@ jobs: build: name: Build vite-plus packages (${{ matrix.os }}) - runs-on: ${{ matrix.runner }} + runs-on: ${{ matrix.os }} permissions: contents: read packages: read needs: - download-previous-rolldown-binaries - env: - XWIN_ACCEPT_LICENSE: '1' - # See ci.yml's build-windows-tests for why this Detours escape hatch is - # needed (harmless for the native Linux entry). - CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH strategy: fail-fast: false matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu - runner: ubuntu-latest - # The Windows packages are cross-compiled on Linux with cargo-xwin; - # only the e2e-test run job below needs a real Windows runner. `os` - # stays windows-latest so the artifact name (consumed by e2e-test) - # is unchanged. - os: windows-latest target: x86_64-pc-windows-msvc - runner: namespace-profile-linux-x64-default steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone + # Disable Windows Defender real-time scanning to speed up I/O-heavy builds (~30-50% faster) + - name: Disable Windows Defender + if: runner.os == 'Windows' + continue-on-error: true + shell: powershell + run: Set-MpPreference -DisableRealtimeMonitoring $true + - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) - if: ${{ !contains(matrix.target, 'windows') }} with: save-cache: ${{ github.ref_name == 'main' }} cache-key: e2e-build-${{ matrix.os }} - - uses: ./.github/actions/setup-xwin - if: contains(matrix.target, 'windows') - with: - save-cache: ${{ github.ref_name == 'main' }} - cache-key: e2e-windows-cross - - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -112,26 +101,10 @@ jobs: path: ./rolldown/packages/rolldown/src merge-multiple: true - # Cross-compile the Windows binding + vp/vp-shim binaries on Linux before - # build-upstream's skip-native pass packs them. `napi build -x` and - # `cargo xwin build` drive cargo-xwin directly, so no `cargo xwin env` - # eval is needed. --skip-format skips formatting the generated - # index.cjs/index.d.cts (which would need the not-yet-built vite-plus - # dist); the unformatted loader is harmless in the e2e tgz since these - # runs never lint vite-plus's own binding. - - name: Cross-build Windows binaries - if: contains(matrix.target, 'windows') - env: - DEBUG: 'napi:*' - run: | - pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x --skip-format - cargo xwin build --release --target x86_64-pc-windows-msvc -p vite_global_cli -p vite_trampoline - - name: Build with upstream uses: ./.github/actions/build-upstream with: target: ${{ matrix.target }} - skip-native: ${{ contains(matrix.target, 'windows') && 'true' || 'false' }} - name: Pack packages into tgz run: | From d163092aaf1ccdd136403be15f664ace82088c0a Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 15 Jun 2026 15:34:24 +0800 Subject: [PATCH 7/7] ci: cross-build e2e-test Windows binaries on Linux (Option A) Extract the Windows cross-build into a .github/actions/build-windows-cli composite (compute cache key, restore, setup-xwin, cross-build binding + vp/vp-shim/vp-setup, upload artifact, save cache) so ci.yml and e2e-test.yml share one implementation and one content-hash cache key. e2e-test gains a build-windows-cli job that produces the artifact; its build job's windows-latest entry downloads it and runs build-upstream with skip-native, so the ~24m native Rust compile moves to Linux while core/rolldown bundling still runs on Windows (it is host-platform specific, which is why the all-Linux approach failed). --- .github/actions/build-windows-cli/action.yml | 101 +++++++++++++++++++ .github/workflows/ci.yml | 75 +------------- .github/workflows/e2e-test.yml | 38 +++++++ 3 files changed, 140 insertions(+), 74 deletions(-) create mode 100644 .github/actions/build-windows-cli/action.yml diff --git a/.github/actions/build-windows-cli/action.yml b/.github/actions/build-windows-cli/action.yml new file mode 100644 index 0000000000..bf55307250 --- /dev/null +++ b/.github/actions/build-windows-cli/action.yml @@ -0,0 +1,101 @@ +name: 'Build Windows CLI binaries' +description: >- + Cross-compile the Windows release NAPI binding and vp/vp-shim/vp-setup + binaries on Linux with cargo-xwin, cache them by source hash, and upload + them as an artifact for Windows jobs to consume (with build-upstream's + skip-native input). Run on a Linux runner after checkout + clone. + +inputs: + save-cache: + description: "Whether setup-xwin's setup-rust should save its cache (usually only on main)." + required: false + default: 'false' + artifact-name: + description: 'Name for the uploaded binaries artifact.' + required: false + default: 'windows-cli-binaries' + +runs: + using: 'composite' + steps: + # Must use the same hashFiles input set as build-upstream's "Compute NAPI + # binding cache key" step (keep the lists in sync, or a stale cross-built + # artifact is served on a cache that should have missed), but under a + # distinct prefix: this cache stores Linux-relative paths and must never + # mix with the caches build-upstream saves on other runners. Sharing this + # one key across ci.yml and e2e-test.yml lets whichever workflow builds + # first serve the other. + - name: Compute binaries cache key + id: cache-key + shell: bash + run: | + echo "key=windows-cli-xwin-v1-${{ hashFiles('packages/tools/.upstream-versions.json', 'rust-toolchain.toml', 'Cargo.lock', 'crates/**/*.rs', 'crates/*/Cargo.toml', 'packages/cli/binding/**/*.rs', 'packages/cli/binding/Cargo.toml', 'Cargo.toml', '.cargo/config.toml', 'packages/cli/package.json', 'packages/cli/build.ts', 'packages/cli/tsdown.config.ts') }}" >> "$GITHUB_OUTPUT" + + - name: Restore binaries cache + id: binaries-cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + packages/cli/binding/*.node + target/x86_64-pc-windows-msvc/release/vp.exe + target/x86_64-pc-windows-msvc/release/vp-shim.exe + target/x86_64-pc-windows-msvc/release/vp-setup.exe + key: ${{ steps.cache-key.outputs.key }} + + - uses: ./.github/actions/setup-xwin + if: steps.binaries-cache.outputs.cache-hit != 'true' + with: + save-cache: ${{ inputs.save-cache }} + cache-key: windows-cli-cross + + - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 + if: steps.binaries-cache.outputs.cache-hit != 'true' + + # --skip-format: formatting the generated index.cjs/index.d.cts needs the + # repo vite config, which imports the not-yet-built vite-plus dist; this + # action skips the TS builds on purpose. + - name: Build NAPI binding + if: steps.binaries-cache.outputs.cache-hit != 'true' + shell: bash + run: pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x --skip-format + env: + DEBUG: 'napi:*' + XWIN_ACCEPT_LICENSE: '1' + # See setup-xwin / build-windows-tests for why this Detours escape + # hatch is needed. + CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH + + - name: Build Rust CLI binaries + if: steps.binaries-cache.outputs.cache-hit != 'true' + shell: bash + run: cargo xwin build --release --target x86_64-pc-windows-msvc -p vite_global_cli -p vite_trampoline -p vite_installer + env: + XWIN_ACCEPT_LICENSE: '1' + CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ inputs.artifact-name }} + # Both path roots are workspace-relative, so the artifact preserves the + # repo layout and a plain download into the workspace places every file + # where tool install-global-cli expects it. The generated binding JS + # (index.cjs etc.) is NOT included: those files are committed, and the + # unformatted copies this action produces (--skip-format) must not + # overwrite them in consumer checkouts (vp check would flag them). + path: | + packages/cli/binding/*.node + target/x86_64-pc-windows-msvc/release/vp.exe + target/x86_64-pc-windows-msvc/release/vp-shim.exe + target/x86_64-pc-windows-msvc/release/vp-setup.exe + if-no-files-found: error + retention-days: 7 + + - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + if: steps.binaries-cache.outputs.cache-hit != 'true' + with: + path: | + packages/cli/binding/*.node + target/x86_64-pc-windows-msvc/release/vp.exe + target/x86_64-pc-windows-msvc/release/vp-shim.exe + target/x86_64-pc-windows-msvc/release/vp-setup.exe + key: ${{ steps.cache-key.outputs.key }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa2a47d9ca..841a8bc056 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -291,85 +291,12 @@ jobs: if: needs.detect-changes.outputs.code-changed == 'true' name: Build Windows CLI binaries runs-on: namespace-profile-linux-x64-default - env: - XWIN_ACCEPT_LICENSE: '1' - # See build-windows-tests for why this Detours escape hatch is needed. - CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - - # Must use the same hashFiles input set as build-upstream's "Compute - # NAPI binding cache key" step (keep the two lists in sync, or a stale - # cross-built artifact is served on a cache that should have missed), - # but under a distinct prefix: this cache stores Linux-relative paths - # and must never mix with the caches build-upstream saves on other - # runners. - - name: Compute binaries cache key - id: cache-key - run: | - echo "key=windows-cli-xwin-v1-${{ hashFiles('packages/tools/.upstream-versions.json', 'rust-toolchain.toml', 'Cargo.lock', 'crates/**/*.rs', 'crates/*/Cargo.toml', 'packages/cli/binding/**/*.rs', 'packages/cli/binding/Cargo.toml', 'Cargo.toml', '.cargo/config.toml', 'packages/cli/package.json', 'packages/cli/build.ts', 'packages/cli/tsdown.config.ts') }}" >> $GITHUB_OUTPUT - - - name: Restore binaries cache - id: binaries-cache - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: | - packages/cli/binding/*.node - target/x86_64-pc-windows-msvc/release/vp.exe - target/x86_64-pc-windows-msvc/release/vp-shim.exe - target/x86_64-pc-windows-msvc/release/vp-setup.exe - key: ${{ steps.cache-key.outputs.key }} - - - uses: ./.github/actions/setup-xwin - if: steps.binaries-cache.outputs.cache-hit != 'true' + - uses: ./.github/actions/build-windows-cli with: save-cache: ${{ github.ref_name == 'main' }} - cache-key: windows-cli-cross - - - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 - if: steps.binaries-cache.outputs.cache-hit != 'true' - - # --skip-format: formatting the generated index.cjs/index.d.cts needs - # the repo vite config, which imports the built vite-plus dist; this - # job skips the TS builds on purpose. - - name: Build NAPI binding - if: steps.binaries-cache.outputs.cache-hit != 'true' - run: pnpm --filter=vite-plus build-native --target x86_64-pc-windows-msvc -x --skip-format - env: - DEBUG: 'napi:*' - - - name: Build Rust CLI binaries - if: steps.binaries-cache.outputs.cache-hit != 'true' - run: cargo xwin build --release --target x86_64-pc-windows-msvc -p vite_global_cli -p vite_trampoline -p vite_installer - - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: windows-cli-binaries - # Both path roots are workspace-relative, so the artifact preserves - # the repo layout and a plain download into the workspace places - # every file where bootstrap-cli:ci (tool install-global-cli) - # expects it. The generated binding JS (index.cjs etc.) is NOT - # included: those files are committed, and the unformatted copies - # this job produces (--skip-format) must not overwrite them in the - # consumer checkouts (vp check would flag them). - path: | - packages/cli/binding/*.node - target/x86_64-pc-windows-msvc/release/vp.exe - target/x86_64-pc-windows-msvc/release/vp-shim.exe - target/x86_64-pc-windows-msvc/release/vp-setup.exe - if-no-files-found: error - retention-days: 7 - - - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - if: steps.binaries-cache.outputs.cache-hit != 'true' - with: - path: | - packages/cli/binding/*.node - target/x86_64-pc-windows-msvc/release/vp.exe - target/x86_64-pc-windows-msvc/release/vp-shim.exe - target/x86_64-pc-windows-msvc/release/vp-setup.exe - key: ${{ steps.cache-key.outputs.key }} cli-e2e-test: name: CLI E2E test diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 6bcd13c694..b59afb1929 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -61,6 +61,31 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} + # Cross-compile the Windows vite-plus binding + vp binaries on Linux so the + # build job's windows-latest entry only runs the (fast) JS/pack pass with + # skip-native, instead of the ~24m native Rust compile. The core/rolldown + # bundling stays on Windows (it is host-platform specific), so this can't be + # folded into the build job. Shares ci.yml's windows-cli-xwin cache. + build-windows-cli: + needs: detect-changes + if: >- + github.event_name != 'pull_request' || + contains(github.event.pull_request.labels.*.name, 'test: e2e') || + github.head_ref == 'deps/upstream-update' || + startsWith(github.head_ref, 'renovate/') || + needs.detect-changes.outputs.related-files-changed == 'true' + name: Build Windows CLI binaries + runs-on: namespace-profile-linux-x64-default + permissions: + contents: read + packages: read + steps: + - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 + - uses: ./.github/actions/clone + - uses: ./.github/actions/build-windows-cli + with: + save-cache: ${{ github.ref_name == 'main' }} + build: name: Build vite-plus packages (${{ matrix.os }}) runs-on: ${{ matrix.os }} @@ -69,6 +94,7 @@ jobs: packages: read needs: - download-previous-rolldown-binaries + - build-windows-cli strategy: fail-fast: false matrix: @@ -88,7 +114,10 @@ jobs: shell: powershell run: Set-MpPreference -DisableRealtimeMonitoring $true + # Windows builds against the prebuilt binaries from build-windows-cli, so + # it needs no Rust toolchain here. - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) + if: ${{ !contains(matrix.target, 'windows') }} with: save-cache: ${{ github.ref_name == 'main' }} cache-key: e2e-build-${{ matrix.os }} @@ -101,10 +130,19 @@ jobs: path: ./rolldown/packages/rolldown/src merge-multiple: true + # Prebuilt by build-windows-cli (Linux + cargo-xwin); placed where + # build-upstream's skip-native pass and the pack step expect them. + - name: Download prebuilt Windows binaries + if: contains(matrix.target, 'windows') + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: windows-cli-binaries + - name: Build with upstream uses: ./.github/actions/build-upstream with: target: ${{ matrix.target }} + skip-native: ${{ contains(matrix.target, 'windows') && 'true' || 'false' }} - name: Pack packages into tgz run: |