#!/usr/bin/env bash # Start everything, in one command. # # ./scripts/up.sh # # Brings up Postgres, applies any migration that has not run yet, then runs the # Go API and the Vite dev server together until Ctrl-C stops both. # # Open http://localhost:5173 — in development Vite serves the page and proxies # /api to the Go process on :3000. set -euo pipefail cd "$(dirname "$0")/.." [ -f .env ] || { echo "no .env — copy .env.example and fill it in"; exit 1; } command -v pnpm >/dev/null || { echo "pnpm is missing — npm i -g pnpm"; exit 1; } WEB_PID= API_PID= # Both halves are process trees, not processes: pnpm sits above vite, and the # `docker run` client above the container. Kill depth-first so nothing is # orphaned holding a port. Plain SIGTERM throughout — SIGINT would be ignored by # the `go run` that is PID 1 inside the container. kill_tree() { local pid="$1" child for child in $(pgrep -P "$pid" 2>/dev/null); do kill_tree "$child"; done kill "$pid" 2>/dev/null || true } cleanup() { local pid for pid in $API_PID $WEB_PID; do kill_tree "$pid"; done wait 2>/dev/null || true } trap 'exit 130' INT TERM trap cleanup EXIT ./scripts/db.sh up # Idempotent, so it can run on every start. This is what makes a fresh clone — # or a pull that added a migration — work without a second command. echo "applying migrations" ./scripts/dev.sh migrate