diff --git a/backend/Dockerfile b/backend/Dockerfile index 0da0ae0c..299c4444 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -28,10 +28,16 @@ RUN pip install --no-cache-dir uv WORKDIR /app -COPY pyproject.toml uv.lock* ./ +# Без глоба и без фолбэка: uv.lock ОБЯЗАТЕЛЕН. `uv.lock*` + `if [ -f uv.lock ]` +# означали, что пропавший лок не ломает сборку, а тихо переключает её на резолв +# «свежайшее из диапазонов pyproject» — образ собирался бы с версиями, которых +# никто не видел ни в одном PR, и воспроизвести прод-сборку было бы нечем. +# Теперь пропажа лока = падение COPY, то есть красный билд вместо незаметной +# подмены зависимостей. +COPY pyproject.toml uv.lock ./ # uv-кеш переживает между билдами: при неизменном lock'е sync ~5 сек вместо 1-2 мин. RUN --mount=type=cache,target=/root/.cache/uv \ - if [ -f uv.lock ]; then uv sync --frozen --no-dev; else uv sync --no-dev; fi + uv sync --frozen --no-dev COPY app ./app COPY alembic.ini ./ diff --git a/tradein-mvp/.gitignore b/tradein-mvp/.gitignore index a746885d..c1c4ab25 100644 --- a/tradein-mvp/.gitignore +++ b/tradein-mvp/.gitignore @@ -8,6 +8,15 @@ backend/*.egg-info/ backend/.pytest_cache/ backend/.ruff_cache/ backend/.mypy_cache/ +# backend/uv.lock — ИГНОРИРУЕТСЯ НАМЕРЕННО, это не забытый файл. +# Мера — 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, которых в реальной сборке Меры нет вообще. +# Локи обновлять из корня: `cd tradein-mvp && uv lock`. backend/uv.lock # Frontend Node diff --git a/tradein-mvp/browser/Dockerfile b/tradein-mvp/browser/Dockerfile index d98b761c..5f789592 100644 --- a/tradein-mvp/browser/Dockerfile +++ b/tradein-mvp/browser/Dockerfile @@ -20,8 +20,18 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ WORKDIR /app -# camoufox[geoip] тянет playwright как зависимость -RUN pip install --no-cache-dir "camoufox[geoip]" aiohttp +# Пины, а не плавающие версии: единственный лок этого сайдкара — вот эти строки. +# Без них любая пересборка образа (в том числе автоматическая, на не связанном +# коммите) втягивала свежайший camoufox — а вместе с ним другой playwright, под +# который написан sed-патч coreBundle.js ниже. Версии сняты с работающего +# прод-контейнера tradein-browser (2026-08-29): camoufox 0.5.5, playwright +# 1.60.0, aiohttp 3.14.3. playwright запинен явно, хотя и приходит транзитивно +# (camoufox 0.5.5 требует playwright<1.61) — патч завязан на конкретную сборку +# драйвера, диапазон тут не годится. +RUN pip install --no-cache-dir \ + "camoufox[geoip]==0.5.5" \ + "playwright==1.60.0" \ + "aiohttp==3.14.3" # ── #915/#918: null-guard + ВАЛИДНЫЕ дефолты для pageError.location (playwright 1.60.0) ─ # Firefox/juggler-бэкенд иногда отдаёт uncaught JS-ошибку страницы (реклама / @@ -42,8 +52,11 @@ RUN pip install --no-cache-dir "camoufox[geoip]" aiohttp # страницы вместо краша → pipeline видит firewall и штатно ротирует IP, а не # уходит в crash-storm (подтверждено прод-дебагом локального стека 2026-05-31). # -# Апгрейд playwright невозможен — camoufox 0.4.11 pinned под Firefox+juggler. -# Сборка падает (exit 1), если паттерн не найден — сигнал что bump playwright сломал патч. +# Апгрейд playwright не заблокирован, но связан: camoufox тянет playwright сам +# (0.5.5 → playwright<1.61), поэтому поднимать его можно только вместе с +# camoufox и только проверив, что паттерны sed ниже ещё есть в coreBundle.js. +# Сборка падает (exit 1), если паттерн не найден — это и есть сигнал, что bump +# сломал патч. RUN PW_BUNDLE="$(python -c 'import os,playwright;print(os.path.join(os.path.dirname(playwright.__file__),"driver","package","lib","coreBundle.js"))')" \ && sed -i -E "s/pageError\.location\.url,/pageError.location?.url ?? '',/g" "$PW_BUNDLE" \ && sed -i -E 's/pageError\.location\.lineNumber,/pageError.location?.lineNumber ?? 0,/g' "$PW_BUNDLE" \