1. deploy/check-prod-readiness.sh — pre-deploy gating: backup<60min, disk≥5GB на /opt+/var/lib/docker, /health/ready=Healthy, .env required-keys без placeholder'ов. --ssh-host для удалённой проверки. 2. deploy/prod-deploy.sh <api-tag> <web-tag> — blue-green release: pull → green-контейнер на :8088 → migrations (auto) → smoke (/health/ready + /api/me с тест-токеном) → nginx upstream switch → swap → docker compose up -d с обновлённым тэгом. Failure → удаление green, blue остаётся. --skip-web флаг. 3. deploy/prod-rollback.sh <to-tag> — docker pull (если нужно) → docker compose up -d --force-recreate с указанным tag'ом → wait /health/ready до 120с. --dry-run + --skip-web. 4. deploy/post-deploy-smoke.sh — 10 шагов (signup → login → /api/me → list products/counterparties/stores/stock → create+delete product → logout-via-session). JSON парсится через python3 (не grep — споткнулись на пробеле перед `:` в access_token). Telegram-alert через FM_TG_TOKEN/CHAT при провале. Stage-тест: 10/10 ✓. 5. deploy/db-schema-diff.sh — pg_dump --schema-only с обоих хостов через ssh+docker exec, нормализация (sed), diff -u. Exit: 0=идентичны, 1=разница, 2=ошибка. 6. deploy/generate-release-notes.sh <from-tag> <to-tag> — git log group by prefix через awk: feat→✨, fix→🐛, perf→⚡, docs→📚, test/refactor/chore→<details>. Сохраняет docs/release-notes/<tag>.md. 7. .forgejo/workflows/auto-tag.yml — на push в main: если HEAD не помечен → создаёт v<YYYYMMDD>.<N> annotated tag, push в origin, генерирует release-notes для будущего деплоя. Все скрипты идемпотентные, поддерживают --dry-run, не трогают прод. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
104 lines
3.9 KiB
Bash
Executable file
104 lines
3.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
#
|
||
# Sprint 21: сравнение схемы БД stage vs prod.
|
||
#
|
||
# Делает `pg_dump --schema-only` с обеих БД, diff'ит. Если выводит
|
||
# непустой diff — миграция не доехала или local-only изменения.
|
||
#
|
||
# Подразумевает что обе БД доступны (например через SSH-туннель или
|
||
# pg_dump --host=<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
|