diff --git a/.forgejo/workflows/ci-tradein.yml b/.forgejo/workflows/ci-tradein.yml index 5b228267..16e35a8b 100644 --- a/.forgejo/workflows/ci-tradein.yml +++ b/.forgejo/workflows/ci-tradein.yml @@ -183,13 +183,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). # diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 7303d4cf..46b570b5 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -20,6 +20,14 @@ on: # деплоя ниже — без этого триггера правка bootstrap-файла молча не доезжала бы # до прода до следующего чужого коммита в backend/. - "ops/db-bootstrap/**" + # RBAC roles config (auth/roles.yaml, bind-mounted read-only ТОЛЬКО в backend — + # см. docker-compose.prod.yml; worker монтирует лишь ./data и ./reports). + # app.core.auth кэширует парсинг на весь lifetime процесса (@lru_cache) — без + # этого триггера правка ролей вступала бы в силу в случайный момент, только на + # следующий чужой деплой (`up -d --force-recreate --no-deps backend worker beat` + # ниже сбрасывает кэш перезапуском процесса; сам файл в образ не запекается, + # ребилда картинок для этого не нужно). + - "auth/**" workflow_dispatch: concurrency: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a2c3ed7e..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -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 diff --git a/README.md b/README.md index 36f5925d..2a1acaa7 100644 --- a/README.md +++ b/README.md @@ -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; контейнер держался вручную.)* diff --git a/tradein-mvp/backend/tests/test_estimator_pure_units.py b/tradein-mvp/backend/tests/test_estimator_pure_units.py index 1170e4c2..b2f64d2c 100644 --- a/tradein-mvp/backend/tests/test_estimator_pure_units.py +++ b/tradein-mvp/backend/tests/test_estimator_pure_units.py @@ -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