From 8dc51a4f65dc423fc8bbdcecf1e4ff9001e495aa Mon Sep 17 00:00:00 2001 From: jze9 Date: Wed, 29 Jul 2026 13:34:30 +0500 Subject: [PATCH] =?UTF-8?q?ci:=20=D0=BF=D0=B5=D1=80=D0=B5=D1=81=D0=BE?= =?UTF-8?q?=D0=B1=D0=B8=D1=80=D0=B0=D1=82=D1=8C=20=D1=82=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=BA=D0=BE=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D1=91=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B=20+?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=20=D0=B4=D0=BB=D1=8F=20diff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Первый прогон вслепую пересобирал все 5 бэкенд-образов, включая worker-gpu (torch/faiss ~2 ГБ) — на медленном канале это ~1-2 часа впустую, хотя коммит менял только CI-файлы. Теперь deploy.sh по git-diff с прошлого деплоя (маркер .last_deploy_sha) собирает лишь сервисы с изменённым кодом; фронт пересобирается только при правках services/frontend; правка compose = полная пересборка. Workflow клонирует репозиторий полностью (нужен лог для diff). Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/deploy.yml | 4 +- scripts/deploy.sh | 105 +++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 0ce4272..81ef740 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -16,12 +16,12 @@ jobs: deploy: runs-on: deploy steps: - - name: Клонировать текущий коммит + - name: Клонировать репозиторий (полностью — нужен git-лог для diff) run: | set -euo pipefail SRC="$(mktemp -d)" echo "SRC=$SRC" >> "$GITHUB_ENV" - git clone --depth 1 --branch main \ + git clone --branch main \ https://gitea.jze9.ru/jze9/anti-plagiarism.git "$SRC" - name: Деплой (бэкенд 1.32 + фронтенд CT 102) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 8d9f08c..74aeb98 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,28 +1,55 @@ #!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── -# Деплой anti-plagiarism. +# Деплой anti-plagiarism (умная пересборка — только изменённых сервисов). # # Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32 # (см. .gitea/workflows/deploy.yml). Ожидает: -# - cwd = свежий чекаут репозитория; -# - переменную окружения PVE_PASSWORD (секрет Gitea) для доступа к Proxmox-хосту, -# через который статика кладётся в CT 102 (reverse-proxy nginx). +# - cwd = ПОЛНЫЙ чекаут репозитория (нужен git-лог для diff); +# - переменную PVE_PASSWORD (секрет) для доступа к Proxmox-хосту → CT 102. # -# Что делает: -# 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). +# Пересобирается только то, чей код изменился с прошлого деплоя (маркер 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 # Proxmox-хост, через pct exec которого живёт CT 102 +PVE_HOST=192.168.20.254 COMPOSE="docker compose -f docker-compose.prod.yml" -BACKEND_SERVICES="api worker-gpu worker-indexer worker-notifier worker-gost" +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 \ @@ -30,33 +57,35 @@ rsync -a \ --exclude='node_modules/' --exclude='dist/' \ "$SRC/services" "$SRC/scripts" "$APP/" -echo "==> [2/5] Пересборка и перезапуск бэкенд-сервисов" cd "$APP" -# shellcheck disable=SC2086 -$COMPOSE up -d --build $BACKEND_SERVICES +if [ -n "$REBUILD" ]; then + echo "==> [2/5] Пересборка и перезапуск: $REBUILD" + # shellcheck disable=SC2086 + $COMPOSE up -d --build $REBUILD +else + echo "==> [2/5] Бэкенд-сервисы не менялись — пропуск сборки" +fi -echo "==> [3/5] Миграции БД (с ретраем, пока api поднимается)" +echo "==> [3/5] Миграции БД (с ретраем)" for i in $(seq 1 10); do - if $COMPOSE exec -T api alembic upgrade head; then - break - fi - echo " api ещё не готов, повтор $i/10..." - sleep 5 + $COMPOSE exec -T api alembic upgrade head && break + 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" +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' + 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 ' @@ -71,6 +100,10 @@ nginx -t && nginx -s reload ' rm -f /tmp/academic-dist.tgz REMOTE -rm -f /tmp/academic-dist.tgz + rm -f /tmp/academic-dist.tgz +else + echo "==> [4-5/5] Фронтенд не менялся — пропуск сборки и выкладки" +fi -echo "==> Деплой завершён успешно" +echo "$CUR_SHA" > "$MARKER" +echo "==> Деплой завершён успешно (маркер обновлён: $CUR_SHA)"