#!/usr/bin/env bash # # Sprint 21: сравнение схемы БД stage vs prod. # # Делает `pg_dump --schema-only` с обеих БД, diff'ит. Если выводит # непустой diff — миграция не доехала или local-only изменения. # # Подразумевает что обе БД доступны (например через SSH-туннель или # pg_dump --host=). Дефолтные подключения: # stage = docker exec food-market-stage-postgres-1 pg_dump (через ssh dev-vm) # prod = docker exec food-market-postgres pg_dump (через ssh prod-vm) # # Usage: # deploy/db-schema-diff.sh [--stage-host HOST] [--prod-host HOST] # [--quick] # без TOAST/sequence-details # [--dry-run] # печать только команд # # Выход: # 0 — схемы идентичны # 1 — есть различия (печатает diff) # 2 — ошибка получения дампа set -uo pipefail STAGE_HOST="${FM_STAGE_HOST:-nns@192.168.1.190}" PROD_HOST="${FM_PROD_HOST:-nns@admin.food-market.kz}" STAGE_CONT="${FM_STAGE_CONT:-food-market-stage-postgres-1}" PROD_CONT="${FM_PROD_CONT:-food-market-postgres}" DB="${FM_PG_DB:-food_market}" DB_USER="${FM_PG_USER:-food_market}" QUICK=0 DRY_RUN=0 while [[ $# -gt 0 ]]; do case "$1" in --stage-host) STAGE_HOST="$2"; shift 2 ;; --prod-host) PROD_HOST="$2"; shift 2 ;; --quick) QUICK=1; shift ;; --dry-run) DRY_RUN=1; shift ;; --help|-h) grep -E '^#( |$)' "$0" | sed 's/^# \?//'; exit 0 ;; *) echo "Unknown: $1" >&2; exit 2 ;; esac done # Флаги pg_dump для schema-only сравнения. --schema-only + --no-owner + # --no-privileges чтобы дамп был стабильный без role-mismatch'ей между # инстансами. --no-comments — выключаем COMMENT'ы (они часто шумят). PGDUMP_FLAGS="--schema-only --no-owner --no-privileges --no-comments" if [[ $QUICK -eq 1 ]]; then PGDUMP_FLAGS="$PGDUMP_FLAGS --exclude-table-data=pg_*" fi TMP_DIR=$(mktemp -d) trap "rm -rf $TMP_DIR" EXIT STAGE_SQL="$TMP_DIR/stage.sql" PROD_SQL="$TMP_DIR/prod.sql" log() { echo "[$(date -Iseconds)] $*" >&2; } dump_remote() { local host="$1" container="$2" out="$3" log "dump from $host (container $container) → $out" if [[ $DRY_RUN -eq 1 ]]; then echo "[dry-run] ssh $host docker exec $container pg_dump $PGDUMP_FLAGS -U $DB_USER -d $DB > $out" touch "$out" return fi ssh -o ConnectTimeout=10 "$host" "docker exec $container pg_dump $PGDUMP_FLAGS -U $DB_USER -d $DB" > "$out" 2>/dev/null \ || { log "FAIL: dump from $host"; return 2; } } dump_remote "$STAGE_HOST" "$STAGE_CONT" "$STAGE_SQL" || exit 2 dump_remote "$PROD_HOST" "$PROD_CONT" "$PROD_SQL" || exit 2 # Нормализация: убираем строки которые всегда отличаются (комментарии, # даты, version-header'ы, OID'ы): normalize() { sed -e '/^-- /d' \ -e '/^SET /d' \ -e '/^SELECT pg_catalog.set_config/d' \ -e '/^[[:space:]]*$/d' "$1" } # Применяем нормализацию ин-плейс и сравниваем. normalize "$STAGE_SQL" > "$TMP_DIR/stage.norm" normalize "$PROD_SQL" > "$TMP_DIR/prod.norm" log "comparing…" if diff -u "$TMP_DIR/prod.norm" "$TMP_DIR/stage.norm" > "$TMP_DIR/diff" 2>&1; then echo "✓ Схемы идентичны (stage == prod)" exit 0 fi LINES=$(wc -l < "$TMP_DIR/diff") echo "✗ Найдены различия: $LINES строк diff'a" echo echo "===== diff (prod → stage) =====" cat "$TMP_DIR/diff" echo "===============================" echo echo "Если разница — новые миграции stage → prod, применить их перед deploy." echo "Если разница — local-only изменения на prod, разобраться вручную." exit 1