#!/usr/bin/env bash # Migrate PostgreSQL data from secrets-mcp-prod to secrets-nn-test. # # Prereqs: pg_dump and pg_restore (PostgreSQL client tools) on PATH. # TLS: Use the same connection parameters as your MCP / app (e.g. sslmode=verify-full # and PGSSLROOTCERT if needed). If local psql fails with "certificate verify failed", # run this script from a host that trusts the server CA, or set PGSSLROOTCERT. # # Usage: # export SOURCE_DATABASE_URL='postgres://USER:PASS@host:5432/secrets-mcp-prod?sslmode=verify-full' # export TARGET_DATABASE_URL='postgres://USER:PASS@host:5432/secrets-nn-test?sslmode=verify-full' # ./scripts/migrate-db-prod-to-nn-test.sh # # Options (env): # BACKUP_TARGET_FIRST=1 # default: dump target to ./backup-secrets-nn-test-.dump before restore # RUN_NN_SQL=1 # default: run migrations/001_nn_schema.sql then 002_data_cleanup.sql on target after restore # SKIP_TARGET_BACKUP=1 # skip target backup # # WARNINGS: # - pg_restore with --clean --if-exists drops objects that exist in the dump; target DB is replaced # to match the logical content of the source dump (same as typical full restore). # - Optionally keep a manual dump of the target before proceeding. # - 001_nn_schema.sql will fail if secrets has duplicate (user_id, name) after backfill; fix data first. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" SOURCE_URL="${SOURCE_DATABASE_URL:-}" TARGET_URL="${TARGET_DATABASE_URL:-}" if [[ -z "$SOURCE_URL" || -z "$TARGET_URL" ]]; then echo "Set SOURCE_DATABASE_URL and TARGET_DATABASE_URL (postgres URLs)." >&2 exit 1 fi if ! command -v pg_dump >/dev/null || ! command -v pg_restore >/dev/null; then echo "pg_dump and pg_restore are required." >&2 exit 1 fi TS="$(date +%Y%m%d%H%M%S)" DUMP_FILE="${DUMP_FILE:-$ROOT/tmp/secrets-mcp-prod-${TS}.dump}" mkdir -p "$(dirname "$DUMP_FILE")" if [[ "${EXCLUDE_TOWER_SESSIONS:-}" == "1" ]]; then echo "==> Excluding schema tower_sessions from dump" pg_dump "$SOURCE_URL" -Fc --no-owner --no-acl --exclude-schema=tower_sessions -f "$DUMP_FILE" else echo "==> Dumping source (custom format) -> $DUMP_FILE" pg_dump "$SOURCE_URL" -Fc --no-owner --no-acl -f "$DUMP_FILE" fi if [[ "${SKIP_TARGET_BACKUP:-}" != "1" && "${BACKUP_TARGET_FIRST:-1}" == "1" ]]; then BACKUP_FILE="$ROOT/tmp/secrets-nn-test-before-${TS}.dump" echo "==> Backing up target -> $BACKUP_FILE" pg_dump "$TARGET_URL" -Fc --no-owner --no-acl -f "$BACKUP_FILE" || { echo "Target backup failed (empty DB is OK). Continuing." >&2 } fi echo "==> Restoring into target (--clean --if-exists)" pg_restore -d "$TARGET_URL" --no-owner --no-acl --clean --if-exists --verbose "$DUMP_FILE" if [[ "${RUN_NN_SQL:-1}" == "1" ]]; then if [[ ! -f "$ROOT/migrations/001_nn_schema.sql" ]]; then echo "migrations/001_nn_schema.sql not found; skip NN SQL." >&2 else echo "==> Applying migrations/001_nn_schema.sql on target" psql "$TARGET_URL" -v ON_ERROR_STOP=1 -f "$ROOT/migrations/001_nn_schema.sql" fi if [[ -f "$ROOT/migrations/002_data_cleanup.sql" ]]; then echo "==> Applying migrations/002_data_cleanup.sql on target" psql "$TARGET_URL" -v ON_ERROR_STOP=1 -f "$ROOT/migrations/002_data_cleanup.sql" fi fi echo "==> Done. Suggested verification:" echo " psql \"\$TARGET_DATABASE_URL\" -c \"SELECT COUNT(*) FROM entries; SELECT COUNT(*) FROM secrets; SELECT COUNT(*) FROM entry_secrets;\"" echo " ./scripts/release-check.sh # optional app-side sanity"