v0.34.0: containerize for per-PR preview environments (flotilla SPEC §15)

This commit is contained in:
Ben Stull
2026-06-01 06:51:22 -07:00
parent 97ba3ae9b5
commit 0252e40527
9 changed files with 292 additions and 2 deletions
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Preview container entrypoint (flotilla SPEC §15).
#
# 1. Render nginx against Cloud Run's $PORT.
# 2. Start the single-process uvicorn (which runs DB migrations on startup via
# the app lifespan — §4.2: one process, one SQLite file, never scale workers).
# 3. On a brand-new DB, apply the SYNTHETIC seed fixture so the preview shows
# realistic content with no real user PII (§15).
# 4. Hand the foreground to nginx.
#
# This is preview-only plumbing — production runs uvicorn under systemd and nginx
# under the OS, never this script.
set -euo pipefail
: "${PORT:=8080}"
: "${DATABASE_PATH:=/opt/rfc-app/backend/data/rfc-app.db}"
export PORT DATABASE_PATH
mkdir -p "$(dirname "$DATABASE_PATH")"
# Render the nginx config with the injected port.
envsubst '${PORT}' \
< /opt/rfc-app/deploy/preview/nginx.conf.template \
> /etc/nginx/nginx.conf
fresh=0
[ -f "$DATABASE_PATH" ] || fresh=1
# Start uvicorn (backgrounded); the app's lifespan runs migrations on boot.
cd /opt/rfc-app/backend
uvicorn app.main:app --host 127.0.0.1 --port 8000 &
UVICORN_PID=$!
# Seed synthetic data once the schema exists (only on a fresh DB).
if [ "$fresh" = "1" ]; then
for _ in $(seq 1 30); do
if [ -f "$DATABASE_PATH" ] \
&& sqlite3 "$DATABASE_PATH" "SELECT 1 FROM schema_migrations LIMIT 1;" >/dev/null 2>&1; then
break
fi
sleep 1
done
if sqlite3 "$DATABASE_PATH" < /opt/rfc-app/deploy/preview/seed.sql; then
echo "[preview-entrypoint] applied synthetic seed" >&2
else
# Best-effort: a seed that drifts from the schema must not block the
# preview from booting (it still serves /api/health + an empty app).
echo "[preview-entrypoint] WARNING: synthetic seed failed (schema drift?) — continuing" >&2
fi
fi
# nginx in the foreground becomes the container's main process. (uvicorn is a
# reparented child; for an ephemeral preview the hard stop on instance teardown
# is fine — a process supervisor is a follow-up if graceful drain matters.)
exec nginx -g 'daemon off;'