Ctrl K
ComunidadGuías…

Dockerfile

Rama dev · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# ---- web build ------------------------------------------------------------------
# --platform=$BUILDPLATFORM: the web build emits static files, which are identical whatever the
# target architecture — so it runs natively on the builder instead of once per platform under QEMU.
# Emulating a pnpm build for arm64 cost minutes and produced byte-identical output.
FROM --platform=$BUILDPLATFORM node:22-alpine AS web
WORKDIR /build
RUN corepack enable
# pnpm-workspace.yaml carries the approved build scripts (esbuild); without it pnpm 10
# refuses the install with ERR_PNPM_IGNORED_BUILDS.
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY web/ ./
RUN pnpm build

# ---- python runtime --------------------------------------------------------------
FROM python:3.12-slim AS runtime

# gosu for the PUID/PGID drop; tini as PID 1
RUN apt-get update \
    && apt-get install -y --no-install-recommends gosu tini curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# DEPENDENCIES FIRST, from the lockfile alone — this layer must not see application source, or
# every commit reinstalls FastAPI, SQLAlchemy and three LLM SDKs from scratch on BOTH
# architectures.
#
# requirements.lock pins every transitive dependency to an exact version. Installing from
# pyproject's floor pins (`fastapi>=0.115`) instead meant two builds of the SAME commit could ship
# different dependency versions — the image was not reproducible, and a dependency could break
# production without a single line of our code changing. Regenerate with the command in
# .claude/CLAUDE.md whenever pyproject's dependencies change.
#
# The lock bundles every LLM provider SDK — the container is the whole product, so the curator must
# work for whichever provider the owner picks in setup without them shelling in to pip install
# extras. (local/none need no SDK.) `posters` (Pillow) powers uploaded-poster normalization;
# OpenAI/Google also generate poster images, reusing the curator key.
COPY requirements.lock ./
RUN pip install --no-cache-dir -r requirements.lock

COPY pyproject.toml README.md LICENSE ./
COPY shortlist/ ./shortlist/
# --no-deps: everything it needs is already in the layer above, so a source-only change reinstalls
# just this package (seconds) instead of the whole dependency tree.
RUN pip install --no-cache-dir --no-deps .

COPY --from=web /build/dist ./web/dist
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENV SHORTLIST_CONFIG=/config \
    PORT=5959 \
    PUID=1000 \
    PGID=1000

# Build provenance. `docker/metadata-action` already stamps the same facts as OCI LABELs, but nothing
# INSIDE the container can read its own labels — so `version_check` read these env vars from the day
# it was written and always got nothing, reporting every Docker install as a source checkout. CI
# passes them as build-args; a plain `docker build` leaves them empty, which is the honest answer.
# Last, because the sha changes on every commit and everything above it should stay cached.
ARG GIT_SHA=""
ARG GIT_BRANCH=""
ENV GIT_SHA=$GIT_SHA \
    GIT_BRANCH=$GIT_BRANCH

VOLUME /config
EXPOSE 5959

HEALTHCHECK --interval=30s --timeout=5s --start-period=20s \
    CMD curl -fsS "http://localhost:${PORT}/api/system/health" || exit 1

ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"]