name: Test (reusable) on: workflow_call: inputs: node: required: true type: string node_major: required: true type: string platform: required: true type: string garnet: required: false type: boolean default: false secrets: GARNET_API_TOKEN: required: false permissions: contents: read jobs: test: name: Node ${{ inputs.node_major }} concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.platform }}-${{ inputs.node }} cancel-in-progress: true runs-on: ${{ inputs.platform }} steps: # A near-full "affected packages" sweep plus the pnpr Rust build # can exhaust the hosted runner's free disk mid-test (the runner # worker itself dies with "No space left on device"). Drop the # preinstalled toolchains this job never uses (~25 GB) first. - name: Free up runner disk space if: ${{ runner.os == 'Linux' }} run: | sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL df -h / - name: Configure Git run: | git config --global core.autocrlf false git config --global user.name "xyz" git config --global user.email "x@y.z" - name: Checkout Commit uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - if: ${{ inputs.garnet }} uses: garnet-org/action@2b7fc9d79b54f551b43358c27424a36064b3e078 # v2 with: api_token: ${{ secrets.GARNET_API_TOKEN }} - name: Install pnpm and Node uses: pnpm/setup@b1cac37306e39c21283b9dd6cb0ac288fb35ba6b with: runtime: node@${{ inputs.node }} - name: Verify Node version shell: bash env: NODE_VERSION: ${{ inputs.node }} # `pn node -v` falls back through `run`/`exec`, which would # otherwise trigger a verifyDepsBeforeRun install just to print # the version. Disable it so this step measures the runtime that # pnpm/setup provisioned, not one a stray install pulled in. pnpm_config_verify_deps_before_run: false run: | actual=$(pn node -v) expected="v${NODE_VERSION}" if [ "$actual" != "$expected" ]; then echo "Expected Node version $expected but got $actual" exit 1 fi # npm is needed for preparing git-hosted dependencies (e.g. in dlx tests). # `pnpm runtime set node` does not extract npm; the runner image's # pre-installed Node toolchain provides it on PATH. - name: Verify npm run: npm --version - name: Download compiled artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: compiled-packages - name: Extract compiled artifacts run: tar -xzf compiled.tar.gz # The test harness serves package fixtures through the in-repo # `pnpr` server; `pnpr-prepare` turns the raw fixtures under # `pnpr/.fixtures/packages` into the storage the server reads. Both # are built from source so the tests exercise the current server # (e.g. the install-accelerator endpoints) rather than a published # `@pnpm/pnpr` binary that may predate it. # # The built binaries are cached and keyed on the Rust sources that # produce them, so a run that only touches TypeScript restores them # in seconds instead of recompiling. They are copied out of `target/` # into a stable dir so `Swatinem/rust-cache`'s `target/` cleanup # can't strip them before this cache saves. - name: Restore prebuilt pnpr binaries id: pnpr-bins uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: .pnpr-bin key: pnpr-bins-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock', '**/Cargo.toml', 'pnpr/**/*.rs', 'pacquet/**/*.rs') }} - name: Install Rust if: steps.pnpr-bins.outputs.cache-hit != 'true' uses: ./.github/actions/rustup with: save-cache: ${{ github.ref_name == 'main' }} shared-key: registry-prepare - name: Build the pnpr server and registry fixture preparer if: steps.pnpr-bins.outputs.cache-hit != 'true' shell: bash run: | cargo build --locked --release -p pnpr --bin pnpr -p pnpr-fixtures --bin pnpr-prepare mkdir -p .pnpr-bin ext="" [ -f target/release/pnpr.exe ] && ext=".exe" cp "target/release/pnpr$ext" "target/release/pnpr-prepare$ext" .pnpr-bin/ # Save the binaries to the cache right after they build, not in a post-job # step: a later step failing (e.g. a crashing test) would otherwise skip # the save and force a full Rust rebuild on the next run. - name: Save prebuilt pnpr binaries if: steps.pnpr-bins.outputs.cache-hit != 'true' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: .pnpr-bin key: ${{ steps.pnpr-bins.outputs.cache-primary-key }} - name: Export pnpr binary paths shell: bash run: | ext="" [ -f "$PWD/.pnpr-bin/pnpr.exe" ] && ext=".exe" echo "PNPR_BIN=$PWD/.pnpr-bin/pnpr$ext" >> "$GITHUB_ENV" echo "PNPR_PREPARE_BIN=$PWD/.pnpr-bin/pnpr-prepare$ext" >> "$GITHUB_ENV" - name: Determine test scope id: test-scope shell: bash env: REF_NAME: ${{ github.ref_name }} run: | if [[ "$REF_NAME" == "main" || "$REF_NAME" == "chore/update-lockfile" || "$REF_NAME" == release/* ]]; then { echo "script=ci:test-all" echo "scope=all" echo "full_tests=true" echo "benchmark=tests.all" } >> "$GITHUB_OUTPUT" else git remote set-branches --add origin main && git fetch origin main --depth=1 if [ -n "$(git diff --name-only origin/main HEAD -- pnpm-workspace.yaml)" ]; then { echo "script=ci:test-all" echo "scope=all — pnpm-workspace.yaml modified" echo "full_tests=true" echo "benchmark=tests.all" } >> "$GITHUB_OUTPUT" else { echo "script=ci:test-branch" echo "scope=affected packages" echo "full_tests=false" echo "benchmark=tests.affected" } >> "$GITHUB_OUTPUT" fi fi - name: Run tests (${{ steps.test-scope.outputs.scope }}) timeout-minutes: 70 shell: bash env: BENCHMARK: ${{ steps.test-scope.outputs.benchmark }} PNPM_WORKERS: 3 TEST_SCRIPT: ${{ steps.test-scope.outputs.script }} run: | node .github/scripts/measure-command.mjs \ --name "$BENCHMARK" \ --output ".bench/${BENCHMARK}.json" \ -- pn run "$TEST_SCRIPT" - name: Extract pnpm CLI e2e test duration if: steps.test-scope.outputs.full_tests == 'true' shell: bash run: | node .github/scripts/bencher-result-from-pnpm-summary.mjs \ --name tests.cli \ --output .bench/tests.cli.json \ --package-dir pnpm11/pnpm - name: Stage Bencher test durations if: steps.test-scope.outputs.full_tests == 'true' env: NODE_VERSION: ${{ inputs.node }} PLATFORM: ${{ inputs.platform }} shell: bash run: | platform_slug=${PLATFORM%-latest} node_major=${NODE_VERSION%%.*} case "$platform_slug" in ubuntu|windows) ;; *) echo "::error::Unsupported platform '$platform_slug' for pnpm benchmarks; expected ubuntu or windows" exit 1 ;; esac case "$node_major" in 22|24|26) ;; *) echo "::error::Unsupported Node major '$node_major' for pnpm benchmarks; expected 22, 24, or 26" exit 1 ;; esac artifact_dir=.bench/artifact/pnpm mkdir -p "$artifact_dir" node .github/scripts/merge-bencher-results.mjs \ --output "$artifact_dir/results.json" \ -- .bench/tests.all.json .bench/tests.cli.json cat > "$artifact_dir/metadata.json" <