# Trade-In MVP backend — lean image (FastAPI + WeasyPrint). # Не содержит Playwright/Chromium/Celery/Redis — только то что нужно для trade-in feature. # # Build context = tradein-mvp/ (uv workspace root), НЕ ./backend (#2137): образу нужны # workspace-манифест (pyproject.toml + uv.lock) и packages/scraper-kit для editable install. # Собирается через `dockerfile: backend/Dockerfile` (см. docker-compose*.yml + # .forgejo/workflows/deploy-tradein.yml build-backend). # ---- builder ---- FROM python:3.12-slim AS builder ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ UV_COMPILE_BYTECODE=1 \ UV_LINK_MODE=copy RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update && apt-get install -y --no-install-recommends \ build-essential \ libpq-dev RUN pip install --no-cache-dir uv # WORKDIR = uv workspace root. backend/ и packages/* — члены workspace. WORKDIR /app # Манифесты workspace + members сначала (кэшируемый слой зависимостей). # packages/ копируем ДО uv sync целиком: editable-install scraper-kit требует # src/ на этапе резолва (hatchling билдит editable-wheel по pyproject + src). COPY pyproject.toml uv.lock ./ COPY backend/pyproject.toml backend/pyproject.toml COPY packages/ packages/ # uv sync --frozen ставит зависимости backend ВКЛЮЧАЯ scraper-kit (editable member, # .pth → /app/packages/scraper-kit/src). --no-dev: без pytest/ruff. # NB: команда содержит import-assert'ы — они И bust'ят build-cache этого слоя # (защита от stale-кэша без нужной deps), И валидируют что зависимость реально # установлена (сборка падает громко, а не в рантайме). RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --frozen --no-dev \ && /app/.venv/bin/python -c "import lxml; print('lxml', lxml.__version__)" \ && /app/.venv/bin/python -c "import scraper_kit; print('scraper_kit', scraper_kit.__file__)" COPY backend/app backend/app COPY backend/scripts backend/scripts # ---- runner ---- FROM python:3.12-slim AS runner ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PATH="/app/.venv/bin:$PATH" RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update && apt-get install -y --no-install-recommends \ libpq5 \ libcairo2 \ libpango-1.0-0 \ libpangoft2-1.0-0 \ fonts-dejavu-core \ curl \ && useradd --create-home --uid 1000 app WORKDIR /app # Runtime-layout сохраняем прежним (app в /app/app, scripts в /app/scripts), чтобы # bind-mount'ы compose не менялись: ./backend/app:/app/app (dev hot-reload) и # ../auth/roles.yaml:/app/auth/roles.yaml (prod, app/core/auth.py читает /app/auth/roles.yaml). # backend = virtual-пакет (НЕ в site-packages) → import app идёт из CWD=/app. # packages/ ОБЯЗАТЕЛЕН в рантайме: editable .pth ссылается на /app/packages/scraper-kit/src. COPY --from=builder --chown=app:app /app/.venv /app/.venv COPY --from=builder --chown=app:app /app/packages /app/packages COPY --from=builder --chown=app:app /app/backend/app /app/app COPY --from=builder --chown=app:app /app/backend/scripts /app/scripts USER app # HOME должен быть явным: Docker НЕ выставляет $HOME по USER, а некоторые # платформенные библиотеки резолвят $HOME/.cache. Безвредно оставить. ENV HOME=/home/app EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]