diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..0ce4272 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,37 @@ +name: Deploy + +# Авто-деплой в прод при пуше в main. +# Джоба выполняется на self-hosted runner'е в host-режиме прямо на app-хосте +# 1.32 (label "deploy"), поэтому имеет доступ к его docker и сети до Proxmox. +on: + push: + branches: [main] + workflow_dispatch: {} + +concurrency: + group: deploy-prod + cancel-in-progress: false + +jobs: + deploy: + runs-on: deploy + steps: + - name: Клонировать текущий коммит + run: | + set -euo pipefail + SRC="$(mktemp -d)" + echo "SRC=$SRC" >> "$GITHUB_ENV" + git clone --depth 1 --branch main \ + https://gitea.jze9.ru/jze9/anti-plagiarism.git "$SRC" + + - name: Деплой (бэкенд 1.32 + фронтенд CT 102) + env: + PVE_PASSWORD: ${{ secrets.PVE_PASSWORD }} + run: | + set -euo pipefail + cd "$SRC" + bash scripts/deploy.sh + + - name: Убрать временный чекаут + if: always() + run: rm -rf "${SRC:-/tmp/none}" diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..8d9f08c --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# ───────────────────────────────────────────────────────────────────────────── +# Деплой anti-plagiarism. +# +# Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32 +# (см. .gitea/workflows/deploy.yml). Ожидает: +# - cwd = свежий чекаут репозитория; +# - переменную окружения PVE_PASSWORD (секрет Gitea) для доступа к Proxmox-хосту, +# через который статика кладётся в CT 102 (reverse-proxy nginx). +# +# Что делает: +# 1. Синхронизирует код (services/ + scripts/) в /home/user/anti-plagiarism, +# НЕ трогая .env и локально-правленый docker-compose.prod.yml. +# 2. Пересобирает и перезапускает бэкенд-сервисы (api + воркеры). +# 3. Применяет миграции БД. +# 4. Собирает фронтенд (npm build) в статику. +# 5. Выкладывает статику в /var/www/academic на CT 102 (с бэкапом + reload nginx). +# ───────────────────────────────────────────────────────────────────────────── +set -euo pipefail + +APP=/home/user/anti-plagiarism +SRC="$(pwd)" +PVE_HOST=192.168.20.254 # Proxmox-хост, через pct exec которого живёт CT 102 +COMPOSE="docker compose -f docker-compose.prod.yml" +BACKEND_SERVICES="api worker-gpu worker-indexer worker-notifier worker-gost" + +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/" + +echo "==> [2/5] Пересборка и перезапуск бэкенд-сервисов" +cd "$APP" +# shellcheck disable=SC2086 +$COMPOSE up -d --build $BACKEND_SERVICES + +echo "==> [3/5] Миграции БД (с ретраем, пока api поднимается)" +for i in $(seq 1 10); do + if $COMPOSE exec -T api alembic upgrade head; then + break + fi + echo " api ещё не готов, повтор $i/10..." + sleep 5 +done + +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 + +echo "==> Деплой завершён успешно"