From c7f7d6e7a72026307d0b66a3b1ce298c531aa8a6 Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 13 Oct 2026 20:21:58 +0800 Subject: [PATCH 1/2] perf(ci): use ThinLTO for native test builds --- .github/actions/build-upstream/action.yml | 6 +++++- .github/actions/build-windows-cli/action.yml | 6 +++++- .../action.yml | 4 +++- .github/workflows/ci.yml | 20 +++++++++++++++++++ .github/workflows/e2e-test.yml | 5 +++++ .github/workflows/reusable-ecosystem-ci.yml | 5 +++++ .github/workflows/test-vp-create.yml | 5 +++++ 7 files changed, 48 insertions(+), 3 deletions(-) diff --git a/.github/actions/build-upstream/action.yml b/.github/actions/build-upstream/action.yml index 81a6c65a6c..4eb90ea298 100644 --- a/.github/actions/build-upstream/action.yml +++ b/.github/actions/build-upstream/action.yml @@ -184,7 +184,11 @@ runs: - name: Build trampoline shim binary (Windows only) if: steps.native.outputs.build == 'true' && contains(inputs.target, 'windows') shell: bash - run: node packages/tools/src/build-trampoline.ts --release --target "${INPUTS_TARGET}" + # The standalone trampoline keeps its own size-optimized Cargo profile. + run: | + env -u CARGO_PROFILE_RELEASE_LTO -u CARGO_PROFILE_RELEASE_CODEGEN_UNITS \ + -u CARGO_PROFILE_RELEASE_OPT_LEVEL \ + node packages/tools/src/build-trampoline.ts --release --target "${INPUTS_TARGET}" env: INPUTS_TARGET: ${{ inputs.target }} diff --git a/.github/actions/build-windows-cli/action.yml b/.github/actions/build-windows-cli/action.yml index 5689767dbf..bbcfa33441 100644 --- a/.github/actions/build-windows-cli/action.yml +++ b/.github/actions/build-windows-cli/action.yml @@ -91,7 +91,11 @@ runs: - name: Build trampoline shim binary if: steps.binaries-cache.outputs.cache-hit != 'true' shell: bash - run: node packages/tools/src/build-trampoline.ts --xwin --release --target x86_64-pc-windows-msvc + # The standalone trampoline keeps its own size-optimized Cargo profile. + run: | + env -u CARGO_PROFILE_RELEASE_LTO -u CARGO_PROFILE_RELEASE_CODEGEN_UNITS \ + -u CARGO_PROFILE_RELEASE_OPT_LEVEL \ + node packages/tools/src/build-trampoline.ts --xwin --release --target x86_64-pc-windows-msvc env: XWIN_ACCEPT_LICENSE: '1' diff --git a/.github/actions/compute-native-cache-input-hash/action.yml b/.github/actions/compute-native-cache-input-hash/action.yml index e0e0ba9a62..0f18c86c16 100644 --- a/.github/actions/compute-native-cache-input-hash/action.yml +++ b/.github/actions/compute-native-cache-input-hash/action.yml @@ -13,7 +13,9 @@ runs: id: compute shell: bash run: | - echo "hash=$NATIVE_CACHE_INPUT_HASH" >> "$GITHUB_OUTPUT" + # Test builds and published builds must never share native binaries. + profile="${CARGO_PROFILE_RELEASE_LTO:-manifest}-${CARGO_PROFILE_RELEASE_CODEGEN_UNITS:-manifest}-${CARGO_PROFILE_RELEASE_OPT_LEVEL:-manifest}" + echo "hash=${NATIVE_CACHE_INPUT_HASH}-${profile}" >> "$GITHUB_OUTPUT" env: # crates/vp_cli_snapshots is excluded: the snapshot suite (runner, # vpt, fixtures, recorded .md baselines) never feeds the native diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d46a9fe600..a304c65cfe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,11 @@ jobs: permissions: contents: read packages: read + # Use Oxc's faster compilation settings for native test builds. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -373,6 +378,11 @@ jobs: if: needs.detect-changes.outputs.code-changed == 'true' name: Build Windows CLI binaries runs-on: namespace-profile-linux-x64-default + # Use Oxc's faster compilation settings for native test builds. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -946,6 +956,11 @@ jobs: - os: namespace-profile-mac-default target: aarch64-apple-darwin runs-on: ${{ matrix.os }} + # Use Oxc's faster compilation settings for native test builds. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -1301,6 +1316,11 @@ jobs: needs: - download-previous-rolldown-binaries runs-on: namespace-profile-linux-x64-default + # Use Oxc's faster compilation settings for native test builds. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 2c1465fed1..d047dc6736 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -80,6 +80,11 @@ jobs: permissions: contents: read packages: read + # Use Oxc's faster compilation settings for native test builds. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone diff --git a/.github/workflows/reusable-ecosystem-ci.yml b/.github/workflows/reusable-ecosystem-ci.yml index 097aa76cc6..4ef8c6a0f8 100644 --- a/.github/workflows/reusable-ecosystem-ci.yml +++ b/.github/workflows/reusable-ecosystem-ci.yml @@ -29,6 +29,11 @@ jobs: permissions: contents: read packages: read + # Use Oxc's faster compilation settings for native test builds. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 1e4dc124cf..82e788204c 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -65,6 +65,11 @@ jobs: build: name: Build vite-plus packages runs-on: namespace-profile-linux-x64-default + # Match the CLI build profile to reuse its native and Cargo caches. + env: + CARGO_PROFILE_RELEASE_LTO: 'thin' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' permissions: contents: read packages: read From c206ebe4ce6dea088d132ae73d62e7421e17e95f Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 13 Oct 2026 21:30:33 +0800 Subject: [PATCH 2/2] refactor(ci): define native build profile in Cargo.toml --- .github/actions/build-upstream/action.yml | 49 ++++++++--- .github/actions/build-windows-cli/action.yml | 31 +++++-- .../action.yml | 12 ++- .github/workflows/ci.yml | 30 ++----- .github/workflows/e2e-test.yml | 6 +- .github/workflows/reusable-ecosystem-ci.yml | 10 +-- .github/workflows/test-vp-create.yml | 10 +-- Cargo.toml | 7 ++ packages/cli/build.ts | 2 +- .../__tests__/native-build-profile.spec.ts | 87 +++++++++++++++++++ 10 files changed, 177 insertions(+), 67 deletions(-) create mode 100644 packages/tools/src/__tests__/native-build-profile.spec.ts diff --git a/.github/actions/build-upstream/action.yml b/.github/actions/build-upstream/action.yml index 4eb90ea298..78fefda286 100644 --- a/.github/actions/build-upstream/action.yml +++ b/.github/actions/build-upstream/action.yml @@ -1,6 +1,10 @@ name: 'Build with Upstream Repositories' description: 'Builds Vite+ with the upstream repositories' inputs: + profile: + description: 'Cargo profile for native builds (release or ci).' + required: false + default: 'release' target: description: 'The target platform' required: true @@ -30,6 +34,8 @@ runs: # (packages/cli/package.json is modified by syncTestPackageExports during build-ts). - uses: ./.github/actions/compute-native-cache-input-hash id: native-cache-inputs + with: + profile: ${{ inputs.profile }} # Include the documentation origin so preview binaries cannot reuse release links. - name: Compute NAPI binding cache key @@ -123,8 +129,9 @@ runs: shell: bash 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 + pnpm --filter=vite-plus build-native --profile "$INPUTS_PROFILE" --target ${INPUTS_TARGET} --use-napi-cross env: + INPUTS_PROFILE: ${{ inputs.profile }} TARGET_CC: clang TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* @@ -134,8 +141,9 @@ runs: shell: bash if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') run: | - pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} -x + pnpm --filter=vite-plus build-native --profile "$INPUTS_PROFILE" --target ${INPUTS_TARGET} -x env: + INPUTS_PROFILE: ${{ inputs.profile }} TARGET_CC: clang TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* @@ -145,8 +153,9 @@ runs: shell: bash if: steps.native.outputs.build == 'true' && !contains(inputs.target, 'linux') run: | - pnpm --filter=vite-plus build-native --target ${INPUTS_TARGET} + pnpm --filter=vite-plus build-native --profile "$INPUTS_PROFILE" --target ${INPUTS_TARGET} env: + INPUTS_PROFILE: ${{ inputs.profile }} DEBUG: napi:* INPUTS_TARGET: ${{ inputs.target }} @@ -154,8 +163,9 @@ runs: 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 vp_global_cli + pnpm exec napi build --use-napi-cross --target ${INPUTS_TARGET} --profile "$INPUTS_PROFILE" -p vp_global_cli env: + INPUTS_PROFILE: ${{ inputs.profile }} TARGET_CC: clang TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* @@ -165,8 +175,9 @@ runs: if: steps.native.outputs.build == 'true' && contains(inputs.target, 'musl') shell: bash run: | - pnpm exec napi build -x --target ${INPUTS_TARGET} --release -p vp_global_cli + pnpm exec napi build -x --target ${INPUTS_TARGET} --profile "$INPUTS_PROFILE" -p vp_global_cli env: + INPUTS_PROFILE: ${{ inputs.profile }} TARGET_CC: clang TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* @@ -175,8 +186,9 @@ runs: - name: Build Rust CLI binary (non-Linux targets) if: steps.native.outputs.build == 'true' && !contains(inputs.target, 'linux') shell: bash - run: cargo build --release --target ${INPUTS_TARGET} -p vp_global_cli + run: cargo build --profile "$INPUTS_PROFILE" --target ${INPUTS_TARGET} -p vp_global_cli env: + INPUTS_PROFILE: ${{ inputs.profile }} INPUTS_TARGET: ${{ inputs.target }} # The helper builds the excluded trampoline from its crate directory. @@ -184,19 +196,32 @@ runs: - name: Build trampoline shim binary (Windows only) if: steps.native.outputs.build == 'true' && contains(inputs.target, 'windows') shell: bash - # The standalone trampoline keeps its own size-optimized Cargo profile. - run: | - env -u CARGO_PROFILE_RELEASE_LTO -u CARGO_PROFILE_RELEASE_CODEGEN_UNITS \ - -u CARGO_PROFILE_RELEASE_OPT_LEVEL \ - node packages/tools/src/build-trampoline.ts --release --target "${INPUTS_TARGET}" + run: node packages/tools/src/build-trampoline.ts --release --target "${INPUTS_TARGET}" env: INPUTS_TARGET: ${{ inputs.target }} - name: Build installer binary (Windows only) if: steps.native.outputs.build == 'true' && contains(inputs.target, 'windows') shell: bash - run: cargo build --release --target ${INPUTS_TARGET} -p vp_installer + run: cargo build --profile "$INPUTS_PROFILE" --target ${INPUTS_TARGET} -p vp_installer + env: + INPUTS_PROFILE: ${{ inputs.profile }} + INPUTS_TARGET: ${{ inputs.target }} + + # Keep the artifact layout used by installers and test consumers. + - name: Stage native CLI binaries + if: steps.native.outputs.build == 'true' && inputs.profile != 'release' + shell: bash + run: | + target_dir="${CARGO_TARGET_DIR:-target}/$INPUTS_TARGET" + mkdir -p "$target_dir/release" + if [[ "$INPUTS_TARGET" == *windows* ]]; then + cp "$target_dir/$INPUTS_PROFILE/vp.exe" "$target_dir/$INPUTS_PROFILE/vp-setup.exe" "$target_dir/release/" + else + cp "$target_dir/$INPUTS_PROFILE/vp" "$target_dir/release/" + fi env: + INPUTS_PROFILE: ${{ inputs.profile }} INPUTS_TARGET: ${{ inputs.target }} - name: Save NAPI binding cache diff --git a/.github/actions/build-windows-cli/action.yml b/.github/actions/build-windows-cli/action.yml index bbcfa33441..4cf5f45f7f 100644 --- a/.github/actions/build-windows-cli/action.yml +++ b/.github/actions/build-windows-cli/action.yml @@ -6,6 +6,10 @@ description: >- skip-native input). Run on a Linux runner after checkout + clone. inputs: + profile: + description: 'Cargo profile for native builds (release or ci).' + required: false + default: 'release' save-cache: description: "Whether setup-xwin's setup-rust should save its cache (usually only on main)." required: false @@ -22,6 +26,8 @@ runs: # the native binaries and generated bindings. - uses: ./.github/actions/compute-native-cache-input-hash id: native-cache-inputs + with: + profile: ${{ inputs.profile }} # Use a distinct prefix: this cache stores Linux-relative paths and must # never mix with the caches build-upstream saves on other runners. Sharing @@ -59,7 +65,7 @@ runs: if: steps.binaries-cache.outputs.cache-hit != 'true' with: save-cache: ${{ inputs.save-cache }} - cache-key: windows-cli-cross + cache-key: windows-cli-cross-${{ inputs.profile }} - uses: oxc-project/setup-node@1f1a5b4450c8905bd1830c3a908d22b960c4559a # main (Node before pnpm; unreleased) if: steps.binaries-cache.outputs.cache-hit != 'true' @@ -70,8 +76,9 @@ runs: - 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 + run: pnpm --filter=vite-plus build-native --profile "$INPUTS_PROFILE" --target x86_64-pc-windows-msvc -x --skip-format env: + INPUTS_PROFILE: ${{ inputs.profile }} DEBUG: 'napi:*' XWIN_ACCEPT_LICENSE: '1' # See setup-xwin / build-windows-tests for why this Detours escape @@ -81,8 +88,9 @@ runs: - 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 vp_global_cli -p vp_installer + run: cargo xwin build --profile "$INPUTS_PROFILE" --target x86_64-pc-windows-msvc -p vp_global_cli -p vp_installer env: + INPUTS_PROFILE: ${{ inputs.profile }} XWIN_ACCEPT_LICENSE: '1' CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH @@ -91,14 +99,21 @@ runs: - name: Build trampoline shim binary if: steps.binaries-cache.outputs.cache-hit != 'true' shell: bash - # The standalone trampoline keeps its own size-optimized Cargo profile. - run: | - env -u CARGO_PROFILE_RELEASE_LTO -u CARGO_PROFILE_RELEASE_CODEGEN_UNITS \ - -u CARGO_PROFILE_RELEASE_OPT_LEVEL \ - node packages/tools/src/build-trampoline.ts --xwin --release --target x86_64-pc-windows-msvc + run: node packages/tools/src/build-trampoline.ts --xwin --release --target x86_64-pc-windows-msvc env: XWIN_ACCEPT_LICENSE: '1' + # Keep the artifact layout used by installers and test consumers. + - name: Stage native CLI binaries + if: steps.binaries-cache.outputs.cache-hit != 'true' && inputs.profile != 'release' + shell: bash + run: | + target_dir="${CARGO_TARGET_DIR:-target}/x86_64-pc-windows-msvc" + mkdir -p "$target_dir/release" + cp "$target_dir/$INPUTS_PROFILE/vp.exe" "$target_dir/$INPUTS_PROFILE/vp-setup.exe" "$target_dir/release/" + env: + INPUTS_PROFILE: ${{ inputs.profile }} + # TypeScript builds need only these Cargo package fields for toolchain # versions. Export them where the Rust dependencies are already present. - name: Export Cargo metadata diff --git a/.github/actions/compute-native-cache-input-hash/action.yml b/.github/actions/compute-native-cache-input-hash/action.yml index 0f18c86c16..f89222f2b6 100644 --- a/.github/actions/compute-native-cache-input-hash/action.yml +++ b/.github/actions/compute-native-cache-input-hash/action.yml @@ -1,6 +1,12 @@ name: 'Compute native cache input hash' description: 'Hash every input that can affect the native CLI binaries and generated bindings.' +inputs: + profile: + description: 'Cargo profile for native builds (release or ci).' + required: false + default: 'release' + outputs: hash: description: 'Combined hash of the native build inputs.' @@ -12,11 +18,9 @@ runs: - name: Compute native cache input hash id: compute shell: bash - run: | - # Test builds and published builds must never share native binaries. - profile="${CARGO_PROFILE_RELEASE_LTO:-manifest}-${CARGO_PROFILE_RELEASE_CODEGEN_UNITS:-manifest}-${CARGO_PROFILE_RELEASE_OPT_LEVEL:-manifest}" - echo "hash=${NATIVE_CACHE_INPUT_HASH}-${profile}" >> "$GITHUB_OUTPUT" + run: echo "hash=${NATIVE_CACHE_INPUT_HASH}-${INPUTS_PROFILE}" >> "$GITHUB_OUTPUT" env: + INPUTS_PROFILE: ${{ inputs.profile }} # crates/vp_cli_snapshots is excluded: the snapshot suite (runner, # vpt, fixtures, recorded .md baselines) never feeds the native # binaries, and its dependency changes surface in the hashed root diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a304c65cfe..e9ab9067b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,11 +65,6 @@ jobs: permissions: contents: read packages: read - # Use Oxc's faster compilation settings for native test builds. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -79,7 +74,7 @@ jobs: # Shares the Cargo cache with the Linux CLI test build job, which # builds the same target. Only that job saves it. save-cache: false - cache-key: cli-snapshot-test-x86_64-unknown-linux-gnu + cache-key: cli-snapshot-test-x86_64-unknown-linux-gnu-ci - uses: ./.github/actions/setup-ci-node @@ -92,6 +87,7 @@ jobs: - name: Build with upstream uses: ./.github/actions/build-upstream with: + profile: ci target: x86_64-unknown-linux-gnu - name: Run vp fmt --check @@ -378,16 +374,12 @@ jobs: if: needs.detect-changes.outputs.code-changed == 'true' name: Build Windows CLI binaries runs-on: namespace-profile-linux-x64-default - # Use Oxc's faster compilation settings for native test builds. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - uses: ./.github/actions/build-windows-cli with: + profile: ci save-cache: ${{ github.ref_name == 'main' }} cli-e2e-test: @@ -956,11 +948,6 @@ jobs: - os: namespace-profile-mac-default target: aarch64-apple-darwin runs-on: ${{ matrix.os }} - # Use Oxc's faster compilation settings for native test builds. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -968,7 +955,7 @@ jobs: - 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: cli-snapshot-test-${{ matrix.target }} + cache-key: cli-snapshot-test-${{ matrix.target }}-ci - uses: ./.github/actions/setup-ci-node @@ -981,6 +968,7 @@ jobs: - name: Build with upstream uses: ./.github/actions/build-upstream with: + profile: ci target: ${{ matrix.target }} - name: Ensure no unexpected file changes after build @@ -1316,11 +1304,6 @@ jobs: needs: - download-previous-rolldown-binaries runs-on: namespace-profile-linux-x64-default - # Use Oxc's faster compilation settings for native test builds. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -1328,7 +1311,7 @@ jobs: - 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: cli-e2e-test-musl + cache-key: cli-e2e-test-musl-ci - uses: ./.github/actions/setup-ci-node @@ -1342,6 +1325,7 @@ jobs: - name: Build with upstream (musl) uses: ./.github/actions/build-upstream with: + profile: ci target: x86_64-unknown-linux-musl # Run bootstrap-cli:ci and E2E tests inside an Alpine container where musl is native. diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index d047dc6736..31ae03983e 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -80,16 +80,12 @@ jobs: permissions: contents: read packages: read - # Use Oxc's faster compilation settings for native test builds. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone - uses: ./.github/actions/build-windows-cli with: + profile: ci save-cache: ${{ github.ref_name == 'main' }} # Each platform starts its tests as soon as its own packages are ready. diff --git a/.github/workflows/reusable-ecosystem-ci.yml b/.github/workflows/reusable-ecosystem-ci.yml index 4ef8c6a0f8..e49e56d3c2 100644 --- a/.github/workflows/reusable-ecosystem-ci.yml +++ b/.github/workflows/reusable-ecosystem-ci.yml @@ -29,11 +29,6 @@ jobs: permissions: contents: read packages: read - # Use Oxc's faster compilation settings for native test builds. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' steps: - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 - uses: ./.github/actions/clone @@ -50,9 +45,9 @@ jobs: - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) if: ${{ !contains(inputs.target, 'windows') }} with: - # The CI CLI build caches these release dependencies and saves the cache. + # Reuse the CLI build job's Cargo cache for the ci profile. save-cache: false - cache-key: cli-snapshot-test-${{ inputs.target }} + cache-key: cli-snapshot-test-${{ inputs.target }}-ci - uses: oxc-project/setup-node@1f1a5b4450c8905bd1830c3a908d22b960c4559a # main (Node before pnpm; unreleased) @@ -73,6 +68,7 @@ jobs: - name: Build with upstream uses: ./.github/actions/build-upstream with: + profile: ci target: ${{ inputs.target }} skip-native: ${{ contains(inputs.target, 'windows') }} diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 82e788204c..3470ced16c 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -65,11 +65,6 @@ jobs: build: name: Build vite-plus packages runs-on: namespace-profile-linux-x64-default - # Match the CLI build profile to reuse its native and Cargo caches. - env: - CARGO_PROFILE_RELEASE_LTO: 'thin' - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' - CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' permissions: contents: read packages: read @@ -81,9 +76,9 @@ jobs: - uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24) with: - # The CI CLI build caches these release dependencies and saves the cache. + # Reuse the CLI build job's Cargo cache for the ci profile. save-cache: false - cache-key: cli-snapshot-test-x86_64-unknown-linux-gnu + cache-key: cli-snapshot-test-x86_64-unknown-linux-gnu-ci - uses: oxc-project/setup-node@1f1a5b4450c8905bd1830c3a908d22b960c4559a # main (Node before pnpm; unreleased) @@ -96,6 +91,7 @@ jobs: - name: Build with upstream uses: ./.github/actions/build-upstream with: + profile: ci target: x86_64-unknown-linux-gnu - name: Pack packages into tgz diff --git a/Cargo.toml b/Cargo.toml index abc49a29e7..c2dc286e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -446,3 +446,10 @@ panic = "abort" # Let it crash and force ourselves to write safe Rust. # The installer binary is downloaded by users, so optimize for size. [profile.release.package.vp_installer] opt-level = "z" + +# Oxc's compilation settings for native CI test binaries. +[profile.ci] +inherits = "release" +opt-level = 2 +lto = "thin" +codegen-units = 256 diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 299c4af950..4cf1c2e855 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -159,7 +159,7 @@ async function buildNapiBinding() { platform: true, jsBinding: 'index.cjs', dts: 'index.d.cts', - release: process.env.VP_CLI_DEBUG !== '1', + release: !passedInOptions.profile && process.env.VP_CLI_DEBUG !== '1', features: bindingFeatures, }); diff --git a/packages/tools/src/__tests__/native-build-profile.spec.ts b/packages/tools/src/__tests__/native-build-profile.spec.ts new file mode 100644 index 0000000000..c3bc1aea27 --- /dev/null +++ b/packages/tools/src/__tests__/native-build-profile.spec.ts @@ -0,0 +1,87 @@ +import { execFileSync } from 'node:child_process'; +import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { afterEach, expect, test } from 'vitest'; +import { parse } from 'yaml'; + +const repoRoot = fileURLToPath(new URL('../../../..', import.meta.url)); +const temporaryDirs: string[] = []; +// Native producers run on Linux and macOS, including Windows cross-builds. +const bashTest = test.skipIf(process.platform === 'win32'); + +function temporaryDir() { + const dir = mkdtempSync(path.join(tmpdir(), 'vp-native-profile-')); + temporaryDirs.push(dir); + return dir; +} + +function actionStep(action: string, name: string): { run: string } { + const file = path.join(repoRoot, '.github/actions', action, 'action.yml'); + return parse(readFileSync(file, 'utf8')).runs.steps.find( + (step: { name?: string }) => step.name === name, + ); +} + +afterEach(() => { + for (const dir of temporaryDirs.splice(0)) { + rmSync(dir, { recursive: true, force: true }); + } +}); + +bashTest('native caches separate release and CI binaries with identical source inputs', () => { + const dir = temporaryDir(); + const { run } = actionStep('compute-native-cache-input-hash', 'Compute native cache input hash'); + const keys = ['release', 'ci'].map((profile) => { + const output = path.join(dir, profile); + execFileSync('bash', ['-e', '-c', run], { + env: { + ...process.env, + GITHUB_OUTPUT: output, + NATIVE_CACHE_INPUT_HASH: 'same-source', + INPUTS_PROFILE: profile, + }, + }); + return readFileSync(output, 'utf8').trim(); + }); + expect(keys).toEqual(['hash=same-source-release', 'hash=same-source-ci']); +}); + +bashTest.each([ + ['build-upstream', 'x86_64-unknown-linux-gnu'], + ['build-upstream', 'x86_64-unknown-linux-musl'], + ['build-upstream', 'aarch64-apple-darwin'], + ['build-upstream', 'x86_64-pc-windows-msvc'], + ['build-windows-cli', 'x86_64-pc-windows-msvc'], +])('%s stages %s binaries for existing artifact consumers', (action, target) => { + const dir = temporaryDir(); + const targetDir = path.join(dir, 'custom target'); + const profileDir = path.join(targetDir, target, 'ci'); + const releaseDir = path.join(targetDir, target, 'release'); + mkdirSync(profileDir, { recursive: true }); + mkdirSync(releaseDir, { recursive: true }); + const binaries = target.includes('windows') ? ['vp.exe', 'vp-setup.exe'] : ['vp']; + for (const binary of binaries) { + writeFileSync(path.join(profileDir, binary), `ci ${binary}`); + chmodSync(path.join(profileDir, binary), 0o755); + writeFileSync(path.join(releaseDir, binary), `stale ${binary}`); + } + writeFileSync(path.join(releaseDir, 'vp-shim.exe'), 'release trampoline'); + + execFileSync('bash', ['-e', '-c', actionStep(action, 'Stage native CLI binaries').run], { + cwd: dir, + env: { + ...process.env, + CARGO_TARGET_DIR: targetDir, + INPUTS_PROFILE: 'ci', + INPUTS_TARGET: target, + }, + }); + + for (const binary of binaries) { + expect(readFileSync(path.join(releaseDir, binary), 'utf8')).toBe(`ci ${binary}`); + } + expect(readFileSync(path.join(releaseDir, 'vp-shim.exe'), 'utf8')).toBe('release trampoline'); +});