diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml new file mode 100644 index 00000000..172729e4 --- /dev/null +++ b/.forgejo/workflows/ci.yml @@ -0,0 +1,118 @@ +name: CI + +# Forgejo Actions pytest gate for the MAIN backend (backend/). +# WHY THIS FILE EXISTS: Forgejo runs ONLY .forgejo/workflows/* — the +# .github/workflows/ci.yml pytest gate does NOT execute on git.gendsgn.ru +# (proven: Forgejo Actions runs показывают только deploy/build jobs). Без этого +# backend-изменения мержились + деплоились БЕЗ автотестов → live-баг #994 +# (district 500) уехал в прод необнаруженным. Этот workflow добавляет реальный +# gate: backend-сьют GREEN (1687 passed / 0 failed) после CI-rehab 1+2. +# +# Lane = MOCK-ONLY (day 1): нет postgres service-контейнера — сьют мокает БД, +# единственный real-Postgres тест (tests/sql/ mv_layout) self-skip'ается через +# connectivity-probe. PDF-тесты (WeasyPrint) РЕАЛЬНО ИДУТ здесь (libpango +# установлен ниже), тогда как на macOS-dev они runtime-skip'аются. +# FUTURE: добавить `postgis/postgis:16-3.4` service + гонять mv_layout — см. +# .github/workflows/ci.yml как образец service-блока. +on: + pull_request: + branches: [main] + push: + branches: + # Mirror the bot-PR / feature-branch flow в .claude/rules/git-pr.md: + # PR получает gate, прямые пуши в feature-ветки — тоже. + - "feat/**" + - "fix/**" + - "refactor/**" + - "chore/**" + - "docs/**" + - "perf/**" + - "test/**" + - "ci/**" + - "hotfix/**" + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # Paths-filter: gate бежит ТОЛЬКО когда поменялся backend-код или SQL, + # которые этот backend читает (mirror deploy.yml changes-job). На чисто + # frontend/docs PR job no-op'ится → дёшево. + changes: + runs-on: ubuntu-latest + outputs: + backend: ${{ steps.filter.outputs.backend }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + backend: + - 'backend/**' + - 'data/sql/**' + - '.forgejo/workflows/ci.yml' + + backend-tests: + runs-on: ubuntu-latest + needs: changes + if: needs.changes.outputs.backend == 'true' + defaults: + run: + working-directory: backend + env: + # TESTING=1 активирует RBAC-bypass (app/main.py rbac_guard пропускает + # запросы при settings.testing=True) — иначе 401 на всём /api/v1. + TESTING: "1" + # Stub DSN: psycopg v3 требует parseable URL на импорте; реального коннекта + # нет — DB-тесты мокаются, real-DB тест (tests/sql/) self-skip'ается через + # connectivity-probe к этому хосту (5432 недоступен → skip). + DATABASE_URL: postgresql+psycopg://test:test@localhost:5432/test + REDIS_URL: redis://localhost:6379/0 + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + # Официальный standalone-инсталлер. НЕ astral-sh/setup-uv — он ломается + # на Forgejo-runner с PEP 668 externally-managed-environment (#666 CI). + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Install system deps for geo + WeasyPrint + # libpq/gdal/proj/geos — geo-стек (geopandas/shapely/pyproj). + # libcairo2/libpango* — нативные либы WeasyPrint: с ними PDF-тесты + # (tests/test_layout_tz_pdf.py) РЕАЛЬНО ИДУТ на CI (на macOS-dev они + # module-skip'аются probe'ом native-libs). Mirror .github ci.yml:55-57. + run: | + sudo apt-get update + sudo apt-get install -y libpq-dev libgdal-dev libproj-dev libgeos-dev \ + libcairo2 libpango-1.0-0 libpangoft2-1.0-0 + + - name: Install Python deps (incl. dev group — ruff, pytest) + # backend/uv.lock закоммичен → --frozen (детерминированно, fail при + # дрейфе lock vs pyproject). dev-группа ставится по умолчанию (ruff/pytest). + run: uv sync --frozen + + - name: Lint (ruff check) + # Дешёвый fail-fast. NB: `ruff format --check` НАМЕРЕННО НЕ в gate — + # на момент создания 17 файлов repo-wide дали бы day-1 red. Отдельный + # format-pass = future enhancement. `ruff check .` сейчас green. + run: uv run ruff check . + + - name: Test (pytest) + # --ignore=tests/smoke: prod-smoke бьёт по live https://gendsgn.ru + # (помечены @pytest.mark.prod_smoke; pyproject addopts уже их deselect'ит, + # но --ignore — belt-and-suspenders на случай сбора фикстур). + # tests/integration self-skip'ается через requires_test_db (skipif на + # TEST_DATABASE_URL, который тут не задан) → НЕ игнорим, оно чисто skip'ается. + # tests/sql/ mv_layout self-skip'ается через Postgres-connectivity probe + # (5432 недоступен в этом mock-lane) → SKIP. Это intended. + # PDF-тесты ИДУТ (libpango выше). Target: 0 failed, skips OK. + run: uv run pytest -q --ignore=tests/smoke