Ничего не апгрейдит. Убирает три способа собрать образ не тем, что в репозитории.
1. tradein-mvp/backend/uv.lock — удалён (файл был untracked, в .gitignore
с рождения). Мера — uv-workspace, сборка берёт КОРНЕВОЙ tradein-mvp/uv.lock
(`context: ./tradein-mvp`, `COPY pyproject.toml uv.lock ./` + `uv sync
--frozen`). Лок, созданный `uv lock` из backend/, не читает никто, и он тихо
разошёлся с рабочим: на 2026-08-29 в нём pillow 12.2.0, starlette 1.0.1,
python-multipart 0.0.29, pydantic-settings 2.14.1, weasyprint 68.1 — пять
пакетов с открытыми advisory, которых в реальной сборке Меры нет вообще.
Именно он подмешал пять фантомных строк в OSV-скан этой серии.
Строка .gitignore остаётся (второй лок не нужен), но теперь с объяснением
почему — иначе следующий читатель снимет ignore и закоммитит призрак.
2. backend/Dockerfile — `COPY pyproject.toml uv.lock* ./` + `if [ -f uv.lock ];
then uv sync --frozen ...; else uv sync ...; fi` → без глоба и без фолбэка.
Глоб + фолбэк означали: пропал лок — сборка не падает, а молча переключается
на резолв «свежайшее из диапазонов pyproject». Образ собрался бы с версиями,
которых никто не видел ни в одном PR. Теперь пропажа лока роняет COPY.
3. tradein-mvp/browser/Dockerfile — `pip install "camoufox[geoip]" aiohttp`
без единого пина. У сайдкара нет лока вообще, так что любая пересборка (в том
числе на несвязанном коммите) тянула свежайший camoufox, а с ним другой
playwright — под который НЕ написан sed-патч coreBundle.js в том же файле.
Запинено по факту прод-контейнера tradein-browser: camoufox 0.5.5,
playwright 1.60.0, aiohttp 3.14.3. playwright явно, хотя и транзитивный
(camoufox 0.5.5 → playwright<1.61): патч завязан на конкретную сборку драйвера.
Там же переписан комментарий «Апгрейд playwright невозможен — camoufox 0.4.11
pinned»: неверны обе половины. camoufox не был запинен ни на что, а 0.4.11
в проде не стоит с неизвестно каких пор — контейнер сейчас несёт 0.5.5 и
playwright 1.60.0.
Версии сняты с живого прод-контейнера, наличие на PyPI проверено.
Симптом: POST /admin/scrape/kn возвращает task_id, но run_id не появляется
в kn_scrape_runs. Причина: зомби-worker оставил Redis-lock с TTL=2h, новые
задачи скипаются с reason='lock_held' молча.
Backend:
- _LOCK_TTL_SECONDS 7200 → 1800 (full sweep ~25-30 мин — потолок).
- force_release_lock() helper + новый POST /api/v1/admin/scrape/release-lock.
- TriggerKnRequest +force: bool — при true перед apply_async удалить lock.
- _log_task_received() / _log_task_skipped() — kn_scrape_log пишет «task
подхвачен/skip» с run_id=NULL до основного flow. Теперь UI показывает
whether worker реально получил задачу.
Frontend:
- Чекбокс ⚡ Force в форме запуска.
- Кнопка 🔓 Снять Redis-lock (отдельный endpoint).
- Уведомление о результате release.
- backend/alembic/* — alembic infra (env.py, script.py.mako). versions/ empty
for now; first migration goes in Stage 2a when models are finalized.
- backend/Dockerfile: bake alembic.ini + alembic/ into the image so
`docker compose exec backend alembic upgrade head` works in prod.
- backend/db/init/99_drop_unused_extensions.sql + bind-mount in both compose
files: drops postgis-image's TIGER/topology/fuzzystrmatch on fresh volumes.
- .pre-commit-config.yaml + pre-commit in dev deps: ruff/prettier on commit
to stop CI failures like UP035 from leaking out.
- ops/backup.sh, ops/restore.sh: pg_dump cron script with optional Selectel S3
upload. 7-day local retention. Restore guard: requires RESTORE_CONFIRM=yes.
- Makefile: new targets `make migration NAME=...`, `make pre-commit-install`.
- backend/.env.example: SENTRY_DSN comment with sentry.io reference.
- backend/Dockerfile: split builder/runner, runtime libs only, non-root app user, curl for healthcheck, --frozen via new uv.lock
- frontend/Dockerfile: npm ci instead of npm install (deterministic), USER node
- docker-compose.prod.yml: working backend healthcheck (curl now in image), redis healthcheck, drop dead NEXT_PUBLIC_API_BASE_URL env (Next.js bakes NEXT_PUBLIC_* at build time, runtime override is no-op)
- docker-compose.yml: same redis healthcheck, BACKEND_URL for next.config rewrites, drop uv from CMD (not in new image)
- Caddyfile: handle (not handle_path) so /api prefix is preserved into FastAPI router
- next.config.ts: rewrite /api/* and /health to BACKEND_URL in dev (no Caddy locally)
- frontend/src/lib/api.ts: empty default = same-origin relative URLs
- Makefile: drop uv run from migrate target
- Add backend/uv.lock and frontend/package-lock.json for reproducible builds
Verified: docker build succeeds for both, backend container starts, /health responds, curl healthcheck works inside image, container runs as non-root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>