chore: version packages #1280
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: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| # PR runs are disposable — cancel superseded ones. `main` runs are not: they | |
| # own the production deploy, and cancelling one mid-deploy strands production | |
| # on an older commit while leaving a grey, cancelled job as the only trace. | |
| # `Production deploy — did it actually happen?` cannot fire from a run that | |
| # was itself cancelled, so the cheapest fix is to never cancel `main`. | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm test | |
| typecheck: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm typecheck | |
| - run: pnpm typecheck:performance | |
| typecheck-public: | |
| name: Typecheck — public API (type-tests) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| # No separate build step: `typecheck:public` builds @pretable/core and | |
| # @pretable/react itself before running the type-tests against their | |
| # built `dist/` output — the public API surface as a consumer sees it, | |
| # not the internal source types `typecheck` checks. | |
| - run: pnpm typecheck:public | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm lint | |
| format: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm format | |
| examples-registry: | |
| name: Examples registry — freshness | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| # Deliberately does NOT run `pnpm build` or `pnpm test` first. The | |
| # website's predev/prebuild/pretest hooks all *regenerate* the | |
| # registry via `examples:gen`, so a job that built or tested before | |
| # this check would validate files it had just rewritten itself — the | |
| # gate would be green even with a stale committed registry. This job | |
| # must check the registry exactly as committed. If you're adding | |
| # caching or folding jobs together, keep this check on its own, | |
| # pristine `pnpm install`. | |
| - run: pnpm --filter @pretable/app-website examples:check | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm build | |
| dev-smoke: | |
| name: Dev build — grid renders under StrictMode | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| # The website imports the packages from `dist`, so a dev server is only | |
| # as honest as the last build. | |
| - run: pnpm build | |
| - run: pnpm exec playwright install --with-deps chromium | |
| # Every other website spec runs against `next start`. A production build | |
| # does not double-invoke effects, which is why a homepage that rendered NO | |
| # grid under StrictMode survived from #321 to #383 with the suite green. | |
| # This job is the one that runs the build a contributor actually sees. | |
| - name: Start the dev server | |
| run: | | |
| pnpm --filter @pretable/app-website exec next dev -p 3100 \ | |
| > /tmp/next-dev.log 2>&1 & | |
| for i in $(seq 1 60); do | |
| if curl -sSf -o /dev/null http://localhost:3100/; then | |
| echo "dev server up after ${i}s" | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "dev server never responded" | |
| cat /tmp/next-dev.log | |
| exit 1 | |
| - name: Smoke the dev build | |
| env: | |
| # Declares intent: with this set, the spec refuses to skip. | |
| PRETABLE_DEV_SMOKE: "1" | |
| PRETABLE_DEV_URL: http://localhost:3100 | |
| run: | | |
| pnpm --filter @pretable/app-website exec playwright test \ | |
| e2e/dev-mode.spec.ts --project=chromium | |
| # The dev server's own output carries the compile errors Playwright only | |
| # sees as a blank page, so it is worth more than the trace on a failure. | |
| - name: Upload dev server log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: website-dev-server-log | |
| path: /tmp/next-dev.log | |
| retention-days: 7 | |
| - name: Upload Playwright traces on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: website-dev-smoke-traces | |
| path: apps/website/test-results/ | |
| retention-days: 7 | |
| bench-e2e: | |
| name: Bench — Playwright (real Chromium) | |
| runs-on: ubuntu-latest | |
| # Deliberately NOT in `deploy-prod`/`deploy-preview`'s `needs:`, and nothing | |
| # needs it. Those lists gate the website deploy; `apps/bench` is private and | |
| # is never deployed, so adding this job to them would only mean a browser | |
| # flake on `main` skips the production deploy and strands prod on an older | |
| # commit — the exact failure `Production deploy — did it actually happen?` | |
| # exists to alarm on. `dev-smoke` and `smoke-preview` are out of those lists | |
| # for the same reason; this job follows them. | |
| # | |
| # What makes it a gate is branch protection: add | |
| # `Bench — Playwright (real Chromium)` to the required checks on `main`. | |
| # | |
| # These specs are the only proof several shipped features work at all — | |
| # every eviction gate, the windowed-geometry gate, AG Grid's wrapped | |
| # auto-height, and the cascade/theming paint assertions. `apps/bench`'s own | |
| # `test` script is `vitest run --environment jsdom`, which collects `src/` | |
| # and never sees `tests/`, so before this job nothing in CI ran them. | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm exec playwright install --with-deps chromium | |
| # Build explicitly rather than letting `playwright.config.ts`'s `webServer` | |
| # do it. That config's command is `preview:bench`, whose `prepreview:bench` | |
| # hook runs the whole dependency + app build (11.7s warm locally, more on a | |
| # cold runner) INSIDE the webServer's 30s readiness budget — so a slow | |
| # runner reads as "server never came up" and a broken build reads as a | |
| # timeout instead of a compile error. A separate step fails on its own line. | |
| - run: pnpm --filter @pretable/app-bench build | |
| # `bench.spec.ts` refuses to measure a page it did not build: it compares | |
| # `dist/bench-build-id.txt` against the served page's `data-bench-build-id`. | |
| # Serving this `dist/` directly is what satisfies that check. | |
| - name: Start the bench preview server | |
| run: | | |
| pnpm --filter @pretable/app-bench exec vite preview \ | |
| --host 127.0.0.1 --port 4173 --strictPort \ | |
| > /tmp/bench-preview.log 2>&1 & | |
| for i in $(seq 1 60); do | |
| if curl -sSf -o /dev/null http://127.0.0.1:4173/; then | |
| echo "bench preview up after ${i}s" | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "bench preview never responded" | |
| cat /tmp/bench-preview.log | |
| exit 1 | |
| - name: Run the bench Playwright suite | |
| env: | |
| # The runner owns port 4173 alone, but the external-server path is | |
| # still the right one: it keeps the build out of the readiness budget | |
| # above and makes the server's log a separate artifact. | |
| PRETABLE_BENCH_EXTERNAL_SERVER: "1" | |
| PRETABLE_BENCH_BASE_URL: http://127.0.0.1:4173 | |
| # Do NOT add PRETABLE_BENCH_ADAPTER / SCENARIO / SCALE / SCRIPT here. | |
| # `resident-cap-memory.spec.ts` skips itself when any of those four is | |
| # set, so a selector added for convenience would silently drop a test | |
| # from the gate while the job stayed green. | |
| # Whole suite, every PR and every push — 16s locally at one worker, and | |
| # the only numeric budget in it (the 32 MB whole-page heap ceiling) is | |
| # measured at ~12.7 MB, so nothing here is a timing race worth splitting | |
| # off to a schedule. One worker because `ag-grid-wrap-auto-height.spec.ts` | |
| # polls for a settled auto-height layout and a two-core runner running two | |
| # workers is the one thing that could make that poll a race. | |
| run: pnpm bench:e2e -- --workers=1 | |
| - name: Upload the bench preview server log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: bench-preview-server-log | |
| path: /tmp/bench-preview.log | |
| retention-days: 7 | |
| - name: Upload the Playwright report on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: bench-playwright-report | |
| path: | | |
| playwright-report/ | |
| test-results/ | |
| retention-days: 7 | |
| packaging: | |
| name: Packaging — publint + attw | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm -r --filter '@pretable/core...' --filter '@pretable/react...' --filter '@pretable/stream-adapter...' --filter '@pretable/ui...' build | |
| - run: pnpm lint:packaging | |
| - run: pnpm consumer:check | |
| - run: pnpm react:compat | |
| publish-preflight: | |
| name: Publish preflight | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm publish:preflight | |
| security-audit: | |
| name: security-audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile | |
| - run: pnpm security:audit | |
| api-report: | |
| name: API Extractor — report freshness | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm -r --filter '@pretable/core...' --filter '@pretable/react...' --filter '@pretable/stream-adapter...' --filter '@pretable/ui...' build | |
| - run: pnpm api:check | |
| deploy-prod: | |
| name: Deploy → Vercel (production) | |
| needs: | |
| [ | |
| test, | |
| typecheck, | |
| typecheck-public, | |
| lint, | |
| format, | |
| build, | |
| packaging, | |
| publish-preflight, | |
| api-report, | |
| examples-registry, | |
| security-audit, | |
| ] | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| # Required GitHub secrets: | |
| # VERCEL_TOKEN — vercel.com/account/tokens | |
| # VERCEL_ORG_ID — Vercel team id | |
| # VERCEL_PROJECT_ID — pretable project id | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - name: Install Playwright browser | |
| run: pnpm exec playwright install --with-deps chromium webkit | |
| - name: Pull Vercel project settings | |
| run: | | |
| mkdir -p .vercel | |
| cat > .vercel/project.json <<EOF | |
| {"projectId":"${{ secrets.VERCEL_PROJECT_ID }}","orgId":"${{ secrets.VERCEL_ORG_ID }}","projectName":"pretable"} | |
| EOF | |
| npx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | |
| rm -rf .vercel/output | |
| - name: Build with Vercel | |
| env: | |
| # Baked into `/version.json` by apps/website/app/version.json/route.ts | |
| # so that something outside this run can verify production is serving | |
| # `main`. See .github/workflows/prod-freshness.yml. | |
| PRETABLE_COMMIT_SHA: ${{ github.sha }} | |
| run: npx vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | |
| - name: Deploy to Vercel (production) | |
| id: deploy | |
| run: | | |
| set -o pipefail | |
| url=$(npx vercel deploy --prebuilt --archive=tgz --prod --yes --token=${{ secrets.VERCEL_TOKEN }} | tail -n 1) | |
| case "$url" in | |
| https://*) ;; | |
| *) | |
| echo "::error::Vercel returned an invalid production deployment URL: $url" | |
| exit 1 | |
| ;; | |
| esac | |
| echo "deployment_url=$url" >> "$GITHUB_OUTPUT" | |
| echo "Deployed: $url" | |
| - name: Wait for production alias to update | |
| # vercel deploy --prod returns once the unique deployment URL is | |
| # ready; the public alias atomically repoints shortly after. A short | |
| # readiness loop avoids racing the alias swap. | |
| run: | | |
| for i in 1 2 3 4 5 6; do | |
| code=$(curl -sS -o /dev/null -w "%{http_code}" "https://pretable.vercel.app" || true) | |
| echo "Attempt $i: HTTP $code" | |
| if [ "$code" = "200" ]; then exit 0; fi | |
| sleep 5 | |
| done | |
| echo "Public alias did not return 200 after deploy" | |
| exit 1 | |
| - name: Smoke test prod with Playwright | |
| env: | |
| BASE_URL: https://pretable.vercel.app | |
| run: pnpm --filter @pretable/app-website smoke | |
| - name: Upload smoke artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: website-smoke-traces | |
| path: apps/website/test-results/ | |
| retention-days: 7 | |
| deploy-prod-alarm: | |
| name: Production deploy — did it actually happen? | |
| needs: [deploy-prod] | |
| runs-on: ubuntu-latest | |
| # `always()` is the whole point. Without it this job inherits `deploy-prod`'s | |
| # skip and goes grey alongside it — which is the bug, not the fix. | |
| if: >- | |
| always() && | |
| github.ref == 'refs/heads/main' && | |
| github.event_name == 'push' | |
| permissions: | |
| contents: read | |
| # To open/close the tracking issue. Branch protection cannot help here: | |
| # required status checks only gate merges *into* `main`; a push run's red | |
| # X blocks nothing and, on an already-red run, tells no one that | |
| # production specifically did not update. The issue is the signal that | |
| # outlives the run. | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| # No `pnpm install`: the script is dependency-free on purpose, so the | |
| # alarm cannot be taken out by the same broken install that could take out | |
| # the deploy. | |
| - name: Check that production got this commit | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| DEPLOY_RESULT: ${{ needs.deploy-prod.result }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: node ./scripts/check-prod-deploy.mjs --mode=run | |
| deploy-preview: | |
| name: Deploy → Vercel (preview) | |
| needs: | |
| [ | |
| test, | |
| typecheck, | |
| typecheck-public, | |
| lint, | |
| format, | |
| build, | |
| packaging, | |
| publish-preflight, | |
| api-report, | |
| examples-registry, | |
| security-audit, | |
| ] | |
| runs-on: ubuntu-latest | |
| if: >- | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.full_name == github.repository && | |
| github.event.pull_request.user.login != 'dependabot[bot]' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| outputs: | |
| preview_url: ${{ steps.deploy.outputs.preview_url }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - name: Pull Vercel project settings | |
| run: | | |
| mkdir -p .vercel | |
| cat > .vercel/project.json <<EOF | |
| {"projectId":"${{ secrets.VERCEL_PROJECT_ID }}","orgId":"${{ secrets.VERCEL_ORG_ID }}","projectName":"pretable"} | |
| EOF | |
| npx vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} | |
| rm -rf .vercel/output | |
| - name: Build with Vercel | |
| env: | |
| # Same stamp on previews, so a preview can be checked against the | |
| # commit it claims to be. | |
| PRETABLE_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: npx vercel build --token=${{ secrets.VERCEL_TOKEN }} | |
| - name: Deploy preview to Vercel | |
| id: deploy | |
| run: | | |
| set -o pipefail | |
| url=$(npx vercel deploy --prebuilt --archive=tgz --yes --token=${{ secrets.VERCEL_TOKEN }} | tail -n 1) | |
| case "$url" in | |
| https://*) ;; | |
| *) | |
| echo "::error::Vercel returned an invalid preview deployment URL: $url" | |
| exit 1 | |
| ;; | |
| esac | |
| echo "preview_url=$url" >> "$GITHUB_OUTPUT" | |
| echo "Preview: $url" | |
| - name: Comment preview URL on PR | |
| uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3 | |
| with: | |
| header: vercel-preview | |
| message: | | |
| ### Vercel preview ready | |
| **Preview**: ${{ steps.deploy.outputs.preview_url }} | |
| **Commit**: `${{ github.event.pull_request.head.sha }}` | |
| <sub>Updated automatically by the `deploy-preview` job.</sub> | |
| smoke-preview: | |
| name: Smoke test → Vercel preview (Playwright) | |
| needs: deploy-preview | |
| runs-on: ubuntu-latest | |
| if: >- | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.full_name == github.repository && | |
| github.event.pull_request.user.login != 'dependabot[bot]' | |
| env: | |
| PREVIEW_URL: ${{ needs.deploy-preview.outputs.preview_url }} | |
| VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 24.19.0 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - name: Choose preview verification mode | |
| id: preview-access | |
| run: | | |
| if [ -n "$VERCEL_AUTOMATION_BYPASS_SECRET" ]; then | |
| echo "mode=browser" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "mode=skip" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "### Preview smoke skipped" | |
| echo | |
| echo "No \`VERCEL_AUTOMATION_BYPASS_SECRET\` repository secret is configured, so the Playwright browser smoke cannot reach the protection-gated preview (it returns 401 without a bypass)." | |
| echo | |
| echo "Add the secret to enable real preview smoke coverage: Vercel → Project → Settings → Deployment Protection → Protection Bypass for Automation, then save it as the \`VERCEL_AUTOMATION_BYPASS_SECRET\` repository secret." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Install Playwright browser | |
| if: steps.preview-access.outputs.mode == 'browser' | |
| run: pnpm exec playwright install --with-deps chromium webkit | |
| - name: Wait for preview to respond | |
| if: steps.preview-access.outputs.mode == 'browser' | |
| run: | | |
| # Readiness ping only: send the bypass header alone. Do NOT send | |
| # `x-vercel-set-bypass-cookie: true` here — that makes Vercel answer | |
| # with a 307 + Set-Cookie (to seed the bypass cookie) rather than 200, | |
| # which a cookieless curl can't satisfy. Playwright keeps the cookie | |
| # header (it persists the cookie across requests); this plain curl | |
| # just needs a 200 to know the deployment is live. | |
| for i in 1 2 3 4 5 6; do | |
| code=$(curl -sS -o /dev/null -w "%{http_code}" \ | |
| -H "x-vercel-protection-bypass: $VERCEL_AUTOMATION_BYPASS_SECRET" \ | |
| "$PREVIEW_URL" || true) | |
| echo "Attempt $i: HTTP $code" | |
| if [ "$code" = "200" ]; then exit 0; fi | |
| sleep 5 | |
| done | |
| echo "Preview did not return 200" | |
| exit 1 | |
| - name: Smoke test preview with Playwright | |
| if: steps.preview-access.outputs.mode == 'browser' | |
| env: | |
| BASE_URL: ${{ env.PREVIEW_URL }} | |
| # Every project, matching the production smoke below. Pinning this to | |
| # chromium made the pre-merge gate structurally blind to exactly the | |
| # regressions it exists to stop: a WebKit-only mount stall shipped, then | |
| # failed the production smoke on every `main` run for 19 hours while | |
| # every PR check stayed green. A gate that cannot see what the next gate | |
| # blocks on is not a gate. | |
| run: pnpm --filter @pretable/app-website smoke | |
| - name: Upload Playwright traces on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: website-preview-traces | |
| path: apps/website/test-results/ | |
| retention-days: 7 |