chore(ci): деплой на изменение ролей, ruff-гейт для МЕРЫ, удаление мёртвого workflow
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 7s
CI Trade-In / browser-tests (pull_request) Successful in 47s
CI / frontend-tests (pull_request) Successful in 1m39s
CI Trade-In / frontend-checks (pull_request) Successful in 1m41s
CI / openapi-codegen-check (pull_request) Successful in 2m21s
CI Trade-In / backend-tests (pull_request) Successful in 3m32s
CI / backend-tests (pull_request) Successful in 15m25s

- deploy.yml: добавлен auth/** в path-триггеры. app.core.auth кэширует
  roles.yaml на весь lifetime процесса (@lru_cache) — без триггера правка
  ролей молча не применялась до случайного чужого деплоя.
- ci-tradein.yml: добавлен блокирующий шаг `ruff check` — гейт для tradein
  backend его не гонял вообще. Заодно почищены 3 срабатывания RUF059
  (unused unpacked vars) в test_estimator_pure_units.py:381 — версия ruff,
  зафиксированная в uv.lock (0.15.20), новее той, на которой аудит проверял
  "чистое дерево" (0.7.4, как в .pre-commit-config.yaml); лок не трогаем —
  правим 3 места по established convention (`_` уже используется в 3 других
  тестах того же файла).
- Удалён мёртвый .github/workflows/ci.yml (Forgejo его не исполняет; GitHub
  mirror отстал на 500+ коммитов). Блок postgis-service перенесён как
  закомментированный образец в .forgejo/workflows/ci.yml (тестов против
  живой БД пока нет ни одного).
- README.md: убрана ложная привязка mypy к живому CI-гейту (его там нет,
  только ruff+pytest+coverage); убран stale-блок про .github/workflows/
  (deploy-obsidian.yml там больше не лежит).
This commit is contained in:
bot-backend 2026-08-06 20:23:51 +03:00
parent d87c9fa191
commit 79a130736f
6 changed files with 46 additions and 102 deletions

View file

@ -95,13 +95,22 @@ jobs:
restore-keys: |
uv-tradein-${{ runner.os }}-
- name: Sync deps (incl. dev group — pytest)
- name: Sync deps (incl. dev group — pytest, ruff)
# Workspace-лок tradein-mvp/uv.lock TRACKED (с воркспейса #2137; gitignored
# только старый backend/uv.lock) → --frozen детерминирован и зеркалит
# Dockerfile (uv sync --frozen --no-dev там). uv находит workspace root
# вверх от cwd.
run: uv sync --frozen
- name: Lint (ruff check)
# Правила выбраны в tradein-mvp/backend/pyproject.toml ([tool.ruff.lint]
# select = E F I B UP N RUF), но до этого шага их никто не гонял в CI —
# "дерево чистое" было непроверенным утверждением, а не гарантией.
# Версия ruff — та же, что в tradein-mvp/uv.lock (--frozen из шага выше),
# т.е. ровно то, что видит `uv sync --frozen` в Dockerfile.
# Blocking: любое нарушение → job RED (не декоративно).
run: uv run ruff check .
- name: Run pytest (tradein-mvp/backend)
# БЕЗ deselect'ов — сьют гоняется целиком (#2722).
#

View file

@ -1,9 +1,9 @@
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). Без этого
# WHY THIS FILE EXISTS: Forgejo runs ONLY .forgejo/workflows/* — GitHub Actions
# workflows под .github/workflows/ НЕ исполняются на 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.
@ -12,8 +12,9 @@ name: CI
# единственный 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-блока.
# FUTURE: добавить `postgis/postgis:16-3.4` service + гонять mv_layout — образец
# service-блока (перенесённый из удалённого мёртвого .github/workflows/ci.yml,
# который Forgejo никогда не исполнял) закомментирован в job backend-tests ниже.
on:
# ТОЛЬКО pull_request — НЕТ push-триггера на feature-ветки (CI-шторм #1709).
# WHY: раньше был и push: [feat/**,fix/**,...]. Каждый коммит в ветку с открытым
@ -69,6 +70,26 @@ jobs:
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.backend == 'true'
# FUTURE service-блок (см. header comment) — образец для когда заведутся
# тесты против живой БД (сейчас — ни одного; tests/sql/ mv_layout
# self-skip'ается через connectivity-probe, см. env.DATABASE_URL ниже).
# Раскомментировать + добавить реальный DATABASE_URL (localhost:5432,
# gendesign/gendesign) в job env, когда появится хотя бы один тест,
# реально требующий Postgres/PostGIS.
# services:
# postgres:
# image: postgis/postgis:16-3.4
# env:
# POSTGRES_DB: gendesign
# POSTGRES_USER: gendesign
# POSTGRES_PASSWORD: gendesign
# ports:
# - 5432:5432
# options: >-
# --health-cmd "pg_isready -U gendesign"
# --health-interval 5s
# --health-timeout 5s
# --health-retries 10
defaults:
run:
working-directory: backend

View file

@ -20,6 +20,13 @@ on:
# деплоя ниже — без этого триггера правка bootstrap-файла молча не доезжала бы
# до прода до следующего чужого коммита в backend/.
- "ops/db-bootstrap/**"
# RBAC roles config (auth/roles.yaml, bind-mounted read-only в backend+worker,
# см. docker-compose.prod.yml). app.core.auth кэширует парсинг на весь lifetime
# процесса (@lru_cache) — без этого триггера правка ролей вступала бы в силу
# только на следующий чужой деплой (`up -d --force-recreate --no-deps backend
# worker beat` ниже сбрасывает кэш перезапуском процесса; сам файл в образ не
# запекается, ребилда картинок для этого не нужно).
- "auth/**"
workflow_dispatch:
concurrency:

View file

@ -1,91 +0,0 @@
name: CI
on:
push:
branches:
- main
- 'feat/**'
- 'fix/**'
- 'refactor/**'
- 'chore/**'
- 'docs/**'
- 'perf/**'
- 'test/**'
- 'hotfix/**'
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
backend:
runs-on: ubuntu-latest
services:
postgres:
image: postgis/postgis:16-3.4
env:
POSTGRES_DB: gendesign
POSTGRES_USER: gendesign
POSTGRES_PASSWORD: gendesign
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U gendesign"
--health-interval 5s
--health-timeout 5s
--health-retries 10
defaults:
run:
working-directory: backend
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.12
- name: Install system deps for geo + WeasyPrint
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
run: uv sync
- name: Lint (ruff)
run: uv run ruff check .
- name: Type check (mypy strict on core)
run: |
uv run mypy \
app/services/generative \
app/services/site_finder/scorer.py
- name: Test (pytest)
run: uv run pytest -q
env:
DATABASE_URL: postgresql+psycopg://gendesign:gendesign@localhost:5432/gendesign
frontend:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- run: npm ci || npm install
- run: npm run lint
- run: npm run type-check
- run: npm run build

View file

@ -85,12 +85,10 @@ docker-compose.prod.yml main стек (backend, frontend, postgres, redis, work
docker-compose.obsidian.yml obsidian-стек (CouchDB) — деплоится отдельно
docker-compose.uptime.yml Uptime Kuma мониторинг (status.gendsgn.ru) — отдельный стек, запуск вручную
.forgejo/workflows/ (Forgejo Actions — основной CI/CD после миграции 16.05.2026)
├── ci.yml lint (ruff) + mypy + pytest на PR
├── ci.yml lint (ruff) + pytest на PR
├── deploy.yml main → пересборка backend/frontend образов + auto-apply data/sql/*.sql + SSH deploy
├── deploy-tradein.yml tradein-mvp стек (отдельный пайплайн + свой _schema_migrations)
└── stale-claims.yml авто-снятие протухших claim-меток в bot-пайплайне
.github/workflows/ (остаточные — только obsidian-стек на GitHub)
└── deploy-obsidian.yml obsidian-стек (CouchDB compose changes + bootstrap)
```
---
@ -158,7 +156,7 @@ docker-compose.uptime.yml Uptime Kuma мониторинг (status.gendsgn.ru
**Forgejo Actions deploys** (self-hosted `git.gendsgn.ru`, мигрировано с GitHub Actions 16.05.2026):
- [`.forgejo/workflows/ci.yml`](.forgejo/workflows/ci.yml) — на PR: ruff lint + mypy (selective strict) + pytest. Блокирует merge при провале.
- [`.forgejo/workflows/ci.yml`](.forgejo/workflows/ci.yml) — на PR: ruff lint + pytest (coverage gate ≥65%). mypy strict в гейте не гоняется (доступен вручную — `uv run mypy app/services/generative app/services/site_finder/scorer.py`). Блокирует merge при провале.
- [`.forgejo/workflows/deploy.yml`](.forgejo/workflows/deploy.yml) — main: триггер на `backend/**`, `frontend/**`, `Caddyfile`, `docker-compose.prod.yml`, `data/sql/**`. Build backend lean + worker-with-chromium + frontend → push в приватный GHCR → SSH `git reset --hard`, **auto-apply pending `data/sql/NN_*.sql` через `_schema_migrations`** (idempotent, см. ниже про миграции), sed `SENTRY_RELEASE=$IMAGE_TAG` в `backend/.env.runtime`, `compose pull && up -d`, `caddy reload`, `curl /health`.
- [`.forgejo/workflows/deploy-tradein.yml`](.forgejo/workflows/deploy-tradein.yml) — tradein-mvp стек (отдельный пайплайн).
- [`.forgejo/workflows/deploy-obsidian.yml`](.forgejo/workflows/deploy-obsidian.yml) — obsidian: триггер на `docker-compose.obsidian.yml`, `scripts/setup-couchdb.sh`, `docs/obsidian-livesync.md`. Без сборки образов (couchdb:3 с DockerHub), SSH `compose up -d` + idempotent bootstrap (CORS, DB, лимиты). *(до 2026-07-05 ошибочно лежал в `.github/workflows/` — там ни разу не исполнился, см. issue #2416; контейнер держался вручную.)*

View file

@ -378,7 +378,7 @@ def test_corridor_clamp_above_corridor_tier_c_clamps() -> None:
def test_corridor_clamp_inside_corridor_is_noop() -> None:
# Эконом/комфорт: headline в коридоре (с учётом slack) → ничего не меняется.
new_ppm2, new_price, new_low, new_high, clamped = _clamp(
new_ppm2, _, _, _, clamped = _clamp(
median_ppm2=140_000, corridor_high=130_000, count=20, tier="C"
)
assert clamped is False