146 lines
5.8 KiB
Bash
Executable File
146 lines
5.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -Eeuo pipefail
|
||
|
||
# === Настройки ===
|
||
SERVICE="bot" # имя сервиса из docker-compose.yml
|
||
APP_DIR="/app" # рабочая директория кода в контейнере
|
||
HOST_DB_DIR="./db" # каталог БД на хосте
|
||
HOST_DB_FILE="./db/bot.db" # файл БД на хосте
|
||
DB_URL_DEFAULT="sqlite+aiosqlite:////app/db/bot.db" # ЕДИНЫЙ URL БД в контейнере
|
||
|
||
log(){ echo -e "[update.sh] $*"; }
|
||
die(){ echo -e "[update.sh][ERROR] $*" >&2; exit 1; }
|
||
|
||
# Запускать из корня репо, даже если скрипт лежит в bin/
|
||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||
|
||
# --- Утилиты для alembic в "смонтированном" контейнере (чтобы файлы миграций попадали в репо) ---
|
||
alembic_run_mounted() {
|
||
# использование: alembic_run_mounted "upgrade head"
|
||
docker compose run --rm -T \
|
||
-v "$PWD":/app \
|
||
-w /app \
|
||
"${SERVICE}" sh -lc "alembic $*"
|
||
}
|
||
|
||
# --- 0) Приводим БД и .env к единому виду ---
|
||
log "Проверка каталога БД ${HOST_DB_DIR} ..."
|
||
mkdir -p "${HOST_DB_DIR}"
|
||
|
||
# Если в проекте остался прежний конфликтный объект ./bot.db — убираем/переносим
|
||
if [[ -d ./bot.db ]]; then
|
||
log "Удаляю конфликтующую ПАПКУ ./bot.db"
|
||
rm -rf ./bot.db
|
||
fi
|
||
if [[ -f ./bot.db && ! -f "${HOST_DB_FILE}" ]]; then
|
||
log "Переношу старый файл ./bot.db -> ${HOST_DB_FILE}"
|
||
mv ./bot.db "${HOST_DB_FILE}"
|
||
fi
|
||
if [[ ! -f "${HOST_DB_FILE}" ]]; then
|
||
log "Создаю пустой файл БД: ${HOST_DB_FILE}"
|
||
:> "${HOST_DB_FILE}"
|
||
fi
|
||
|
||
# .env: зафиксировать DATABASE_URL
|
||
if [[ -f .env ]]; then
|
||
if grep -q '^DATABASE_URL=' .env; then
|
||
sed -i "s|^DATABASE_URL=.*$|DATABASE_URL=${DB_URL_DEFAULT}|g" .env
|
||
log "DATABASE_URL в .env → ${DB_URL_DEFAULT}"
|
||
else
|
||
echo "DATABASE_URL=${DB_URL_DEFAULT}" >> .env
|
||
log "Добавил DATABASE_URL в .env → ${DB_URL_DEFAULT}"
|
||
fi
|
||
else
|
||
echo "DATABASE_URL=${DB_URL_DEFAULT}" > .env
|
||
log "Создал .env с DATABASE_URL=${DB_URL_DEFAULT}"
|
||
fi
|
||
|
||
# --- 1) git pull + сборка ---
|
||
log "git pull --rebase --autostash ..."
|
||
git pull --rebase --autostash || die "git pull не удался"
|
||
|
||
log "Пересборка образа ..."
|
||
docker compose build --no-cache || die "docker compose build не удался"
|
||
|
||
# --- 2) Безопасный upgrade: выравниваем БД до HEAD; при «потере» ревизии чиним alembic_version ---
|
||
safe_upgrade() {
|
||
log "alembic upgrade head ..."
|
||
set +e
|
||
UPG_LOG="$(alembic_run_mounted 'upgrade head' 2>&1)"
|
||
RC=$?
|
||
set -e
|
||
if [[ $RC -eq 0 ]]; then
|
||
return 0
|
||
fi
|
||
|
||
echo "$UPG_LOG"
|
||
if grep -q "Can't locate revision identified by" <<< "$UPG_LOG"; then
|
||
log "Обнаружена «потерянная» ревизия. Автопочинка: подшиваю БД к актуальному HEAD ..."
|
||
# Узнаём актуальный HEAD из каталога миграций
|
||
set +e
|
||
HEADREV="$(alembic_run_mounted 'heads -v' | awk '/^Rev:/{print $2; exit}')"
|
||
set -e
|
||
[[ -n "${HEADREV:-}" ]] || die "Не удалось определить HEAD ревизию"
|
||
|
||
# Переписываем alembic_version в файле БД (внутри контейнера сервиса)
|
||
docker compose run --rm -T \
|
||
-v "$PWD":/app \
|
||
-w /app \
|
||
"${SERVICE}" sh -lc "sqlite3 /app/db/bot.db \"UPDATE alembic_version SET version_num='${HEADREV}';\" || true"
|
||
|
||
# Повторяем апгрейд
|
||
alembic_run_mounted 'upgrade head' || die "Повторный upgrade head не удался"
|
||
else
|
||
die "alembic upgrade head не удался"
|
||
fi
|
||
}
|
||
|
||
# --- 3) Выравниваем миграции до текущего HEAD ---
|
||
safe_upgrade
|
||
|
||
# --- 4) (опционально) создаём ревизию с комментарием и применяем её ---
|
||
MIG_MSG="${1-}"
|
||
if [[ -z "${MIG_MSG}" ]]; then
|
||
read -rp "[update.sh] Комментарий для новой миграции (Enter — пропустить): " MIG_MSG || true
|
||
fi
|
||
|
||
if [[ -n "${MIG_MSG}" ]]; then
|
||
log "Создаю ревизию Alembic с комментарием: ${MIG_MSG}"
|
||
alembic_run_mounted "revision --autogenerate -m \"${MIG_MSG}\"" || die "alembic revision --autogenerate не удался"
|
||
# Сразу применяем
|
||
safe_upgrade
|
||
else
|
||
log "Создание ревизии пропущено."
|
||
fi
|
||
|
||
# --- 5) Запуск сервиса и пост-проверки ---
|
||
log "Запускаю контейнер ..."
|
||
docker compose up -d || die "docker compose up -d не удался"
|
||
|
||
log "Проверка переменных и таблиц внутри контейнера ..."
|
||
docker compose exec -T "${SERVICE}" sh -lc "
|
||
echo 'DATABASE_URL='\"\$DATABASE_URL\";
|
||
cd '${APP_DIR}';
|
||
echo 'Alembic HEADS:'; alembic heads -v || true;
|
||
echo 'Alembic CURRENT:'; alembic current -v || true;
|
||
if [ -f /app/db/bot.db ]; then
|
||
echo 'Таблицы SQLite (/app/db/bot.db):';
|
||
sqlite3 /app/db/bot.db '.tables' || true;
|
||
else
|
||
echo 'Внимание: /app/db/bot.db отсутствует!';
|
||
fi
|
||
" || true
|
||
|
||
log "Готово ✅"
|
||
log "Проверка переменных и таблиц внутри контейнера ..."
|
||
docker compose exec -T "${SERVICE}" sh -lc "
|
||
echo 'DATABASE_URL='\"\$DATABASE_URL\";
|
||
cd '${APP_DIR}';
|
||
echo 'Alembic HEADS:'; alembic heads -v || true;
|
||
echo 'Alembic CURRENT:'; alembic current -v || true;
|
||
if [ -f /app/db/bot.db ]; then
|
||
echo 'Таблицы SQLite (/app/db/bot.db):';
|
||
sqlite3 /app/db/bot.db '.tables' || true;
|
||
else
|
||
echo 'Внимание: /app/db/bot.db отсутствует!';
|
||
fi |