From 9f2aa2d2b5721092fb9e8a3cb99843fe546a5f62 Mon Sep 17 00:00:00 2001 From: Alexandr Date: Fri, 3 Apr 2026 14:26:39 +0300 Subject: [PATCH] =?UTF-8?q?ci:=20CI=20=D0=BD=D0=B0=20=D1=82=D0=B5=D0=B3?= =?UTF-8?q?=D0=B0=D1=85=20v*,=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20DEPLOY=5F*,=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B4=D0=B5=D0=BF=D0=BB?= =?UTF-8?q?=D0=BE=D1=8F=20=D0=B8=20=D1=82=D0=B5=D0=B3=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- .env.example | 4 ++++ .gitea/workflows/ci.yml | 18 +++++++++++++++--- .gitea/workflows/deploy.yml | 17 +++++++++++++++++ .gitignore | 2 ++ docs/CICD.md | 14 +++++++++++++- 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 0d2888f..533fef5 100644 --- a/.env.example +++ b/.env.example @@ -36,3 +36,7 @@ VAULT_TOKEN= # Токен: Forgejo → Settings → Applications → Generate New Token (или старый PAT) FORGEJO_URL=https://forgejo.pvenode.ru # FORGEJO_TOKEN= + +# --- Деплой через Forgejo Actions (НЕ сюда) --- +# DEPLOY_HOST, DEPLOY_USER, DEPLOY_PATH, DEPLOY_SSH_KEY задаются только в веб-UI: +# репозиторий → Настройки → Actions → Secrets. Контейнер onGuard24 их не читает. diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 711d2e3..bec640e 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,14 +1,22 @@ -# Forgejo / Gitea Actions — проверка перед деплоем +# Forgejo / Gitea Actions — проверка перед деплоем (совместимо с синтаксисом GitHub Actions). name: CI on: push: branches: [main] + tags: + - "v*" pull_request: + branches: [main] + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: test: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@v4 @@ -17,8 +25,12 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml - name: Pytest run: | - pip install -e ".[dev]" - pytest -q + set -euo pipefail + python -m pip install -U pip + python -m pip install -e ".[dev]" + python -m pytest tests/ -q --tb=short diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 0e6c481..1290363 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -25,10 +25,26 @@ jobs: echo "ref=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" fi + # Без секретов appleboy/ssh-action падает с «missing server host» — даём явную подсказку. + - name: Проверить секреты деплоя + env: + DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} + DEPLOY_USER: ${{ secrets.DEPLOY_USER }} + DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} + DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + run: | + ok=0 + [ -n "$DEPLOY_HOST" ] || { echo "::error::Секрет DEPLOY_HOST пустой. Forgejo → репозиторий → Настройки → Actions → Secrets."; ok=1; } + [ -n "$DEPLOY_USER" ] || { echo "::error::Секрет DEPLOY_USER пустой."; ok=1; } + [ -n "$DEPLOY_PATH" ] || { echo "::error::Секрет DEPLOY_PATH пустой (каталог клона на сервере, напр. /opt/onGuard24)."; ok=1; } + [ -n "$DEPLOY_SSH_KEY" ] || { echo "::error::Секрет DEPLOY_SSH_KEY пустой (приватный SSH-ключ целиком, PEM)."; ok=1; } + exit "$ok" + - name: SSH — fetch, checkout, docker compose uses: appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.DEPLOY_HOST }} + port: "22" username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script_stop: true @@ -38,6 +54,7 @@ jobs: cd "${{ secrets.DEPLOY_PATH }}" git fetch origin --tags --prune git checkout "${{ steps.pick.outputs.ref }}" + # Теги не дают refs/remotes/origin/<тег> — только ветки; для v* срабатывает else. if git show-ref --verify --quiet "refs/remotes/origin/${{ steps.pick.outputs.ref }}"; then git reset --hard "origin/${{ steps.pick.outputs.ref }}" else diff --git a/.gitignore b/.gitignore index 250cc3c..f9b59d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .env .env.local +# локальные секреты на сервере (compose использует только .env) +/env *.pem dist/ bin/ diff --git a/docs/CICD.md b/docs/CICD.md index 1eb53e5..a4583bb 100644 --- a/docs/CICD.md +++ b/docs/CICD.md @@ -6,7 +6,7 @@ | Файл | Назначение | |------|------------| -| `.gitea/workflows/ci.yml` | На `push` в `main` и PR: `pytest`. | +| `.gitea/workflows/ci.yml` | `pytest` на `push` в `main`, на `push` тегов `v*`, на PR в `main` (см. чеклист ниже). | | `.gitea/workflows/deploy.yml` | На `push` тега `v*`: SSH на сервер → `git checkout` → `docker compose build/up`. | | `Dockerfile` | Образ Python 3.12, `pip install .`, entrypoint: `alembic upgrade` + `uvicorn`. | | `docker-compose.yml` | Сервис `onguard24`, порт `8080`, `env_file: .env`. | @@ -36,6 +36,14 @@ sudo docker run -d --restart always --name act_runner \ Стабильные теги: [hub.docker.com/r/gitea/act_runner/tags](https://hub.docker.com/r/gitea/act_runner/tags). +## Чеклист: CI и Deploy проходят + +1. **Runner** зарегистрирован, метка совпадает с `runs-on: ubuntu-latest` в `ci.yml` / `deploy.yml` (или поменяйте `runs-on` на свою метку, например `self-hosted`). +2. **Сеть:** runner может скачивать образы Docker (для job’ов) и при необходимости — **`github.com`** (экшены `actions/checkout`, `actions/setup-python`, `appleboy/ssh-action`). Если Forgejo без выхода в интернет — в админке задайте **зеркало/прокси для Actions** или подставьте полные URL экшенов с вашего зеркала. +3. **CI:** после `push` в `main` или тега `v*` в разделе **Actions** должен быть успешный workflow **CI** (установка Python 3.12, `pytest`). Локально то же самое: `python -m pip install -e ".[dev]" && python -m pytest tests/ -q`. +4. **Deploy:** заданы секреты `DEPLOY_HOST`, `DEPLOY_USER`, `DEPLOY_SSH_KEY`, `DEPLOY_PATH`; с хоста runner выполняется `ssh` на сервер (см. раздел про секреты). Если SSH падает с **host key** — при первом подключении с runner можно зафиксировать отпечаток и добавить в Forgejo секрет (см. раздел «Устранение неполадок»). +5. **На сервере** в `DEPLOY_PATH`: рабочий `git remote`, `docker compose`, без конфликтов портов. + ## Секреты репозитория **Настройки репозитория → Actions → Secrets:** @@ -95,9 +103,13 @@ curl -s http://127.0.0.1:8080/health ## Устранение неполадок +- **`Error: missing server host` / `cd ""` в логе** — в репозитории **не заданы** (или пустые) секреты **`DEPLOY_HOST`**, **`DEPLOY_USER`**, **`DEPLOY_SSH_KEY`**, **`DEPLOY_PATH`**. Задайте их в **Forgejo → репозиторий → Настройки → Actions → Secrets** (имена **точно** как в таблице выше). Workflow теперь падает раньше с явным сообщением, какой секрет пустой. +- **SSH: host key verification failed** — runner впервые видит ключ сервера. Варианты: (а) один раз с машины runner выполнить `ssh-keyscan -H ` и настроить доверие в среде act (зависит от образа); (б) в `appleboy/ssh-action` при необходимости задать входной параметр **`fingerprint`** (SHA256), его можно взять так: `ssh-keyscan -t ed25519 2>/dev/null | ssh-keygen -lf -` (на любой машине, которая видит сервер). Пустой `DEPLOY_SSH_KEY` или ключ с Windows-переводами строк (`\r\n`) тоже даёт ошибки SSH — ключ в секрете должен быть PEM целиком, Unix newlines. +- **CI: не качается `actions/checkout@v4`** — настройте зеркало GitHub для Actions в Forgejo или замените `uses:` на URL экшена с доступного вам хоста (см. документацию вашей версии Forgejo). - **Runner не берёт job** — проверьте `runs-on` и метки runner. - **SSH fails** — `ssh -i key root@DEPLOY_HOST` с машины runner; `known_hosts` при необходимости добавьте в экшен (расширение `appleboy/ssh-action` / `ssh-keyscan`). - **`git checkout` fails** — выполните на сервере `git fetch --tags` вручную, проверьте remote URL и ключ. +- **`fatal: ambiguous argument 'origin/v1.x.x'`** при ручном деплое — у **тегов** нет ref вида `origin/имя-тега`, только `refs/tags/…`. После `git fetch && git checkout v1.x.x` делайте `git reset --hard v1.x.x`, а не `origin/v1.x.x`. В `.gitea/workflows/deploy.yml` это уже учтено (ветка `origin/` только для **веток**). - **База недоступна из контейнера** — в `DATABASE_URL` укажите хост, доступный **из Docker** (не `127.0.0.1` хоста, если БД на хосте — используйте IP хоста или `host.docker.internal` где поддерживается). См. также [VERSIONING.md](VERSIONING.md) и [IRM.md](IRM.md).