#!/usr/bin/env bash # # Phase 3 cutover: migrate the running stack from `capakraken` (DB, role, # volumes, compose project) to `nexus`. Intended to be run inside a brief # maintenance window — the app is stopped for the duration. # # Idempotency: each step checks the current state. Re-running after a # partial run only does the missing pieces. The script never DROPS the # legacy `capakraken` DB or role — that's a manual decision after a # stability window. # # Usage: # ./tooling/migrate/rename-to-nexus.sh dev # against docker-compose.yml # ./tooling/migrate/rename-to-nexus.sh prod # against docker-compose.prod.yml # # Requires: # - POSTGRES_PASSWORD set in env (or .env file picked up by docker compose) # - docker compose CLI v2+ # - the `nexus` branch of code already checked out (compose files reference nexus_*) set -euo pipefail MODE="${1:-dev}" case "$MODE" in dev) COMPOSE_FILE=docker-compose.yml OLD_PROJECT=capakraken NEW_PROJECT=nexus VOLUMES=(pgdata node_modules next) ;; prod) COMPOSE_FILE=docker-compose.prod.yml OLD_PROJECT=capakraken-prod NEW_PROJECT=nexus-prod VOLUMES=(prod_pgdata prod_redis) ;; *) echo "Usage: $0 [dev|prod]" >&2 exit 2 ;; esac if [ -z "${POSTGRES_PASSWORD:-}" ]; then echo "POSTGRES_PASSWORD must be set in the environment." >&2 exit 2 fi DUMP_FILE="/tmp/capakraken-pre-rename-$(date +%Y%m%d-%H%M%S).sql" echo "=== Phase 3 cutover ($MODE) ===" echo "compose: $COMPOSE_FILE project: $OLD_PROJECT → $NEW_PROJECT dump: $DUMP_FILE" echo #─────────────────────────────────────────────────────────────────────────────── # 1. Stop the app (DB stays up so we can dump it). #─────────────────────────────────────────────────────────────────────────────── echo "[1/7] Stopping app container under old project name..." docker compose -p "$OLD_PROJECT" -f "$COMPOSE_FILE" stop app 2>/dev/null || true #─────────────────────────────────────────────────────────────────────────────── # 2. Capture row counts for verification. #─────────────────────────────────────────────────────────────────────────────── echo "[2/7] Capturing pre-rename row counts..." PRE_COUNTS=$(docker compose -p "$OLD_PROJECT" -f "$COMPOSE_FILE" exec -T postgres \ psql -U capakraken -d capakraken -t -c \ "SELECT relname, n_live_tup FROM pg_stat_user_tables ORDER BY relname;") echo "$PRE_COUNTS" | head -20 echo "..." #─────────────────────────────────────────────────────────────────────────────── # 3. Dump existing DB. #─────────────────────────────────────────────────────────────────────────────── echo "[3/7] pg_dump capakraken → $DUMP_FILE..." docker compose -p "$OLD_PROJECT" -f "$COMPOSE_FILE" exec -T postgres \ pg_dump -U capakraken -d capakraken --clean --if-exists > "$DUMP_FILE" echo "Dump size: $(du -h "$DUMP_FILE" | cut -f1)" #─────────────────────────────────────────────────────────────────────────────── # 4. Create new role + DB inside the running postgres container. #─────────────────────────────────────────────────────────────────────────────── echo "[4/7] Creating nexus role + database..." docker compose -p "$OLD_PROJECT" -f "$COMPOSE_FILE" exec -T postgres \ psql -U capakraken -d postgres </dev/null 2>&1; then echo " Source volume $src missing — skip" continue fi if docker volume inspect "$dst" >/dev/null 2>&1; then echo " Destination volume $dst already exists — skip" continue fi echo " $src → $dst" docker volume create "$dst" >/dev/null docker run --rm \ -v "${src}:/from:ro" \ -v "${dst}:/to" \ alpine sh -c "cd /from && cp -a . /to/" done #─────────────────────────────────────────────────────────────────────────────── # 7. Bring up under new compose project name. #─────────────────────────────────────────────────────────────────────────────── echo "[7/7] Starting stack under new project name '$NEW_PROJECT'..." PROFILE="" [ "$MODE" = "dev" ] && PROFILE="--profile full" # shellcheck disable=SC2086 docker compose -p "$NEW_PROJECT" -f "$COMPOSE_FILE" $PROFILE up -d echo echo "Waiting 15s for postgres to be ready..." sleep 15 echo "=== Verification ===" POST_COUNTS=$(docker compose -p "$NEW_PROJECT" -f "$COMPOSE_FILE" exec -T postgres \ psql -U nexus -d nexus -t -c \ "SELECT relname, n_live_tup FROM pg_stat_user_tables ORDER BY relname;") echo "Post-rename row counts (sample):" echo "$POST_COUNTS" | head -20 if diff <(echo "$PRE_COUNTS") <(echo "$POST_COUNTS") >/dev/null; then echo "✓ Row counts match — migration verified." else echo "⚠ Row counts differ — review diff:" diff <(echo "$PRE_COUNTS") <(echo "$POST_COUNTS") | head fi echo echo "Done. Old DB+role retained for rollback. Dump kept at $DUMP_FILE." echo "After a stability window, drop with:" echo " docker compose -p $NEW_PROJECT -f $COMPOSE_FILE exec postgres psql -U nexus -d postgres \\" echo " -c 'DROP DATABASE capakraken; DROP ROLE capakraken;'"