rename(phase 3): compose/DB/infra names + stray code refs capakraken → nexus
CI / Architecture Guardrails (pull_request) Successful in 2m59s
CI / Typecheck (pull_request) Successful in 6m41s
CI / Lint (pull_request) Successful in 4m18s
CI / Assistant Split Regression (pull_request) Successful in 5m6s
CI / Unit Tests (pull_request) Successful in 7m21s
CI / Build (pull_request) Successful in 5m21s
CI / Fresh-Linux Docker Deploy (pull_request) Failing after 38s
CI / E2E Tests (pull_request) Successful in 3m28s
CI / Release Images (pull_request) Has been skipped

- docker-compose.yml / .prod.yml / .ci.yml: project names, POSTGRES_DB/USER,
  pg_isready, DATABASE_URL, volume names (nexus_pgdata, nexus_prod_*)
- .github/workflows/ci.yml: POSTGRES_PASSWORD, pg_isready, psql credentials,
  GRANT statements, POSTGRES_PASSWORD=nexus_dev for Docker Deploy job
- scripts/db-target-guard.mjs: expectedDatabase default, NEXUS_EXPECTED_DB_NAME
- scripts/prisma-with-env.mjs, e2e/test-server.mjs: env-var rename
- packages/db/src/safe-destructive-env.ts + reset-dispo-import.ts: DB name set
- packages/db/src/destructive-db-guard.ts: PROTECTED_DATABASE_NAMES → "nexus"
- packages/db/src/destructive-db-guard.test.ts: all fixture DB names + comments
- .env.example, tooling/deploy/deploy.env.example: DATABASE_URL, image refs
- packages/api: Redis channel/key prefixes (rbac-invalidate, sse, ratelimit),
  logger service name, app-base-url log prefix
- E2E: DB container names, localStorage/sessionStorage keys, email domains
- scripts: architecture-guardrails filter, export/import-dev-seed defaults,
  harden-postgres defaults, start.sh pg_isready, worktree-hygiene fixture
- tooling/migrate/rename-to-nexus.sh: new maintenance-window cutover script

Only intentional capakraken survivor: anonymization.ts DEFAULT_ANONYMIZATION_SEED
(functional cryptographic constant — changing it would invalidate stored aliases).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 16:35:39 +02:00
parent b41c1d2501
commit 01f8974314
44 changed files with 401 additions and 186 deletions
+167
View File
@@ -0,0 +1,167 @@
#!/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 table_name, n_live_tup FROM pg_stat_user_tables ORDER BY table_name;")
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 <<SQL
DO \$\$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='nexus') THEN
CREATE ROLE nexus LOGIN PASSWORD '${POSTGRES_PASSWORD}';
END IF;
END
\$\$;
SELECT 'CREATE DATABASE nexus OWNER nexus'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname='nexus')
\gexec
SQL
#───────────────────────────────────────────────────────────────────────────────
# 5. Restore into new DB.
#───────────────────────────────────────────────────────────────────────────────
echo "[5/7] Restoring dump into nexus DB..."
# Replace OWNER directives so restored objects belong to `nexus`.
sed 's/OWNER TO capakraken/OWNER TO nexus/g' "$DUMP_FILE" | \
docker compose -p "$OLD_PROJECT" -f "$COMPOSE_FILE" exec -T postgres \
psql -U nexus -d nexus
#───────────────────────────────────────────────────────────────────────────────
# 6. Volume rename (offline copy).
#───────────────────────────────────────────────────────────────────────────────
echo "[6/7] Stopping old project and copying volumes..."
docker compose -p "$OLD_PROJECT" -f "$COMPOSE_FILE" down --remove-orphans
for v in "${VOLUMES[@]}"; do
src="${OLD_PROJECT//-/_}_${v}"
dst="${NEW_PROJECT//-/_}_${v}"
# Skip dev-mode app overlays (node_modules / next) — they regenerate on startup.
if [ "$MODE" = "dev" ] && { [ "$v" = "node_modules" ] || [ "$v" = "next" ]; }; then
echo " Skipping $v (regenerated on next boot)"
continue
fi
if ! docker volume inspect "$src" >/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 table_name, n_live_tup FROM pg_stat_user_tables ORDER BY table_name;")
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;'"