All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 36s
Deploy Trade-In / build-frontend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m14s
Deploy Trade-In / build-backend (push) Successful in 4m19s
Deploy Trade-In / deploy (push) Successful in 1m44s
111 lines
5.5 KiB
Docker
111 lines
5.5 KiB
Docker
# 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
|
||
|
||
# Version-файл фолбэка (app/core/version.py ищет VERSION, идя вверх от своего
|
||
# каталога — здесь она на 2 уровня выше /app/app/core/, т.е. ровно /app/VERSION).
|
||
# Build context = tradein-mvp/, поэтому VERSION резолвится с корня контекста.
|
||
COPY --chown=app:app VERSION VERSION
|
||
|
||
# Версия продукта + короткий git SHA + дата сборки — запечены как build-args
|
||
# в образ (см. .forgejo/workflows/deploy-tradein.yml, job build-backend).
|
||
# Пустые дефолты ЗДЕСЬ не читаются напрямую: app/core/version.py фолбэчит сам
|
||
# (VERSION-файл выше / "dev" / момент импорта модуля).
|
||
#
|
||
# НАМЕРЕННО в самом низу runner-стадии, ПОСЛЕ apt-get install и тяжёлых
|
||
# COPY --from=builder (.venv/packages/app выше) — BUILD_DATE меняется на
|
||
# КАЖДОМ деплое (текущее время сборки), а Docker-кэш инвалидирует ВСЕ слои
|
||
# ПОСЛЕ первого изменившегося ENV/ARG. Если бы этот блок стоял в начале
|
||
# стадии (как раньше), апдейт даты бил бы registry buildcache для apt-get +
|
||
# COPY .venv/packages/app КАЖДЫЙ раз — здесь инвалидирует только этот
|
||
# дешёвый хвост (ENV + USER + EXPOSE + CMD ниже).
|
||
ARG APP_VERSION=""
|
||
ARG BUILD_SHA=""
|
||
ARG BUILD_DATE=""
|
||
ENV APP_VERSION=$APP_VERSION \
|
||
BUILD_SHA=$BUILD_SHA \
|
||
BUILD_DATE=$BUILD_DATE
|
||
|
||
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"]
|