ci: пересобирать только изменённые сервисы + полный клон для diff
All checks were successful
Deploy / deploy (push) Successful in 3s

Первый прогон вслепую пересобирал все 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 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-07-29 13:34:30 +05:00
parent 34fa495fb1
commit 8dc51a4f65
2 changed files with 71 additions and 38 deletions

View File

@@ -16,12 +16,12 @@ jobs:
deploy: deploy:
runs-on: deploy runs-on: deploy
steps: steps:
- name: Клонировать текущий коммит - name: Клонировать репозиторий (полностью — нужен git-лог для diff)
run: | run: |
set -euo pipefail set -euo pipefail
SRC="$(mktemp -d)" SRC="$(mktemp -d)"
echo "SRC=$SRC" >> "$GITHUB_ENV" echo "SRC=$SRC" >> "$GITHUB_ENV"
git clone --depth 1 --branch main \ git clone --branch main \
https://gitea.jze9.ru/jze9/anti-plagiarism.git "$SRC" https://gitea.jze9.ru/jze9/anti-plagiarism.git "$SRC"
- name: Деплой (бэкенд 1.32 + фронтенд CT 102) - name: Деплой (бэкенд 1.32 + фронтенд CT 102)

View File

@@ -1,28 +1,55 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
# Деплой anti-plagiarism. # Деплой anti-plagiarism (умная пересборка — только изменённых сервисов).
# #
# Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32 # Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32
# (см. .gitea/workflows/deploy.yml). Ожидает: # (см. .gitea/workflows/deploy.yml). Ожидает:
# - cwd = свежий чекаут репозитория; # - cwd = ПОЛНЫЙ чекаут репозитория (нужен git-лог для diff);
# - переменную окружения PVE_PASSWORD (секрет Gitea) для доступа к Proxmox-хосту, # - переменную PVE_PASSWORD (секрет) для доступа к Proxmox-хосту → CT 102.
# через который статика кладётся в CT 102 (reverse-proxy nginx).
# #
# Что делает: # Пересобирается только то, чей код изменился с прошлого деплоя (маркер SHA в
# 1. Синхронизирует код (services/ + scripts/) в /home/user/anti-plagiarism, # $APP/.last_deploy_sha). Это критично: worker-gpu тянет torch/faiss (~2 ГБ),
# НЕ трогая .env и локально-правленый docker-compose.prod.yml. # его нельзя пересобирать без нужды. Если менялся docker-compose.prod.yml
# 2. Пересобирает и перезапускает бэкенд-сервисы (api + воркеры). # пересобираются все бэкенд-сервисы.
# 3. Применяет миграции БД.
# 4. Собирает фронтенд (npm build) в статику.
# 5. Выкладывает статику в /var/www/academic на CT 102 (с бэкапом + reload nginx).
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail set -euo pipefail
APP=/home/user/anti-plagiarism APP=/home/user/anti-plagiarism
SRC="$(pwd)" 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" 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)" echo "==> [1/5] Синхронизация кода в $APP (сохраняя .env и compose)"
rsync -a \ rsync -a \
@@ -30,33 +57,35 @@ rsync -a \
--exclude='node_modules/' --exclude='dist/' \ --exclude='node_modules/' --exclude='dist/' \
"$SRC/services" "$SRC/scripts" "$APP/" "$SRC/services" "$SRC/scripts" "$APP/"
echo "==> [2/5] Пересборка и перезапуск бэкенд-сервисов"
cd "$APP" cd "$APP"
# shellcheck disable=SC2086 if [ -n "$REBUILD" ]; then
$COMPOSE up -d --build $BACKEND_SERVICES 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 for i in $(seq 1 10); do
if $COMPOSE exec -T api alembic upgrade head; then $COMPOSE exec -T api alembic upgrade head && break
break echo " api ещё не готов, повтор $i/10..."; sleep 5
fi
echo " api ещё не готов, повтор $i/10..."
sleep 5
done done
echo "==> [4/5] Сборка фронтенда" if [ "$FRONTEND_CHANGED" = 1 ]; then
docker run --rm -v "$APP/services/frontend":/app -w /app node:20-slim \ 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" sh -c "npm install --no-audit --no-fund --loglevel=error && npm run build"
echo "==> [5/5] Выкладка статики на CT 102 (через $PVE_HOST)" echo "==> [5/5] Выкладка статики на CT 102 (через $PVE_HOST)"
cd "$APP/services/frontend/dist" cd "$APP/services/frontend/dist"
tar czf /tmp/academic-dist.tgz . tar czf /tmp/academic-dist.tgz .
export SSHPASS="${PVE_PASSWORD:?PVE_PASSWORD не задан}" export SSHPASS="${PVE_PASSWORD:?PVE_PASSWORD не задан}"
SSHOPT="-o StrictHostKeyChecking=no -o ConnectTimeout=10" SSHOPT="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
sshpass -e scp $SSHOPT /tmp/academic-dist.tgz "root@${PVE_HOST}:/tmp/academic-dist.tgz" sshpass -e scp $SSHOPT /tmp/academic-dist.tgz "root@${PVE_HOST}:/tmp/academic-dist.tgz"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
sshpass -e ssh $SSHOPT "root@${PVE_HOST}" 'bash -s' <<'REMOTE' sshpass -e ssh $SSHOPT "root@${PVE_HOST}" 'bash -s' <<'REMOTE'
set -e set -e
pct push 102 /tmp/academic-dist.tgz /tmp/academic-dist.tgz pct push 102 /tmp/academic-dist.tgz /tmp/academic-dist.tgz
pct exec 102 -- bash -c ' pct exec 102 -- bash -c '
@@ -71,6 +100,10 @@ nginx -t && nginx -s reload
' '
rm -f /tmp/academic-dist.tgz rm -f /tmp/academic-dist.tgz
REMOTE 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)"