Skip to content

chore: version packages #1280

chore: version packages

chore: version packages #1280

Workflow file for this run

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