Some checks failed
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Failing after 21s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 55s
CI Trade-In / frontend-checks (pull_request) Successful in 1m26s
CI / openapi-codegen-check (pull_request) Successful in 2m7s
CI / backend-tests (pull_request) Successful in 16m53s
_manifest_applied.txt по построению не мог покраснеть. Тест считал «новым» любой файл, которого нет в списке, а новые файлы от списка освобождены (докстринг test_manifest_covers_all_but_new_files: «НЕ требует, чтобы новый файл уже был в manifest»). Забытое имя и новая миграция PR для гейта — одно и то же, поэтому дрейф был не пропуском проверки, а её штатным исключением. Замер на main 2026-08-07: 15 имён не дописано, все четыре теста зелёные — через сутки после того, как #2692 догнал список руками. Список при этом был лишь копией того, что git и так знает: deploy-tradein.yml применяет КАЖДЫЙ data/sql/*.sql из main под ON_ERROR_STOP, то есть «файл доехал до main» и есть «имя закреплено на проде». Ведём эталон в git — и дрейфовать становится нечему. Кросс-ветковая дыра закрыта тем же ходом: номер нового файла сверяется с ПОЛНЫМ origin/main, а не с рабочим деревом, поэтому коллизия с миграцией, смерженной после ветвления, находится. Проверено на живом PR #2754 (234_trade_in_estimates_retain_until против 234_scrape_runs_ban_kind_unknown из main): старый гейт зелёный, новый красный. Удаление/переименование применённой миграции сверяется с ТОЧКОЙ ВЕТВЛЕНИЯ, а не с origin/main: иначе ветка недельной давности краснела бы за чужие миграции. Проверено — ветка от 2026-07-30 при +43 миграциях в main зелёная. CI: checkout переведён на fetch-depth 0 + отдельный fetch main. Этот Forgejo не публикует refs/pull/N/merge (1620 */head, ноль */merge), а на depth=1 нет ни origin/main, ни общего предка — без этого гейту не с чем сверять, и он намеренно красный, а не тихо пропущенный. Контракт сведён к одной формулировке — докстринг test_migration_numbering.py; шапка манифеста, правило 3, хвост манифеста и рецепт из .claude/rules удалены или заменены ссылкой. Заодно исправлен сам рецепт: `git ls-tree` без `-r` печатает каталог, а не файлы. Refs #2683
65 lines
3.8 KiB
Markdown
65 lines
3.8 KiB
Markdown
---
|
||
paths:
|
||
- tradein-mvp/**/*.py
|
||
- tradein-mvp/**/*.sql
|
||
- tradein-mvp/frontend/**/*.{ts,tsx}
|
||
---
|
||
|
||
# trade-in (Mera) conventions — `tradein-mvp/`
|
||
|
||
Отдельный продукт + отдельный стек от Site Finder. Backend `tradein-mvp/backend/app/**`,
|
||
SQL `tradein-mvp/backend/data/sql/NN_*.sql`, frontend `tradein-mvp/frontend/`. Backend Python
|
||
подчиняется `.claude/rules/backend.md` (psycopg v3, CAST, ruff-100), SQL — `.claude/rules/sql.md`
|
||
(NN naming, idempotency). Ниже — то, что СПЕЦИФИЧНО для trade-in.
|
||
|
||
## Две БД — не путай
|
||
|
||
- **`postgres-tradein`** (db=tradein) — скрейпленные листинги avito/cian/yandex, estimator,
|
||
coverage, houses. Для ЛЮБОЙ tradein-задачи метрики/схему бери отсюда (`mcp__postgres-tradein__*`).
|
||
- **`postgres-gendesign`** (db=gendesign) — Site Finder, НЕ trade-in.
|
||
|
||
## Тестировать HTTP только ВНУТРИ контейнера
|
||
|
||
SSH-туннель `localhost:8000` → `gendesign-backend` (Site Finder, db=gendesign, старый код),
|
||
**НЕ** tradein-backend (порт не опубликован на хост). curl на туннель:8000 по trade-in endpoint =
|
||
мусор / чужая БД (стоило ~2ч). Тест trade-in API только изнутри контейнера:
|
||
|
||
```bash
|
||
ssh gendesign # затем:
|
||
docker exec tradein-backend curl -s localhost:8000/<route> # админ-роуты: -H "X-Authenticated-User: admin"
|
||
docker exec tradein-postgres psql -U <user> -d tradein -c "..."
|
||
```
|
||
|
||
## Scheduler крутится в `tradein-scraper`, не `tradein-backend`
|
||
|
||
In-app scheduler (`scrape_schedules`, tick 60s, `python -m app.scheduler_main`,
|
||
`SCHEDULER_ENABLE=true`) живёт в контейнере **`tradein-scraper`**; в `tradein-backend` намеренно
|
||
`false`. Статус scheduled-задач смотри в scraper-контейнере (logs/printenv), не в backend.
|
||
Ручной smoke: `UPDATE scrape_schedules SET next_run_at=now() WHERE source='X'` → подхват ≤60s.
|
||
|
||
## SQL авто-применяется на ПРОД (strict)
|
||
|
||
`tradein-mvp/backend/data/sql/NN_*.sql` применяется автоматически на деплое через `_schema_migrations`
|
||
в `.forgejo/workflows/deploy-tradein.yml` (НЕ init-only, strict exit-1). Idempotency критична —
|
||
деструктивный DDL хитит прод на деплое.
|
||
|
||
**Номер новой миграции сверяй с `origin/main`, не с локальным `ls`** — локальное дерево не видит
|
||
миграций, смерженных после ветвления (так разъехались 212 в #2682 и 234 в #2754):
|
||
|
||
```bash
|
||
git fetch origin main
|
||
git ls-tree -r --name-only origin/main -- tradein-mvp/backend/data/sql | tail
|
||
```
|
||
|
||
`-r` обязателен — без него `ls-tree` печатает сам каталог одной строкой, а не файлы.
|
||
|
||
Правило целиком — в докстринге `tradein-mvp/backend/tests/test_migration_numbering.py` (единственная
|
||
формулировка контракта, #2683); он же гейтит его в CI. Дописывать имя в какой-либо список НЕ надо:
|
||
`_manifest_applied.txt` удалён — он отставал и по построению не мог покраснеть.
|
||
|
||
## Rapid-merge trap
|
||
|
||
2 tradein-PR мержа за секунды → backend `test`-job cancelled → `build-backend` пропущен →
|
||
«deploy success» на СТАРОМ образе (нет нового кода/deps). Сверяй `:latest` Created-timestamp vs
|
||
время мержа + smoke в контейнере; не верь «деплой прошёл». Recovery: ручной `workflow_dispatch`
|
||
для `deploy-tradein.yml`.
|