#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # Деплой anti-plagiarism (умная пересборка — только изменённых сервисов). # # Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32 # (см. .gitea/workflows/deploy.yml). Ожидает: # - cwd = ПОЛНЫЙ чекаут репозитория (нужен git-лог для diff); # - переменную PVE_PASSWORD (секрет) для доступа к Proxmox-хосту → CT 102. # # Пересобирается только то, чей код изменился с прошлого деплоя (маркер SHA в # $APP/.last_deploy_sha). Это критично: worker-gpu тянет torch/faiss (~2 ГБ), # его нельзя пересобирать без нужды. Если менялся docker-compose.prod.yml — # пересобираются все бэкенд-сервисы. # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail APP=/home/user/anti-plagiarism SRC="$(pwd)" PVE_HOST=192.168.20.254 COMPOSE="docker compose -f docker-compose.prod.yml" ALL_BACKEND="api worker-gpu worker-indexer worker-notifier worker-gost" MARKER="$APP/.last_deploy_sha" CUR_SHA="$(git -C "$SRC" rev-parse HEAD)" # ── Определить, что менялось ────────────────────────────────────────────────── FULL=0 CHANGED="" if [ -f "$MARKER" ] && PREV="$(cat "$MARKER")" && [ -n "$PREV" ] \ && git -C "$SRC" cat-file -e "$PREV" 2>/dev/null; then CHANGED="$(git -C "$SRC" diff --name-only "$PREV" "$CUR_SHA")" echo "Изменения с $PREV:" echo "$CHANGED" | sed 's/^/ /' # Правка общего compose затрагивает все образы echo "$CHANGED" | grep -qE '^docker-compose\.prod\.yml$' && FULL=1 else echo "Маркер прошлого деплоя недоступен — полная пересборка (разовая)" FULL=1 fi REBUILD="" FRONTEND_CHANGED=0 if [ "$FULL" = 1 ]; then REBUILD="$ALL_BACKEND" FRONTEND_CHANGED=1 else for svc in $ALL_BACKEND; do echo "$CHANGED" | grep -qE "^services/$svc/" && REBUILD="$REBUILD $svc" done echo "$CHANGED" | grep -qE '^services/frontend/' && FRONTEND_CHANGED=1 fi REBUILD="$(echo "$REBUILD" | xargs || true)" echo "==> [1/5] Синхронизация кода в $APP (сохраняя .env и compose)" rsync -a \ --exclude='__pycache__/' --exclude='*.pyc' --exclude='.pytest_cache/' \ --exclude='node_modules/' --exclude='dist/' \ "$SRC/services" "$SRC/scripts" "$APP/" cd "$APP" if [ -n "$REBUILD" ]; then echo "==> [2/5] Пересборка и перезапуск: $REBUILD" # shellcheck disable=SC2086 $COMPOSE up -d --build $REBUILD else echo "==> [2/5] Бэкенд-сервисы не менялись — пропуск сборки" fi echo "==> [3/5] Миграции БД (с ретраем)" for i in $(seq 1 10); do $COMPOSE exec -T api alembic upgrade head && break echo " api ещё не готов, повтор $i/10..."; sleep 5 done if [ "$FRONTEND_CHANGED" = 1 ]; then echo "==> [4/5] Сборка фронтенда" docker run --rm -v "$APP/services/frontend":/app -w /app node:20-slim \ sh -c "npm install --no-audit --no-fund --loglevel=error && npm run build" echo "==> [5/5] Выкладка статики на CT 102 (через $PVE_HOST)" cd "$APP/services/frontend/dist" tar czf /tmp/academic-dist.tgz . export SSHPASS="${PVE_PASSWORD:?PVE_PASSWORD не задан}" SSHOPT="-o StrictHostKeyChecking=no -o ConnectTimeout=10" # shellcheck disable=SC2086 sshpass -e scp $SSHOPT /tmp/academic-dist.tgz "root@${PVE_HOST}:/tmp/academic-dist.tgz" # shellcheck disable=SC2086 sshpass -e ssh $SSHOPT "root@${PVE_HOST}" 'bash -s' <<'REMOTE' set -e pct push 102 /tmp/academic-dist.tgz /tmp/academic-dist.tgz pct exec 102 -- bash -c ' set -e cd /var/www cp -a academic "academic.bak.$(date +%s)" cd academic rm -rf assets index.html tar xzf /tmp/academic-dist.tgz chown -R 1000:1000 . nginx -t && nginx -s reload ' rm -f /tmp/academic-dist.tgz REMOTE rm -f /tmp/academic-dist.tgz else echo "==> [4-5/5] Фронтенд не менялся — пропуск сборки и выкладки" fi echo "$CUR_SHA" > "$MARKER" echo "==> Деплой завершён успешно (маркер обновлён: $CUR_SHA)"