|
1 | | -# Use Dockerfile syntax version 1.5 for compatibility and new features |
2 | | -# syntax=docker/dockerfile:1.5 |
| 1 | +# syntax=docker/dockerfile:1 |
3 | 2 |
|
4 | | -FROM python:3.13 AS builder |
| 3 | +# ============================================================================== |
| 4 | +# Base Stage: Installs uv and creates a non-root user for security |
| 5 | +# ============================================================================== |
| 6 | +FROM python:3.13-slim AS base |
5 | 7 |
|
6 | | -# Set non-interactive mode |
7 | | -ENV DEBIAN_FRONTEND=noninteractive |
| 8 | +ENV UV_COMPILE_BYTECODE=1 |
| 9 | +ENV UV_LINK_MODE=copy |
8 | 10 |
|
9 | | -# Prevent docker from cleaning up the apt cache |
10 | | -RUN rm -f /etc/apt/apt.conf.d/docker-clean |
| 11 | +# Install uv, the modern Python package manager |
| 12 | +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv |
11 | 13 |
|
12 | | -# Define ARG for platform-specific cache separation |
| 14 | +# Create a non-root user and group for enhanced security |
| 15 | +RUN groupadd --system --gid 1001 app && \ |
| 16 | + useradd --system --uid 1001 --gid 1001 -m app |
| 17 | + |
| 18 | +# ============================================================================== |
| 19 | +# Builder Stage: Install system and Python dependencies with optimized caching |
| 20 | +# ============================================================================== |
| 21 | +FROM base AS builder |
| 22 | + |
| 23 | +# Argument for multi-platform builds |
13 | 24 | ARG TARGETPLATFORM |
14 | 25 |
|
15 | | -# Update and install dependencies with cache separated by architecture |
| 26 | +# CORRECTION: Argument to receive the application version from the host |
| 27 | +ARG APP_VERSION=0.0.0 |
| 28 | + |
| 29 | +# Install build-time system dependencies using BuildKit cache mounts |
16 | 30 | RUN --mount=type=cache,target=/var/cache/apt,id=apt-cache-${TARGETPLATFORM} \ |
17 | | - --mount=type=cache,target=/var/lib/apt,id=apt-lib-${TARGETPLATFORM} \ |
18 | 31 | apt-get update && \ |
19 | | - apt-get install --no-install-recommends -y libxml2-dev libxslt-dev |
| 32 | + apt-get install -y --no-install-recommends \ |
| 33 | + libxml2-dev \ |
| 34 | + libxslt-dev |
20 | 35 |
|
21 | 36 | WORKDIR /app |
22 | 37 |
|
23 | | -COPY requirements.txt . |
| 38 | +# Grant ownership to the non-root user before using it |
| 39 | +RUN --mount=type=cache,target=/home/app/.cache/uv,uid=1001,gid=1001 \ |
| 40 | + chown -R app:app /app /home/app/.cache/uv |
24 | 41 |
|
25 | | -# Use pip cache to speed up builds |
26 | | -RUN --mount=type=cache,target=/root/.cache/pip \ |
27 | | - pip install -r requirements.txt -t packages |
| 42 | +USER app |
28 | 43 |
|
29 | | -# Start from a slim Python 3.12 image for a small final image size |
30 | | -FROM python:3.13-slim AS final |
| 44 | +# Copy source code BEFORE installing dependencies, as setuptools-scm needs it |
| 45 | +COPY --chown=app:app . . |
31 | 46 |
|
32 | | -# Set non-interactive mode |
33 | | -ENV DEBIAN_FRONTEND=noninteractive |
| 47 | +# CORRECTION: Pass the version to setuptools-scm via an environment variable |
| 48 | +# This tells setuptools-scm to use this version string instead of looking for .git |
| 49 | +RUN --mount=type=cache,target=/home/app/.cache/uv,uid=1001,gid=1001 \ |
| 50 | + SETUPTOOLS_SCM_PRETEND_VERSION=${APP_VERSION} \ |
| 51 | + uv sync |
34 | 52 |
|
35 | | -# Prevent docker from cleaning up the apt cache in the final image |
36 | | -RUN rm -f /etc/apt/apt.conf.d/docker-clean |
| 53 | +# ============================================================================== |
| 54 | +# Final Stage: Assemble the lean production image |
| 55 | +# ============================================================================== |
| 56 | +FROM base AS final |
37 | 57 |
|
38 | | -ARG TARGETPLATFORM |
| 58 | +WORKDIR /app |
39 | 59 |
|
40 | | -# Copy built packages from the previous stage |
41 | | -COPY --from=builder /app/packages /app/packages |
| 60 | +# Activate the virtual environment by adding it to the PATH |
| 61 | +ENV PATH="/app/.venv/bin:$PATH" |
42 | 62 |
|
43 | | -# Update and install runtime dependencies if necessary, with cache separated by architecture |
| 63 | +# Install only essential runtime system dependencies |
| 64 | +ARG TARGETPLATFORM |
44 | 65 | RUN --mount=type=cache,target=/var/cache/apt,id=apt-cache-${TARGETPLATFORM} \ |
45 | | - --mount=type=cache,target=/var/lib/apt,id=apt-lib-${TARGETPLATFORM} \ |
46 | 66 | apt-get update && \ |
47 | | - apt-get full-upgrade -y && \ |
48 | | - apt-get install -y --no-install-recommends libxml2 libxslt1.1 libtk8.6 |
| 67 | + apt-get install -y --no-install-recommends \ |
| 68 | + libxml2 \ |
| 69 | + libxslt1.1 \ |
| 70 | + libtk8.6 |
49 | 71 |
|
50 | | -WORKDIR /app |
| 72 | +# Copy the virtual environment and source code from previous stages |
| 73 | +# The source code is already in the builder stage, no need for another COPY . . |
| 74 | +COPY --from=builder --chown=app:app /app /app |
51 | 75 |
|
52 | | -ENV PYTHONPATH=/app/packages:$PYTHONPATH |
| 76 | +# This must be done as root BEFORE switching to the non-root user |
| 77 | +RUN mkdir -p /home/app/.cache/crawler-to-md && chown -R app:app /home/app/.cache/crawler-to-md |
53 | 78 |
|
54 | | -# Copy the rest of the application's source code into the working directory |
55 | | -COPY . . |
| 79 | +# Switch to the non-root user for execution |
| 80 | +USER app |
56 | 81 |
|
57 | | -VOLUME [ "/app/cache"] |
| 82 | +VOLUME [ "/home/app/.cache/crawler-to-md" ] |
58 | 83 |
|
59 | | -ENTRYPOINT [ "python", "main.py" ] |
| 84 | +ENTRYPOINT [ "/app/.venv/bin/crawler-to-md" ] |
0 commit comments