Files
PIG-Demo/.github/workflows/ci.yml
T
karti-ai c4bf0c197e sitemap.xml was 404 on the live host; make the build own it
sitemap.mjs was a step I ran by hand, so the moment I rebuilt without it the
file vanished from dist and the deploy shipped a site with no sitemap — on a
site whose entire point is being findable. Nothing caught it because nothing
was looking.

It is now inside `pnpm build`, CI asserts the file exists, and deploy.sh
smoke-tests /sitemap.xml, /robots.txt and an OG card against the live hostname.
The 404 assertion was also a warning rather than a failure; it exits now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
2026-08-28 16:48:50 -07:00

106 lines
4.3 KiB
YAML

name: ci
on:
push:
branches: [main]
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
with: { version: 11.21.0 }
- uses: actions/setup-node@v5
with: { node-version: 22, cache: pnpm }
# Asserts the toolchain matches what package.json pins, rather than
# discovering a mismatch three steps later as an unrelated build error.
- name: preflight
run: |
test "$(pnpm -v)" = "11.21.0" || { echo "pnpm $(pnpm -v) != 11.21.0"; exit 1; }
- run: CI=true pnpm install --frozen-lockfile
# `pnpm build` ends in the prerender pass, which drives a real browser.
# Without this the build fails at the very last step, after everything
# else has passed, with a Playwright message about installing browsers.
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm typecheck
- run: pnpm check
- run: pnpm test
- run: pnpm build
- run: node scripts/bundle-budget.mjs
# The prerender pass writes a real HTML file per route. Crawlers do not
# run JavaScript, so without these every shared link previews as the
# homepage — assert the baked tags actually landed.
- name: prerendered head is real
run: |
test -f dist/demos/wordle/index.html || { echo "no prerendered demo route"; exit 1; }
grep -q 'og:title' dist/demos/wordle/index.html || { echo "og tags missing"; exit 1; }
grep -qv 'PIG Demo — RL environments you can play</title>' dist/demos/wordle/index.html \
|| { echo "demo route kept the homepage title"; exit 1; }
test -f dist/404.html || { echo "no 404.html"; exit 1; }
# sitemap.xml is written by a build step, not by Vite copying public/.
# It went missing once because the step was outside `pnpm build`, and
# nothing noticed until a 404 on the live host.
test -f dist/sitemap.xml || { echo "no sitemap.xml"; exit 1; }
# A blob-backed worker is blocked in production and nowhere else: the
# site's CSP has no worker-src, so it falls back to default-src 'self'.
# The failure is silent — the worker simply never boots.
#
# Grep for worker construction FROM a blob, not for the string `blob:`
# anywhere. React's own bundle contains that string in a URL-scheme check,
# so the broad version fails on a risk that is not present, which teaches
# everyone to ignore it.
- name: no blob-backed workers
run: |
if grep -rEo "new (Shared)?Worker\([^)]{0,80}" dist/assets/*.js \
| grep -E "blob:|createObjectURL"; then
echo "a worker is constructed from a blob URL; production CSP blocks it silently"
exit 1
fi
echo "ok — no blob-backed worker construction in the bundle"
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
with: { enable-cache: true }
- run: uv sync --all-packages
# Rebuild the word lists from their committed sources and assert the
# output is byte-identical. If it is not, every downstream number —
# the conformance digest included — is describing a different game.
- name: word lists rebuild identically
run: |
uv run python envs/wordle_five/words/build_words.py
git diff --exit-code envs/wordle_five/words/*.json
- run: uv run pytest envs/wordle_five/tests -q
- run: uv run python envs/probe.py
# The cross-language gate. Both halves score all 21.2M (guess, answer)
# pairs; the digests must match each other and the committed value.
- uses: pnpm/action-setup@v4
with: { version: 11.21.0 }
- uses: actions/setup-node@v5
with: { node-version: 22, cache: pnpm }
- run: CI=true pnpm install --frozen-lockfile
- run: pnpm conformance
# Every committed fixture must replay through the Python engine and
# reproduce its own recorded rewards. A fixture that cannot be
# regenerated is a claim with no receipt behind it.
- run: uv run python envs/verify_fixtures.py