#!/usr/bin/env bash # # Deploy PIG. Run on the host that serves it. # # ./scripts/deploy.sh # # Deliberately a script rather than automated push-to-deploy. Automating it # would mean putting an SSH key with write access to the production host onto # the CI runner, which is a meaningful escalation for a project this size. CI # proves the commit is sound; a human decides when it ships. # # Safe to re-run. Migrations are additive and tracked. set -euo pipefail cd "$(dirname "$0")/.." echo "==> Fetching" git fetch -q origin BEFORE=$(git rev-parse --short HEAD) git reset --hard -q origin/main AFTER=$(git rev-parse --short HEAD) if [ "$BEFORE" = "$AFTER" ]; then echo " Already at $AFTER" else echo " $BEFORE -> $AFTER" git --no-pager log --oneline "$BEFORE..$AFTER" | sed 's/^/ /' fi echo "==> Backing up the database first" # Cheap insurance. A migration that goes wrong on a database holding real deal # data is not something to discover without a dump in hand. mkdir -p backups BACKUP="backups/pig-$(date +%Y%m%d-%H%M%S).sql.gz" sudo docker compose -p pig exec -T db pg_dump -U pig pig | gzip > "$BACKUP" echo " $BACKUP ($(du -h "$BACKUP" | cut -f1))" echo "==> Building" sudo docker compose -p pig build app echo "==> Starting the database" sudo docker compose -p pig up -d db for _ in $(seq 1 60); do if sudo docker compose -p pig exec -T db pg_isready -U pig -d pig > /dev/null; then break; fi sleep 1 done if ! sudo docker compose -p pig exec -T db pg_isready -U pig -d pig > /dev/null; then echo "ERROR: database did not become ready within 60 seconds" >&2 exit 1 fi echo "==> Migrating before the schema-dependent app starts" # A release may query a newly introduced table during startup. Running the # migration from a one-off container prevents that app from crash-looping # before an `exec`-based migration can reach it. sudo docker compose -p pig run --rm --no-deps app npx tsx packages/db/src/migrate.ts echo "==> Starting the app" sudo docker compose -p pig up -d app echo "==> Waiting for health" for _ in $(seq 1 60); do if curl -sf http://127.0.0.1:8920/api/health > /dev/null; then break; fi sleep 1 done echo "==> Verifying" if curl -sf http://127.0.0.1:8920/api/health | grep -q '"ok":true'; then echo " health ok" else echo " HEALTH CHECK FAILED" sudo docker compose -p pig logs app --tail 40 exit 1 fi # Authentication must be enforced. A deploy that accidentally serves the CRM # unauthenticated is the one failure worth blocking on. CODE=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8920/api/dashboard) if [ "$CODE" != "401" ]; then echo " UNAUTHENTICATED REQUEST RETURNED $CODE, EXPECTED 401" exit 1 fi echo " auth enforced" echo "==> Deployed $AFTER"