From 2d19eef9c216e35ac86e531e1a011b9172e8d486 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 11 Sep 2026 13:16:43 +0300 Subject: [PATCH] ci: add protected production deploy workflow --- .gitea/workflows/deploy.yml | 63 +++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 4 +++ README.md | 10 +++--- app/version.py | 6 ++-- pyproject.toml | 2 +- tests/test_api.py | 2 +- 6 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 .gitea/workflows/deploy.yml diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..bfc1e93 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,63 @@ +name: Deploy production + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + env: + PROD_PATH: /srv/miem_workers + steps: + - uses: actions/checkout@v4 + - name: Prepare release + shell: bash + run: | + set -euo pipefail + tar --exclude=.git --exclude=.env -cf "$RUNNER_TEMP/miem-workers-release.tar" . + printf '%s\n' "$PROD_SSH_KEY" > "$RUNNER_TEMP/deploy_key" + printf '%s\n' "$PROD_KNOWN_HOSTS" > "$RUNNER_TEMP/known_hosts" + chmod 600 "$RUNNER_TEMP/deploy_key" "$RUNNER_TEMP/known_hosts" + env: + PROD_SSH_KEY: ${{ secrets.PROD_SSH_KEY }} + PROD_KNOWN_HOSTS: ${{ secrets.PROD_KNOWN_HOSTS }} + - name: Upload release + shell: bash + run: | + set -euo pipefail + scp -i "$RUNNER_TEMP/deploy_key" \ + -o BatchMode=yes \ + -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" \ + "$RUNNER_TEMP/miem-workers-release.tar" \ + "$PROD_USER@$PROD_HOST:/tmp/miem-workers-release.tar" + env: + PROD_HOST: ${{ secrets.PROD_HOST }} + PROD_USER: ${{ secrets.PROD_USER }} + - name: Deploy and verify + shell: bash + run: | + set -euo pipefail + ssh -i "$RUNNER_TEMP/deploy_key" \ + -o BatchMode=yes \ + -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" \ + "$PROD_USER@$PROD_HOST" \ + "PROD_PATH='$PROD_PATH' bash -s" <<'REMOTE' + set -euo pipefail + cd "$PROD_PATH" + mkdir -p "$HOME/backups/miem_workers" + backup="$HOME/backups/miem_workers/miem_workers-$(date +%Y%m%d-%H%M%S).sql" + docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > "$backup" + docker run --rm \ + -v /tmp/miem-workers-release.tar:/src/release.tar:ro \ + -v "$PROD_PATH":/dst \ + alpine:3.20 sh -c 'tar -xf /src/release.tar -C /dst' + docker compose up -d --build + curl --fail --silent --show-error http://127.0.0.1:8000/api/health + docker compose ps + rm -f /tmp/miem-workers-release.tar + REMOTE + env: + PROD_HOST: ${{ secrets.PROD_HOST }} + PROD_USER: ${{ secrets.PROD_USER }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dd01e5..9d4b5c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.8.2 + +- Добавлен production CD workflow с backup PostgreSQL и проверкой healthcheck после обновления. + ## 0.8.1 - Улучшены доступность, фильтры, состояния и адаптивность административного каталога. diff --git a/README.md b/README.md index a2c4eba..a9eea95 100644 --- a/README.md +++ b/README.md @@ -119,9 +119,11 @@ curl -X POST http://localhost:8000/api/crawl-runs --cookie "miem_admin_session=. docker compose logs -f api docker compose logs -f worker docker compose exec postgres pg_dump -U miem miem_workers > backup.sql -docker compose down -``` - +docker compose down +``` + +Production deploy выполняется workflow `Deploy production` после push в `main` или вручную. Для него нужны Actions secrets `PROD_HOST`, `PROD_USER`, `PROD_SSH_KEY` и `PROD_KNOWN_HOSTS`; `.env` и ключи в репозиторий не добавляются. Перед обновлением workflow сохраняет PostgreSQL backup в домашний каталог deploy-пользователя, пересобирает `api` и `worker` и проверяет `/api/health`. + ## Проверки После установки зависимостей из `requirements.txt` выполните: @@ -133,4 +135,4 @@ node --check app/static/admin.js Тесты с данными используют временную SQLite; отдельные API smoke-тесты запускают приложение с его текущей конфигурацией. Браузерная проверка: `pip install playwright`, `python -m playwright install chromium`, затем `python tests/browser_admin.py`. Она проверяет реальный рендеринг страниц, клавиатуру, диалог, фильтры и восстановление прогресса после ошибки. Скриншоты сохраняются во временную папку; путь выводится в конце. -Версия сервиса: `0.8.1`. Админка всегда показывает версии backend и frontend в footer. +Версия сервиса: `0.8.2`. Админка всегда показывает версии backend и frontend в footer. diff --git a/app/version.py b/app/version.py index b797d9d..9024ee5 100644 --- a/app/version.py +++ b/app/version.py @@ -1,3 +1,3 @@ -APP_VERSION = "0.8.1" -FRONTEND_VERSION = "0.8.1" -BACKEND_VERSION = "0.8.1" +APP_VERSION = "0.8.2" +FRONTEND_VERSION = "0.8.2" +BACKEND_VERSION = "0.8.2" diff --git a/pyproject.toml b/pyproject.toml index 4bcfbab..82d6487 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "miem-workers" -version = "0.8.1" +version = "0.8.2" description = "MIEM employees parser, admin API, and web admin" requires-python = ">=3.11" dependencies = [ diff --git a/tests/test_api.py b/tests/test_api.py index 212f52b..bc24263 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -18,7 +18,7 @@ def test_health_returns_versions(): response = TestClient(app).get("/api/health") assert response.status_code == 200 - assert response.json()["backend_version"] == "0.8.1" + assert response.json()["backend_version"] == "0.8.2" def test_mcp_endpoint_is_removed():