From bb39bc1ccd59136e5cf1eea24cc5f2f6022f1584 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:14:18 +0000 Subject: [PATCH 1/2] Initial plan From 718d82c815c693436708944b4396939f329a1ca7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:28:31 +0000 Subject: [PATCH 2/2] chore(docker): optimize image size with venv, slim builder, and dockerignore Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- .dockerignore | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 56 +++++++++++++++++++---------- Dockerfile.local | 48 ++++++++++++++++++------- 3 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..36bd98c6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,91 @@ +# ============================================================================= +# Docker build context exclusions +# Reducing the build context speeds up builds and prevents unnecessary cache +# invalidation when unrelated files change. +# ============================================================================= + +# ── Version control ────────────────────────────────────────────────────────── +.git + +# ── GitHub / CI tooling ────────────────────────────────────────────────────── +.github + +# ── IDE / local dev ────────────────────────────────────────────────────────── +.vscode +.jules + +# ── Pre-commit / linting config (not needed at runtime) ────────────────────── +.pre-commit-config.yaml +pyproject.toml +codecov.yml +crowdin.yml + +# ── Test suite ─────────────────────────────────────────────────────────────── +tests/ +requirements-dev.txt +coverage.json +COVERAGE_REPORT.md +.coverage +htmlcov/ +.pytest_cache/ +junit.xml +coverage.xml + +# ── Mobile app / browser extension / legacy placeholder ───────────────────── +# backend/ is an empty placeholder directory not part of the Python application +mobile/ +browser-extension/ +backend/ + +# ── Helm charts ────────────────────────────────────────────────────────────── +helm/ + +# ── Scripts (run before Docker build, output files are COPYd separately) ───── +scripts/ + +# ── Benchmark and one-off utility scripts ──────────────────────────────────── +benchmark_*.py +fix_test*.py +run_fast_tests.sh + +# ── Root-level Markdown files (docs/ is kept for docs-builder stage) ───────── +# Note: *.md only matches files at the root level, not inside subdirectories +*.md + +# ── Python bytecode / compiled artifacts ───────────────────────────────────── +__pycache__/ +*.pyc +*.pyo +*.pyd +*.so +*.egg +*.egg-info/ + +# ── Virtual environments ────────────────────────────────────────────────────── +.venv/ +venv/ +env/ + +# ── Environment / secret files ─────────────────────────────────────────────── +.env +.env.local +.env.*.local + +# ── Runtime state files ─────────────────────────────────────────────────────── +*.log +celerybeat-schedule +celerybeat.pid + +# ── Build artifacts ─────────────────────────────────────────────────────────── +build/ +dist/ +.cache/ +.mypy_cache/ +.ruff_cache/ +site/ +docs_build/ + +# ── Editor temp files ───────────────────────────────────────────────────────── +*.swp +*.swo +*~ diff --git a/Dockerfile b/Dockerfile index 2485fa56..f472d8c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,34 @@ -# Use multi-stage build for a smaller final image -FROM python:3.14.1 AS builder +# syntax=docker/dockerfile:1 -WORKDIR /app +# ── Stage 1: Python dependency builder ────────────────────────────────────── +# Use the same slim variant as the runtime to keep Python versions in sync. +# build-essential + libffi-dev cover the few packages (e.g. cryptography) that +# need a C compiler; they are discarded after this stage. +FROM python:3.14.3-slim AS builder -# Copy requirements first for better layer caching -COPY requirements.txt /app/ -RUN pip install --no-cache-dir -r requirements.txt +WORKDIR /build -# ── Documentation build stage ─────────────────────────────────────────────── -FROM python:3.14.1-slim AS docs-builder +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + libffi-dev \ + && rm -rf /var/lib/apt/lists/* + +# Create an isolated virtual environment so only installed packages are copied +# to the runtime image (no pip, setuptools, or other builder artefacts). +RUN python -m venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 + +COPY requirements.txt /build/ +RUN pip install --no-cache-dir -r requirements.txt \ + # Remove bytecode and cache to keep the venv lean + && find /opt/venv -type f -name "*.pyc" -delete \ + && find /opt/venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + +# ── Stage 2: Documentation builder ────────────────────────────────────────── +FROM python:3.14.3-slim AS docs-builder WORKDIR /docs @@ -23,14 +43,13 @@ COPY mkdocs.yml /docs/mkdocs.yml # Build the static documentation site RUN mkdocs build --config-file /docs/mkdocs.yml --site-dir /docs/docs_build -# Second stage for the actual runtime +# ── Stage 3: Runtime image ─────────────────────────────────────────────────── FROM python:3.14.3-slim WORKDIR /app -# Copy installed packages from builder stage -COPY --from=builder /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages -COPY --from=builder /usr/local/bin /usr/local/bin +# Copy only the pre-built virtual environment from the builder +COPY --from=builder /opt/venv /opt/venv # Install system-level OCR tools required for local OCR workflows: # tesseract-ocr – OCR engine used by pytesseract and ocrmypdf @@ -62,15 +81,14 @@ COPY ./RUNTIME_INFO /app/RUNTIME_INFO # Copy the pre-built MkDocs documentation site (served at /help) COPY --from=docs-builder /docs/docs_build /app/docs_build -# Create runtime_info directory -RUN mkdir -p /app/runtime_info - -# Create necessary directories -RUN mkdir -p /workdir +# Create necessary runtime directories in a single layer +RUN mkdir -p /app/runtime_info /workdir # Set environment variables -ENV PYTHONPATH=/app -ENV PYTHONUNBUFFERED=1 +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONPATH=/app \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 # Expose the port the app runs on EXPOSE 8000 diff --git a/Dockerfile.local b/Dockerfile.local index 138d726c..5b2c5487 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -1,13 +1,31 @@ +# syntax=docker/dockerfile:1 + # Local development Dockerfile (avoids CI-only build metadata files) -FROM python:3.14.1 AS builder -WORKDIR /app +# ── Stage 1: Python dependency builder ────────────────────────────────────── +FROM python:3.14.3-slim AS builder -COPY requirements.txt /app/ -RUN pip install --no-cache-dir -r requirements.txt +WORKDIR /build -# ── Documentation build stage ─────────────────────────────────────────────── -FROM python:3.14.1-slim AS docs-builder +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + libffi-dev \ + && rm -rf /var/lib/apt/lists/* + +# Create an isolated virtual environment +RUN python -m venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 + +COPY requirements.txt /build/ +RUN pip install --no-cache-dir -r requirements.txt \ + && find /opt/venv -type f -name "*.pyc" -delete \ + && find /opt/venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + +# ── Stage 2: Documentation builder ────────────────────────────────────────── +FROM python:3.14.3-slim AS docs-builder WORKDIR /docs @@ -19,23 +37,25 @@ COPY mkdocs.yml /docs/mkdocs.yml RUN mkdocs build --config-file /docs/mkdocs.yml --site-dir /docs/docs_build -FROM python:3.14.1-slim +# ── Stage 3: Runtime image ─────────────────────────────────────────────────── +FROM python:3.14.3-slim WORKDIR /app -COPY --from=builder /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages -COPY --from=builder /usr/local/bin /usr/local/bin +COPY --from=builder /opt/venv /opt/venv # Install system-level OCR tools required for local OCR workflows: # tesseract-ocr – OCR engine used by pytesseract and ocrmypdf # ghostscript – required by ocrmypdf for PDF/PS operations # poppler-utils – provides pdfinfo/pdftoppm used by pdf2image # unpaper – optional deskewing pre-processor used by ocrmypdf +# wget – used by ocr_language_manager to download tessdata files RUN apt-get update && apt-get install -y --no-install-recommends \ tesseract-ocr \ ghostscript \ poppler-utils \ unpaper \ + wget \ && apt-get clean && rm -rf /var/lib/apt/lists/* COPY ./app /app/app @@ -53,11 +73,13 @@ COPY --from=docs-builder /docs/docs_build /app/docs_build RUN echo "local" > /app/GIT_SHA \ && echo "local" > /app/RUNTIME_INFO -RUN mkdir -p /app/runtime_info -RUN mkdir -p /workdir +# Create necessary runtime directories in a single layer +RUN mkdir -p /app/runtime_info /workdir -ENV PYTHONPATH=/app -ENV PYTHONUNBUFFERED=1 +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONPATH=/app \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 EXPOSE 8000