fix(rsc): preserve exported client reference subpaths #4271
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_call: # allow other workflows to run CI as a gate | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.event_name }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| check: | |
| name: Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup | |
| - name: Build plugin (needed for benchmark workspace type resolution) | |
| run: vp run build | |
| - run: vp run check | |
| - run: vp run knip | |
| - name: Bash syntax check | |
| run: | | |
| for f in scripts/*.sh; do | |
| bash -n "$f" || { echo "Syntax error in $f"; exit 1; } | |
| done | |
| test-unit: | |
| name: Vitest (unit) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup | |
| - run: vp test run --project unit | |
| test-integration: | |
| name: Vitest (integration ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shardIndex: [1, 2, 3] | |
| shardTotal: [3] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup | |
| - name: Build plugin (needed by ecosystem and nextjs-compat fixtures) | |
| run: vp run build | |
| # Coverage is gated to push-to-main runs to keep PR feedback fast. | |
| # Istanbul instrumentation adds ~10–25% to each shard's wall-clock. | |
| - run: vp test run --project integration ${{ github.event_name == 'push' && '--coverage' || '' }} --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} | |
| env: | |
| CI: true | |
| - uses: actions/upload-artifact@v7 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: blob-report-${{ matrix.shardIndex }} | |
| path: .vitest-reports/* | |
| include-hidden-files: true | |
| retention-days: 1 | |
| test-integration-merge: | |
| name: Vitest (integration report) | |
| if: ${{ !cancelled() }} | |
| needs: [test-integration] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup | |
| - uses: actions/download-artifact@v7 | |
| with: | |
| path: .vitest-reports | |
| pattern: blob-report-* | |
| merge-multiple: true | |
| # --coverage tells vitest to invoke coverageProvider.mergeReports() on the | |
| # coverage data embedded in each shard's blob, producing a unified report. | |
| # Only enabled on push-to-main, matching the shard config above. | |
| - run: vp test run --merge-reports ${{ github.event_name == 'push' && '--coverage' || '' }} | |
| env: | |
| CI: true | |
| - name: Publish coverage matrix to job summary | |
| if: ${{ !cancelled() && hashFiles('coverage/coverage-summary.json') != '' }} | |
| run: node scripts/coverage-summary.mjs >> "$GITHUB_STEP_SUMMARY" | |
| - uses: actions/upload-artifact@v7 | |
| if: ${{ !cancelled() && hashFiles('coverage/index.html') != '' }} | |
| with: | |
| name: coverage-html | |
| path: coverage | |
| retention-days: 14 | |
| create-next-app: | |
| name: create-next-app (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup | |
| - name: Build plugin | |
| run: vp run build | |
| - name: Pack vinext for local install | |
| run: vp pm pack --pack-destination "${{ runner.temp }}" | |
| working-directory: packages/vinext | |
| - name: Scaffold a fresh create-next-app project | |
| run: vp dlx create-next-app@16.2.6 "${{ runner.temp }}/cna-test" --yes | |
| - name: Deny build scripts in scaffolded project | |
| working-directory: ${{ runner.temp }}/cna-test | |
| shell: bash | |
| # pnpm 11 may auto-add placeholder allowBuilds entries during the | |
| # scaffold install. Replace them with explicit false values. | |
| run: | | |
| node -e ' | |
| const fs = require("fs"); | |
| const f = "pnpm-workspace.yaml"; | |
| let y = fs.existsSync(f) ? fs.readFileSync(f, "utf8") : ""; | |
| y = y.replace(/allowBuilds:[\s\S]*?(?=\n\S|\n*$)/, ""); | |
| y = y.trimEnd() + "\n\nallowBuilds:\n sharp: false\n unrs-resolver: false\n"; | |
| fs.writeFileSync(f, y); | |
| ' | |
| - name: Pin pnpm in scaffolded project to vinext's version | |
| working-directory: ${{ runner.temp }}/cna-test | |
| shell: bash | |
| # create-next-app's bundled pnpm links node_modules to a major-version | |
| # store (e.g. v10). vp's bundled pnpm can drift to a newer major (v11), | |
| # which then refuses to operate on the existing node_modules with | |
| # ERR_PNPM_UNEXPECTED_STORE. vp respects the project's packageManager | |
| # field, so writing the same one vinext itself uses makes vp's pnpm | |
| # match the store layout from create-next-app. Tracks any future | |
| # bump in vinext's root package.json automatically. | |
| run: | | |
| # cd into the workspace to read package.json via a relative path — | |
| # ${{ github.workspace }} contains backslashes on Windows that get | |
| # eaten by the JS string literal parser when interpolated inline. | |
| PKG_MGR=$(cd "${{ github.workspace }}" && node -p "require('./package.json').packageManager") | |
| node -e " | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8')); | |
| pkg.packageManager = '$PKG_MGR'; | |
| fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| echo "Pinned packageManager to $PKG_MGR" | |
| - name: Install vinext from local tarball | |
| working-directory: ${{ runner.temp }}/cna-test | |
| shell: bash | |
| run: vp add "${{ runner.temp }}"/vinext-*.tgz | |
| - name: Run vinext init | |
| working-directory: ${{ runner.temp }}/cna-test | |
| run: vp exec vinext init --skip-check | |
| - name: Start dev server and verify HTTP 200 | |
| working-directory: ${{ runner.temp }}/cna-test | |
| shell: bash | |
| run: | | |
| vp exec vite dev --port 3099 & | |
| SERVER_PID=$! | |
| for i in $(seq 1 30); do | |
| STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3099/ || true) | |
| if [ "$STATUS" = "200" ]; then | |
| echo "Server responded with HTTP 200 (attempt $i)" | |
| kill "$SERVER_PID" 2>/dev/null || true | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "Server did not respond with HTTP 200 within 30 seconds" | |
| kill "$SERVER_PID" 2>/dev/null || true | |
| exit 1 | |
| e2e: | |
| name: E2E (${{ matrix.project }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| project: | |
| - pages-router | |
| - app-router | |
| - app-router-chrome-browser-specific | |
| - app-router-webkit-browser-specific | |
| - cloudflare-pages-router | |
| - pages-router-prod | |
| - cloudflare-workers | |
| - cloudflare-dev | |
| - cloudflare-pages-router-dev | |
| - static-export | |
| - app-with-src | |
| - standalone-output | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup | |
| - name: Build plugin | |
| run: vp run build | |
| # Resolve the exact Playwright version from the lockfile so the cache key | |
| # only changes when Playwright itself is bumped, not on unrelated lockfile | |
| # churn. Falls back to a hash of pnpm-lock.yaml if parsing fails. | |
| - name: Resolve Playwright version | |
| id: playwright-version | |
| run: | | |
| set -euo pipefail | |
| version=$(grep -E "^\s+playwright:\s+[0-9]" pnpm-lock.yaml | head -n1 | awk '{print $2}' || true) | |
| if [ -z "$version" ]; then | |
| version="unknown-${{ hashFiles('pnpm-lock.yaml') }}" | |
| fi | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "Resolved Playwright version: $version" | |
| - name: Cache Playwright browsers | |
| id: playwright-cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-${{ matrix.project == 'app-router-webkit-browser-specific' && 'webkit' || 'chromium' }}-${{ runner.os }}-v${{ steps.playwright-version.outputs.version }} | |
| restore-keys: | | |
| playwright-${{ matrix.project == 'app-router-webkit-browser-specific' && 'webkit' || 'chromium' }}-${{ runner.os }}- | |
| # Cache the apt download cache for WebKit's system dependencies. | |
| # `playwright install-deps webkit` always runs `apt-get install`, but with | |
| # the package archives cached we skip the (slow) download step on hits. | |
| - name: Cache apt archives (WebKit deps) | |
| if: ${{ matrix.project == 'app-router-webkit-browser-specific' }} | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cache/apt-archives | |
| key: apt-webkit-${{ runner.os }}-v${{ steps.playwright-version.outputs.version }} | |
| restore-keys: | | |
| apt-webkit-${{ runner.os }}- | |
| # Install Chromium browser binaries. Skip when the cache is fully hit — | |
| # `playwright install` is a no-op for already-installed browsers but the | |
| # CLI startup itself adds a few seconds per job. | |
| - name: Install Playwright Chromium | |
| if: ${{ matrix.project != 'app-router-webkit-browser-specific' && steps.playwright-cache.outputs.cache-hit != 'true' }} | |
| run: vp exec playwright install chromium | |
| # Install WebKit browser binaries only on cache miss. System deps are | |
| # installed in a separate step below so we can cache them independently. | |
| - name: Install Playwright WebKit (browser only) | |
| if: ${{ matrix.project == 'app-router-webkit-browser-specific' && steps.playwright-cache.outputs.cache-hit != 'true' }} | |
| run: vp exec playwright install webkit | |
| # Install WebKit system dependencies. This always runs because apt | |
| # packages are root-level and not part of ~/.cache/ms-playwright. The | |
| # apt archive cache above keeps this fast on hits by avoiding re-downloads. | |
| # `playwright install-deps` invokes apt-get under sudo internally. | |
| - name: Install Playwright WebKit system dependencies | |
| if: ${{ matrix.project == 'app-router-webkit-browser-specific' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ~/.cache/apt-archives | |
| sudo mkdir -p /var/cache/apt/archives | |
| # Seed apt's archive dir from our cache so apt skips re-downloading | |
| # .deb files it already has from a previous run. | |
| if [ -n "$(ls -A ~/.cache/apt-archives 2>/dev/null)" ]; then | |
| sudo cp -rn ~/.cache/apt-archives/. /var/cache/apt/archives/ || true | |
| fi | |
| # Tell apt not to delete .deb files after install so they survive | |
| # for our post-step cache copy. | |
| echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | sudo tee /etc/apt/apt.conf.d/01keep-debs >/dev/null | |
| vp exec playwright install-deps webkit | |
| # Persist newly downloaded archives back to our cache directory. | |
| sudo find /var/cache/apt/archives -maxdepth 1 -name '*.deb' -exec cp -n {} ~/.cache/apt-archives/ \; | |
| sudo chown -R "$(id -u):$(id -g)" ~/.cache/apt-archives | |
| - run: vp run test:e2e | |
| env: | |
| CI: true | |
| PLAYWRIGHT_PROJECT: ${{ matrix.project }} | |
| - uses: actions/upload-artifact@v7 | |
| if: failure() | |
| with: | |
| name: playwright-report-${{ matrix.project }} | |
| path: playwright-report/ | |
| retention-days: 7 | |
| ci: | |
| name: CI | |
| if: ${{ always() }} | |
| needs: [check, test-unit, test-integration-merge, create-next-app, e2e] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: All checks passed | |
| if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} | |
| run: echo "All checks passed" | |
| - name: Some checks failed | |
| if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} | |
| run: exit 1 |