name: PR checks # Enforces the contribution policy on every PR into master (all gates hard): # - code changes must ship with tests (docs/config/automation are exempt) # - type-check is clean across the whole project (tests included) # - unit tests pass # - integration tests pass against an ephemeral Postgres (migrate + seed) # Runs on the pms1 host runner. See automation/README.md > "Contribution policy". on: pull_request: branches: [master] jobs: checks: runs-on: host steps: - name: Checkout PR uses: actions/checkout@v4 with: fetch-depth: 0 - name: Policy — code changes must include tests run: | set -uo pipefail base="${GITHUB_BASE_REF:-master}" git fetch origin "$base" --depth=200 -q changed=$(git diff --name-only "origin/$base...HEAD") printf 'Changed files:\n%s\n\n' "$changed" # "Code" = app source (pages, API routes, lib, components, hooks). # Tests, prisma, config, docs, automation and .forgejo are exempt. code=$(printf '%s\n' "$changed" | grep -E '^App/(app|lib|components|hooks)/' \ | grep -vE '(\.test\.|\.spec\.|/tests/)' || true) tests=$(printf '%s\n' "$changed" | grep -E '(\.test\.|\.spec\.|/tests/)' || true) if [ -n "$code" ] && [ -z "$tests" ]; then echo "::error::Code changed but no test files changed." echo "Every code PR must add or update tests (model: the claude/issue-12 integration test)." echo "If a test is genuinely not applicable, say why in the PR description so a reviewer can override." printf '\nCode files without accompanying tests:\n%s\n' "$code" exit 1 fi echo "OK — test-presence policy satisfied." - name: Type-check (no errors) run: | set -e export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" cd App pnpm install --frozen-lockfile pnpm db:generate # prisma client types (no DB connection needed) pnpm type-check # whole project, tests included — must be clean - name: Unit tests run: | set -e export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" cd App && pnpm test # jsdom unit tests, no DB — must pass integration: runs-on: host steps: - name: Checkout PR uses: actions/checkout@v4 - name: Integration tests (ephemeral Postgres) run: | set -euo pipefail export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" # Throwaway Postgres per run — isolated from prod / pelagia_test / staging. # A random host port avoids collisions with the host DB and concurrent runs. PG="ci-pg-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}" cleanup() { docker rm -f "$PG" >/dev/null 2>&1 || true; } trap cleanup EXIT docker rm -f "$PG" >/dev/null 2>&1 || true docker run -d --name "$PG" \ -e POSTGRES_USER=ci -e POSTGRES_PASSWORD=ci -e POSTGRES_DB=pelagia_ci \ -p 127.0.0.1::5432 postgres:16 >/dev/null for i in $(seq 1 30); do docker exec "$PG" pg_isready -U ci -d pelagia_ci >/dev/null 2>&1 && break sleep 1 done PORT=$(docker inspect --format '{{ (index (index .NetworkSettings.Ports "5432/tcp") 0).HostPort }}' "$PG") export DATABASE_URL="postgresql://ci:ci@127.0.0.1:${PORT}/pelagia_ci" # Non-secret placeholders so auth.ts (reads these at module load) boots in dev mode. export NEXTAUTH_SECRET="ci-secret" export NEXTAUTH_URL="http://localhost:3000" export AZURE_AD_CLIENT_ID="placeholder" export AZURE_AD_CLIENT_SECRET="placeholder" export AZURE_AD_TENANT_ID="placeholder" cd App pnpm install --frozen-lockfile pnpm db:generate pnpm db:migrate:deploy # apply migrations to the fresh DB pnpm db:seed # dev seed — integration tests rely on it pnpm test:integration # node + real DB — must pass