Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,22 @@ src/frontend/.vite/
templates/
static/css/
static/js/

# Developer-only trees. `COPY . .` puts the whole context into /app, so anything
# left here ships to production: the CodeGraph index alone was 38MB of the image.
# NOTE: this file applies to EVERY stage, so `src/` cannot be listed — the
# frontend-builder stage needs it. The final stage drops it after the copy.
# Deliberately NOT listed: `data/` (languages.py:19 reads
# data/book-languages.json at runtime) and `genDebug.sh` (the final stage
# chmod +x's it, so excluding it fails the build).
.codegraph/
.ruff_cache/
.claude/
tests/
docs/
scripts/
compose/
downloaded_files/
frontend-dist/
SESSION_STATE.md
Makefile
27 changes: 22 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ ENV FLASK_PORT=8084
# Configure locale, timezone, and perform initial cleanup in a single layer
RUN apt-get update && \
apt-get install -y --no-install-recommends \
# For building C-extensions (cffi, gevent, etc.)
gcc \
g++ \
libffi-dev \
python3-dev \
# No C toolchain: every compiled dependency (cffi, gevent, greenlet,
# zope-interface) ships a cp314 manylinux wheel for amd64 and arm64, and
# the sdist-only ones are pure Python. The toolchain was 290MB of the
# runtime image, and python3-dev pulled Debian's libpython3.13 into a 3.14
# image. If a dependency ever needs compiling, build it in a separate stage.
# For locale
locales tzdata \
# For healthcheck
Expand Down Expand Up @@ -209,6 +209,13 @@ RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --python /app/.venv/bin/python --reinstall python-xlib==0.33 && \
/app/.venv/bin/python -c "import Xlib.X; assert hasattr(Xlib.X, 'FamilyServerInterpreted'), 'Xlib.X.FamilyServerInterpreted missing after fix'; print('Xlib namespace OK:', Xlib.__version__)"

# The venv's own pip goes too. uv seeded a copy into /app/.venv and nothing
# installs at runtime. setuptools deliberately STAYS: several deps still import
# pkg_resources on the hot path.
RUN rm -rf /app/.venv/bin/pip /app/.venv/bin/pip3 /app/.venv/bin/pip3.* \
/app/.venv/lib/python*/site-packages/pip \
/app/.venv/lib/python*/site-packages/pip-*.dist-info

# Keep SeleniumBase's bundled driver cache writable for the fixed non-root user.
RUN SELENIUMBASE_DRIVERS_DIR=$(/app/.venv/bin/python -c "import pathlib, seleniumbase; print(pathlib.Path(seleniumbase.__file__).resolve().parent / 'drivers')") && \
chown -R 1000:1000 "${SELENIUMBASE_DRIVERS_DIR}" && \
Expand All @@ -224,6 +231,11 @@ COPY . .

COPY --from=frontend-builder /frontend/dist /app/frontend-dist

# The frontend SOURCE cannot be excluded via .dockerignore — that file applies
# to every stage and frontend-builder needs src/. Only the built dist (copied
# above) is served at runtime, so drop the source here.
RUN rm -rf /app/src

# Image-owned runtime paths for the fixed non-root user. Root/PUID mode still
# re-homes ownership at startup when needed.
RUN mkdir -p \
Expand Down Expand Up @@ -262,6 +274,11 @@ COPY . .

COPY --from=frontend-builder /frontend/dist /app/frontend-dist

# The frontend SOURCE cannot be excluded via .dockerignore — that file applies
# to every stage and frontend-builder needs src/. Only the built dist (copied
# above) is served at runtime, so drop the source here.
RUN rm -rf /app/src

# Image-owned runtime paths for the fixed non-root user. Root/PUID mode still
# re-homes ownership at startup when needed.
RUN mkdir -p \
Expand Down
Loading