Problem
The Build CLI step in the CLI E2E test (windows-latest) job takes ~5 minutes, significantly slower than Linux (~2 min) and macOS (~1 min).
Example run: https://github.com/voidzero-dev/vite-plus/actions/runs/22795306880/job/66128870036
Step timing comparison (CLI E2E test)
| Step |
Windows |
Ubuntu |
macOS |
| setup-rust |
1m 5s |
29s |
11s |
| setup-node |
1m 44s |
25s |
15s |
| Build with upstream |
1m 12s (cache hit) |
44s |
16s |
| Build CLI |
5m 12s |
1m 49s |
58s |
| Total job |
~14 min |
~8 min |
~4 min |
Root Cause Analysis
1. Redundant NAPI Rust compilation (primary bottleneck)
The Build CLI step runs pnpm build, which calls:
pnpm -F vite-plus-cli build → oxnode -C dev ./build.ts
build.ts runs buildNapiBinding() by default (no --skip-native flag), triggering a full cargo build --release of the NAPI binding — even though Build with upstream already compiled the same NAPI bindings (with caching via actions/cache).
The Build with upstream action correctly uses build-ts (which passes --skip-native) for the TypeScript parts and separately handles native builds with caching. But the subsequent pnpm build in Build CLI re-compiles everything.
Cargo release build of 9 workspace crates (same code, all NAPI cache hit):
| Platform |
Time |
Relative |
| macOS (Apple Silicon) |
49s |
1× |
| Ubuntu (x86_64) |
1m 24s |
1.7× |
| Windows (x86_64, MSVC) |
3m 10s |
3.9× |
2. Windows MSVC is inherently slower for Rust compilation
- MSVC linker (
link.exe) is single-threaded and slower than lld/macOS linker (PDB generation overhead)
- NTFS file I/O has higher per-operation latency than ext4/APFS for the many small files cargo generates
- Windows Defender real-time scanning on GitHub Actions runners scans every file read/write, adding 30-50% overhead
- Process spawning —
CreateProcess is ~10× slower than Unix fork/exec; cargo spawns many rustc processes
3. Slower toolchain setup on Windows
setup-node (pnpm install): 1m 44s vs 25s on Linux (NTFS I/O + antivirus overhead)
setup-rust: 1m 5s vs 29s on Linux
Proposed Solutions
1. Skip native rebuild in Build CLI step (quick fix, biggest impact)
Change the Build CLI step from:
- name: Build CLI
run: |
pnpm build
pnpm bootstrap-cli:ci
To:
- name: Build CLI
run: |
pnpm build-ts-only # or: pnpm -F @voidzero-dev/* -F vite-plus build && pnpm -F vite-plus-cli build-ts
pnpm bootstrap-cli:ci
Since Build with upstream already handles the NAPI bindings with proper caching, the Build CLI step only needs to do the TypeScript build (build-ts / --skip-native).
Expected savings: ~3 min on Windows, ~1-2 min on Ubuntu
2. Disable Windows Defender real-time scanning
Add this step before any build steps on Windows:
- name: Disable Windows Defender real-time monitoring
if: runner.os == 'Windows'
run: Set-ExecutionPolicy Bypass -Scope Process -Force; Set-MpPreference -DisableRealtimeMonitoring $true
shell: powershell
GitHub-hosted Windows runners allow this without admin elevation. This can reduce I/O-heavy operations (cargo, pnpm install) by 30-50%.
3. Additional optimizations (lower priority)
- Add
sccache for Windows Rust builds when NAPI cache misses
- Consider
cargo build --profile ci: a custom profile with fewer optimizations for faster compile time in CI
Problem
The
Build CLIstep in the CLI E2E test (windows-latest) job takes ~5 minutes, significantly slower than Linux (~2 min) and macOS (~1 min).Example run: https://github.com/voidzero-dev/vite-plus/actions/runs/22795306880/job/66128870036
Step timing comparison (CLI E2E test)
Root Cause Analysis
1. Redundant NAPI Rust compilation (primary bottleneck)
The
Build CLIstep runspnpm build, which calls:build.tsrunsbuildNapiBinding()by default (no--skip-nativeflag), triggering a fullcargo build --releaseof the NAPI binding — even thoughBuild with upstreamalready compiled the same NAPI bindings (with caching viaactions/cache).The
Build with upstreamaction correctly usesbuild-ts(which passes--skip-native) for the TypeScript parts and separately handles native builds with caching. But the subsequentpnpm buildinBuild CLIre-compiles everything.Cargo release build of 9 workspace crates (same code, all NAPI cache hit):
2. Windows MSVC is inherently slower for Rust compilation
link.exe) is single-threaded and slower thanlld/macOS linker (PDB generation overhead)CreateProcessis ~10× slower than Unixfork/exec; cargo spawns manyrustcprocesses3. Slower toolchain setup on Windows
setup-node(pnpm install): 1m 44s vs 25s on Linux (NTFS I/O + antivirus overhead)setup-rust: 1m 5s vs 29s on LinuxProposed Solutions
1. Skip native rebuild in Build CLI step (quick fix, biggest impact)
Change the
Build CLIstep from:To:
Since
Build with upstreamalready handles the NAPI bindings with proper caching, theBuild CLIstep only needs to do the TypeScript build (build-ts/--skip-native).Expected savings: ~3 min on Windows, ~1-2 min on Ubuntu
2. Disable Windows Defender real-time scanning
Add this step before any build steps on Windows:
GitHub-hosted Windows runners allow this without admin elevation. This can reduce I/O-heavy operations (cargo, pnpm install) by 30-50%.
3. Additional optimizations (lower priority)
sccachefor Windows Rust builds when NAPI cache missescargo build --profile ci: a custom profile with fewer optimizations for faster compile time in CI