4d603637e5
Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
4.3 KiB
Bash
126 lines
4.3 KiB
Bash
#!/usr/bin/env bash
|
||
# Полное обновление SAC на сервере (native).
|
||
# Установка: sudo cp /opt/security-alert-center/deploy/sac-deploy.sh /opt/sac-deploy.sh
|
||
# sudo chmod 755 /opt/sac-deploy.sh
|
||
# Запуск: sudo /opt/sac-deploy.sh
|
||
#
|
||
# Выполняет от пользователя sac: git pull, pip, alembic, npm build.
|
||
# От root: systemctl restart sac-api.
|
||
|
||
set -euo pipefail
|
||
|
||
APP_ROOT="${SAC_APP_ROOT:-/opt/security-alert-center}"
|
||
APP_USER="${SAC_APP_USER:-sac}"
|
||
SERVICE_NAME="${SAC_SERVICE:-sac-api}"
|
||
CONFIG_FILE="${SAC_CONFIG_FILE:-${APP_ROOT}/config/sac-api.env}"
|
||
|
||
log() { printf '[sac-deploy] %s\n' "$*"; }
|
||
die() { printf '[sac-deploy] ERROR: %s\n' "$*" >&2; exit 1; }
|
||
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
die "Запускайте от root: sudo /opt/sac-deploy.sh"
|
||
fi
|
||
|
||
id "$APP_USER" &>/dev/null || die "Пользователь ${APP_USER} не существует"
|
||
|
||
if [ ! -d "${APP_ROOT}/.git" ]; then
|
||
die "Не найден git-репозиторий: ${APP_ROOT}"
|
||
fi
|
||
|
||
log "Выравнивание владельца ${APP_ROOT} → ${APP_USER}:${APP_USER}"
|
||
chown -R "${APP_USER}:${APP_USER}" "${APP_ROOT}"
|
||
|
||
log "git fetch + sync"
|
||
sudo -u "${APP_USER}" git -C "${APP_ROOT}" fetch --prune
|
||
|
||
if sudo -u "${APP_USER}" git -C "${APP_ROOT}" pull --ff-only 2>/dev/null; then
|
||
log "git: fast-forward OK"
|
||
else
|
||
UPSTREAM="$(sudo -u "${APP_USER}" git -C "${APP_ROOT}" rev-parse --abbrev-ref '@{u}' 2>/dev/null || true)"
|
||
if [ -z "${UPSTREAM}" ]; then
|
||
UPSTREAM="origin/main"
|
||
fi
|
||
log "WARN: fast-forward невозможен (часто после force-push на kalinamall) — reset --hard ${UPSTREAM}"
|
||
sudo -u "${APP_USER}" git -C "${APP_ROOT}" reset --hard "${UPSTREAM}"
|
||
fi
|
||
|
||
if [ ! -f "${CONFIG_FILE}" ]; then
|
||
die "Нет конфига: ${CONFIG_FILE}"
|
||
fi
|
||
|
||
VENV="${APP_ROOT}/backend/.venv"
|
||
if [ ! -x "${VENV}/bin/pip" ]; then
|
||
die "Нет venv: ${VENV} — сначала установите по install-ubuntu-24.04-native.md"
|
||
fi
|
||
|
||
log "pip install -r requirements.txt"
|
||
sudo -u "${APP_USER}" "${VENV}/bin/pip" install -q -r "${APP_ROOT}/backend/requirements.txt"
|
||
|
||
log "alembic upgrade head"
|
||
sudo -u "${APP_USER}" bash -c "
|
||
set -a
|
||
# shellcheck source=/dev/null
|
||
source '${CONFIG_FILE}'
|
||
set +a
|
||
cd '${APP_ROOT}/backend' && '${VENV}/bin/alembic' upgrade head
|
||
"
|
||
|
||
if [ -f "${APP_ROOT}/frontend/package.json" ]; then
|
||
if ! command -v npm >/dev/null 2>&1; then
|
||
log "npm не найден — пропуск сборки UI (apt install nodejs npm)"
|
||
else
|
||
log "frontend: npm ci / build"
|
||
sudo -u "${APP_USER}" bash -c "
|
||
cd '${APP_ROOT}/frontend'
|
||
if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
||
npm run build
|
||
"
|
||
if [ ! -f "${APP_ROOT}/frontend/dist/index.html" ]; then
|
||
die "Сборка UI не создала frontend/dist/index.html"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [ -f "${APP_ROOT}/deploy/systemd/${SERVICE_NAME}.service" ]; then
|
||
SYSTEMD_DST="/etc/systemd/system/${SERVICE_NAME}.service"
|
||
if [ ! -f "${SYSTEMD_DST}" ] || ! cmp -s "${APP_ROOT}/deploy/systemd/${SERVICE_NAME}.service" "${SYSTEMD_DST}"; then
|
||
log "Обновление systemd unit → ${SYSTEMD_DST}"
|
||
cp "${APP_ROOT}/deploy/systemd/${SERVICE_NAME}.service" "${SYSTEMD_DST}"
|
||
systemctl daemon-reload
|
||
fi
|
||
fi
|
||
|
||
log "Проверка DATABASE_URL (как у uvicorn через SAC_CONFIG_FILE)"
|
||
sudo -u "${APP_USER}" bash -c "
|
||
export SAC_CONFIG_FILE='${CONFIG_FILE}'
|
||
cd '${APP_ROOT}/backend'
|
||
'${VENV}/bin/python' -c \"
|
||
from sqlalchemy import create_engine, text
|
||
from app.config import get_settings
|
||
engine = create_engine(get_settings().database_url, pool_pre_ping=True)
|
||
with engine.connect() as conn:
|
||
conn.execute(text('SELECT 1'))
|
||
print('db: OK')
|
||
\"
|
||
" || die "PostgreSQL: проверьте DATABASE_URL в ${CONFIG_FILE} и systemctl status postgresql"
|
||
|
||
log "systemctl restart ${SERVICE_NAME}"
|
||
systemctl restart "${SERVICE_NAME}"
|
||
systemctl is-active --quiet "${SERVICE_NAME}" || die "${SERVICE_NAME} не active"
|
||
|
||
HEALTH_OK=0
|
||
for _ in 1 2 3 4 5 6; do
|
||
sleep 2
|
||
if curl -sf "http://127.0.0.1:8000/health" >/dev/null 2>&1; then
|
||
HEALTH_OK=1
|
||
break
|
||
fi
|
||
done
|
||
if [ "${HEALTH_OK}" -eq 1 ]; then
|
||
log "health: OK (http://127.0.0.1:8000/health)"
|
||
else
|
||
die "${SERVICE_NAME} запущен, но /health не ответил — journalctl -u ${SERVICE_NAME} -n 80 --no-pager"
|
||
fi
|
||
|
||
log "Готово."
|