From 12e6d16061736973fbd4799cb397bf5df102f3bb Mon Sep 17 00:00:00 2001 From: Hardik Date: Fri, 19 Jun 2026 04:49:15 +0530 Subject: [PATCH] feat(automation): test DB mirror + dev-server env for autofix verification - automation/refresh-test-db.sh: daily pg_dump of prod (pelagia) into a throwaway mirror (pelagia_test) on pms1; cron at 03:30. ~10MB, refresh ~1s. - Autofix clone ~/pelagia-autofix/App/.env points DATABASE_URL at pelagia_test in safe dev mode (no Resend/SSO secrets -> console email, local storage), port 3100. - Fix prompt: Claude may run integration tests against the test DB and start a dev server on port 3100 ONLY; stop it by port (fuser -k 3100/tcp), never broad pkill (production also runs a next-server on 3000). Co-Authored-By: Claude Opus 4.8 --- automation/README.md | 19 ++++++++++++ automation/claude-issue-watcher.sh | 16 +++++++++-- automation/refresh-test-db.sh | 46 ++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 automation/refresh-test-db.sh diff --git a/automation/README.md b/automation/README.md index 52ba446..c1b0c96 100644 --- a/automation/README.md +++ b/automation/README.md @@ -63,6 +63,25 @@ activates automatically once signed in. (An `ANTHROPIC_API_KEY` env var also sat The Windows variant (`.ps1` + `register-watcher-task.ps1`) is the portable fallback; re-enable its task only if pms1 is unavailable, and disable one before enabling the other. +## Test database (for autofix verification) + +So the fix stage can verify against realistic data without touching production: + +- **`pelagia_test`** — a PostgreSQL database on pms1, owned by `pelagia_user`, that is + a **daily mirror of production** (`pelagia`). Created once as superuser; refreshed by + `automation/refresh-test-db.sh` via cron at **03:30** (`pg_dump pelagia | psql pelagia_test`). +- The autofix clone's `~/pelagia-autofix/App/.env` points `DATABASE_URL` at `pelagia_test` + and runs in **safe dev mode** — no Resend/SSO secrets, so email is console-logged and + storage is local. `NEXTAUTH_URL`/`PORT` are set to **3100** (production app is on 3000). +- The fix prompt tells Claude it may run integration tests against this DB + (`set -a; . ./.env; set +a; pnpm test:integration`) and may start a dev server on + **port 3100 only**, stopping it by port (`fuser -k 3100/tcp`) — never a broad `pkill next`, + which would take down production (it also runs a `next-server`). + +Because the test DB is refreshed daily, anything the autofix writes to it (test data, +schema experiments) is disposable. Schema-migration issues are routed to `interactive` +by triage, so the unattended fixer should not be altering the schema anyway. + ## Issue label lifecycle ``` diff --git a/automation/claude-issue-watcher.sh b/automation/claude-issue-watcher.sh index f751be4..8207aab 100644 --- a/automation/claude-issue-watcher.sh +++ b/automation/claude-issue-watcher.sh @@ -270,10 +270,22 @@ while [ "$f" -lt "$n_fix" ]; do printf '\n## Issue #%s: %s\n\n' "$num" "$title" printf '%s\n\n' "$body" printf '%s\n\n' "$comments" + printf '%s\n' "## Test environment available to you" + printf '%s\n' "- App/.env points DATABASE_URL at a TEST database (pelagia_test) -- a daily mirror of" + printf '%s\n' " production, safe to read and write. It is NOT production. Email is console-logged and" + printf '%s\n' " storage is local in this dev mode (no real emails/uploads)." + printf '%s\n' "- To run integration tests against it, load the env first:" + printf '%s\n' " cd App && set -a && . ./.env && set +a && pnpm test:integration" + printf '%s\n' "- If you need runtime verification, you MAY start a dev server ON PORT 3100 ONLY:" + printf '%s\n' " cd App && pnpm dev -p 3100 (production runs on 3000 -- NEVER touch 3000)" + printf '%s\n' " When done, stop ONLY your own server by port: 'fuser -k 3100/tcp' (or kill its exact PID)." + printf '%s\n' " NEVER use a broad 'pkill -f next' -- it would kill the production app." + printf '%s\n' "- Never connect to or modify the production database or the production app." + printf '%s\n' "" printf '%s\n' "## Your job" printf '%s\n' "1. Investigate the issue and implement a focused, minimal fix in this repository." - printf '%s\n' "2. Verify: run 'pnpm type-check' and 'pnpm lint' in App/. If you changed behaviour covered" - printf '%s\n' " by unit tests, run the relevant tests. Do not start the dev server or any database." + printf '%s\n' "2. Verify: run 'pnpm type-check' and 'pnpm lint' in App/. If behaviour is covered by unit" + printf '%s\n' " tests, run them; for DB-backed behaviour, run integration tests against the test DB above." printf '%s\n' "3. Add or adjust tests when it makes sense." printf '%s\n' "4. Commit ALL changes to the current branch with a conventional message ending: Fixes #$num" printf '%s\n' "5. Do NOT push, do NOT create tags, do NOT switch branches. The supervisor handles push and PR." diff --git a/automation/refresh-test-db.sh b/automation/refresh-test-db.sh new file mode 100644 index 0000000..bf01590 --- /dev/null +++ b/automation/refresh-test-db.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Refresh the test database from production. Runs daily via cron on pms1. +# +# pelagia_test is a throwaway mirror of prod (pelagia) so the autofix Claude can +# run integration tests / a dev server against realistic data WITHOUT touching +# production. The test DB is owned by pelagia_user (created once as superuser); +# this refresh runs purely as pelagia_user using the prod connection string. + +set -uo pipefail + +ENV_FILE="${1:-/home/shad0w/pms/App/.env}" +PROD_DB="pelagia" +TEST_DB="pelagia_test" + +log() { echo "$(date '+%F %T') $*"; } + +PROD_URL=$(grep -E '^DATABASE_URL' "$ENV_FILE" | sed -E 's/^DATABASE_URL=//; s/^"//; s/"$//') +[ -n "$PROD_URL" ] || { log "ERROR: no DATABASE_URL in $ENV_FILE"; exit 1; } + +# Derive the test URL by swapping ONLY the database-name path segment (anchored on +# @host/ so the 'pelagia' inside the username is never touched). +TEST_URL=$(printf '%s' "$PROD_URL" | sed -E "s#(@[^/]+/)$PROD_DB([?]|\$)#\1$TEST_DB\2#") + +if [ "$TEST_URL" = "$PROD_URL" ]; then + log "ERROR: failed to derive test URL (db name not found in connection string)"; exit 1 +fi + +log "Refreshing $TEST_DB from $PROD_DB ..." +# --clean --if-exists drops+recreates each object in place (first run on an empty +# DB is a no-op for the DROPs). --no-owner/--no-privileges keep it portable. +errfile=$(mktemp) +pg_dump --clean --if-exists --no-owner --no-privileges "$PROD_URL" \ + | psql "$TEST_URL" >/dev/null 2>"$errfile" + +prod_tables=$(psql -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';" "$PROD_URL") +test_tables=$(psql -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';" "$TEST_URL") + +if [ "$test_tables" = "$prod_tables" ] && [ "$test_tables" -gt 0 ]; then + log "Done. $TEST_DB has $test_tables public tables (prod has $prod_tables)." + rm -f "$errfile" +else + log "WARNING: table counts differ (test=$test_tables prod=$prod_tables). Recent errors:" + tail -8 "$errfile" + rm -f "$errfile" + exit 1 +fi