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 }} frontend: ${{ steps.filter.outputs.frontend }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: filters: | backend: - 'backend/**' - 'data/sql/**' - '.forgejo/workflows/ci.yml' frontend: - 'frontend/**' - '.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 frontend-tests: runs-on: ubuntu-latest needs: changes if: needs.changes.outputs.frontend == 'true' defaults: run: working-directory: frontend steps: - uses: actions/checkout@v4 - name: Set up Node # Node 20 — совпадает с major из frontend/Dockerfile (node:20-alpine). # cache=npm + cache-dependency-path на lockfile → переиспользуем ~/.npm # между прогонами (mirror Dockerfile's `--mount=type=cache,target=/root/.npm`). uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: frontend/package-lock.json - name: Install deps (npm ci, frozen lockfile) # ТОЧНЫЕ флаги из frontend/Dockerfile (deps stage): # --legacy-peer-deps — Tailwind 4 alpha + React 19 peer-dep mismatches; # --no-audit --no-fund — тише и быстрее в CI. `ci` (не `install`) = # детерминированно из package-lock.json, fail при дрейфе lock vs package.json. run: npm ci --legacy-peer-deps --no-audit --no-fund - name: Test (vitest) # `npm run test` = `vitest run` (single-shot, не watch). Только тесты — # `next build` НАМЕРЕННО не здесь (тяжёлый, verified в deploy.yml build). run: npm run test