feat(deploy): переключить .env на проде на Infisical вместо ручного файла
deploy.sh теперь на каждом запуске логинится в Infisical (Machine Identity Universal Auth) и генерирует .env из окружения prod перед синком кода и поднятием контейнеров. Если Infisical недоступен или вернул подозрительно мало ключей — деплой падает раньше, не трогая рабочий .env на сервере. Добавлен шаг compose up -d без --build для всех бэкенд-сервисов после сборки изменившихся — иначе правка секрета без изменения кода не попадала бы в уже запущенные контейнеры (docker compose пересоздаёт только то, чей эффективный конфиг реально изменился, остальное не трогает).
This commit is contained in:
@@ -58,6 +58,8 @@ jobs:
|
|||||||
- name: Деплой (бэкенд 1.32 + фронтенд CT 102)
|
- name: Деплой (бэкенд 1.32 + фронтенд CT 102)
|
||||||
env:
|
env:
|
||||||
PVE_PASSWORD: ${{ secrets.PVE_PASSWORD }}
|
PVE_PASSWORD: ${{ secrets.PVE_PASSWORD }}
|
||||||
|
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
|
||||||
|
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cd "$SRC"
|
cd "$SRC"
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
# Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32
|
# Запускается Gitea Actions runner'ом в host-режиме прямо на app-хосте 1.32
|
||||||
# (см. .gitea/workflows/deploy.yml). Ожидает:
|
# (см. .gitea/workflows/deploy.yml). Ожидает:
|
||||||
# - cwd = ПОЛНЫЙ чекаут репозитория (нужен git-лог для diff);
|
# - cwd = ПОЛНЫЙ чекаут репозитория (нужен git-лог для diff);
|
||||||
# - переменную PVE_PASSWORD (секрет) для доступа к Proxmox-хосту → CT 102.
|
# - переменную PVE_PASSWORD (секрет) для доступа к Proxmox-хосту → CT 102;
|
||||||
|
# - INFISICAL_CLIENT_ID / INFISICAL_CLIENT_SECRET (Machine Identity
|
||||||
|
# "claude-ai", Universal Auth) — .env на проде теперь генерируется из
|
||||||
|
# Infisical (prod) на каждом деплое, а не правится руками на сервере.
|
||||||
#
|
#
|
||||||
# Пересобирается только то, чей код изменился с прошлого деплоя (маркер SHA в
|
# Пересобирается только то, чей код изменился с прошлого деплоя (маркер SHA в
|
||||||
# $APP/.last_deploy_sha). Это критично: worker-gpu тянет torch/faiss (~2 ГБ),
|
# $APP/.last_deploy_sha). Это критично: worker-gpu тянет torch/faiss (~2 ГБ),
|
||||||
@@ -51,7 +54,41 @@ else
|
|||||||
fi
|
fi
|
||||||
REBUILD="$(echo "$REBUILD" | xargs || true)"
|
REBUILD="$(echo "$REBUILD" | xargs || true)"
|
||||||
|
|
||||||
echo "==> [1/5] Синхронизация кода в $APP (сохраняя .env и compose)"
|
echo "==> [1/6] Секреты из Infisical (prod) → .env"
|
||||||
|
INFISICAL_BIN="$APP/.infisical-cli"
|
||||||
|
if [ ! -x "$INFISICAL_BIN" ]; then
|
||||||
|
echo " ставлю infisical CLI (разово)"
|
||||||
|
curl -sSL -o /tmp/infisical-cli.tar.gz \
|
||||||
|
"https://github.com/Infisical/infisical/releases/download/infisical-cli%2Fv0.31.1/infisical_0.31.1_linux_amd64.tar.gz"
|
||||||
|
tar xzf /tmp/infisical-cli.tar.gz -C /tmp infisical
|
||||||
|
mv /tmp/infisical "$INFISICAL_BIN"
|
||||||
|
rm -f /tmp/infisical-cli.tar.gz
|
||||||
|
fi
|
||||||
|
|
||||||
|
INFISICAL_DOMAIN=https://infisical.jze9.ru/api
|
||||||
|
INFISICAL_PROJECT_ID=a2e7505e-573f-43e9-872b-4ed7a6bed7b8
|
||||||
|
INFISICAL_TOKEN="$("$INFISICAL_BIN" login --method=universal-auth \
|
||||||
|
--client-id="${INFISICAL_CLIENT_ID:?INFISICAL_CLIENT_ID не задан}" \
|
||||||
|
--client-secret="${INFISICAL_CLIENT_SECRET:?INFISICAL_CLIENT_SECRET не задан}" \
|
||||||
|
--domain="$INFISICAL_DOMAIN" --plain --silent)"
|
||||||
|
|
||||||
|
TMP_ENV="$(mktemp)"
|
||||||
|
"$INFISICAL_BIN" export --token="$INFISICAL_TOKEN" --domain="$INFISICAL_DOMAIN" \
|
||||||
|
--projectId="$INFISICAL_PROJECT_ID" --env=prod --format=dotenv --expand=false \
|
||||||
|
--silent > "$TMP_ENV"
|
||||||
|
|
||||||
|
# Не затирать рабочий .env, если Infisical вернул мало строк (недоступен/пуст) —
|
||||||
|
# иначе следующий шаг раскатит прод с пустым конфигом.
|
||||||
|
ENV_LINES="$(grep -cE '^[A-Za-z_][A-Za-z0-9_]*=' "$TMP_ENV" || true)"
|
||||||
|
if [ "$ENV_LINES" -lt 20 ]; then
|
||||||
|
echo "ОШИБКА: Infisical export вернул только $ENV_LINES строк (ожидалось ~38) — сервис недоступен или пуст. Деплой прерван, .env на проде не тронут."
|
||||||
|
rm -f "$TMP_ENV"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mv "$TMP_ENV" "$APP/.env"
|
||||||
|
echo " OK: $ENV_LINES секретов"
|
||||||
|
|
||||||
|
echo "==> [2/6] Синхронизация кода в $APP (сохраняя .env и compose)"
|
||||||
rsync -a \
|
rsync -a \
|
||||||
--exclude='__pycache__/' --exclude='*.pyc' --exclude='.pytest_cache/' \
|
--exclude='__pycache__/' --exclude='*.pyc' --exclude='.pytest_cache/' \
|
||||||
--exclude='node_modules/' --exclude='dist/' \
|
--exclude='node_modules/' --exclude='dist/' \
|
||||||
@@ -59,25 +96,33 @@ rsync -a \
|
|||||||
|
|
||||||
cd "$APP"
|
cd "$APP"
|
||||||
if [ -n "$REBUILD" ]; then
|
if [ -n "$REBUILD" ]; then
|
||||||
echo "==> [2/5] Пересборка и перезапуск: $REBUILD"
|
echo "==> [3/6] Пересборка и перезапуск: $REBUILD"
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
$COMPOSE up -d --build $REBUILD
|
$COMPOSE up -d --build $REBUILD
|
||||||
else
|
else
|
||||||
echo "==> [2/5] Бэкенд-сервисы не менялись — пропуск сборки"
|
echo "==> [3/6] Бэкенд-сервисы не менялись — пропуск сборки"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "==> [3/5] Миграции БД (с ретраем)"
|
# Секреты в .env могли поменяться без изменения кода (правка в Infisical) —
|
||||||
|
# docker compose сам не пересоздаст контейнер, чей env_file изменился, если
|
||||||
|
# просто повторно не позвать up -d. Дёшево и идемпотентно: compose пересоздаёт
|
||||||
|
# только контейнеры с реально изменившимся конфигом, остальные не трогает.
|
||||||
|
echo "==> [4/6] Применение секретов к неизменившимся сервисам (если менялись)"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$COMPOSE up -d $ALL_BACKEND
|
||||||
|
|
||||||
|
echo "==> [5/6] Миграции БД (с ретраем)"
|
||||||
for i in $(seq 1 10); do
|
for i in $(seq 1 10); do
|
||||||
$COMPOSE exec -T api alembic upgrade head && break
|
$COMPOSE exec -T api alembic upgrade head && break
|
||||||
echo " api ещё не готов, повтор $i/10..."; sleep 5
|
echo " api ещё не готов, повтор $i/10..."; sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$FRONTEND_CHANGED" = 1 ]; then
|
if [ "$FRONTEND_CHANGED" = 1 ]; then
|
||||||
echo "==> [4/5] Сборка фронтенда"
|
echo "==> [6/6] Сборка фронтенда"
|
||||||
docker run --rm -v "$APP/services/frontend":/app -w /app node:20-slim \
|
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 "==> [6/6] Выкладка статики на 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 не задан}"
|
||||||
@@ -102,7 +147,7 @@ rm -f /tmp/academic-dist.tgz
|
|||||||
REMOTE
|
REMOTE
|
||||||
rm -f /tmp/academic-dist.tgz
|
rm -f /tmp/academic-dist.tgz
|
||||||
else
|
else
|
||||||
echo "==> [4-5/5] Фронтенд не менялся — пропуск сборки и выкладки"
|
echo "==> [6/6] Фронтенд не менялся — пропуск сборки и выкладки"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$CUR_SHA" > "$MARKER"
|
echo "$CUR_SHA" > "$MARKER"
|
||||||
|
|||||||
Reference in New Issue
Block a user