diff --git a/Dockerfile b/Dockerfile index 6b2f7d8..6f3d32b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,61 +1,99 @@ # Dockerfile for uWSGI wrapped Giftless Git LFS Server +# Shared build ARGs among stages +ARG WORKDIR=/app +ARG VENV="$WORKDIR/.venv" +ARG UV_VERSION=0.5.16 -### --- Build Depdendencies --- - -FROM python:3.12 as builder -MAINTAINER "Shahar Evron " - -# Build wheels for uWSGI and all requirements -RUN DEBIAN_FRONTEND=noninteractive apt-get update \ - && apt-get install -y build-essential libpcre3 libpcre3-dev git -RUN pip install -U pip -RUN mkdir /wheels +### Distroless uv version layer to be copied from (because COPY --from does not interpolate variables) +FROM ghcr.io/astral-sh/uv:$UV_VERSION AS uv +### --- Build Depdendencies --- +FROM python:3.12 AS builder ARG UWSGI_VERSION=2.0.23 -RUN pip wheel -w /wheels uwsgi==$UWSGI_VERSION - -COPY requirements/main.txt /requirements.txt -RUN pip wheel -w /wheels -r /requirements.txt +# Common WSGI middleware modules to be pip-installed +# These are not required in every Giftless installation but are common enough +ARG EXTRA_PACKAGES="wsgi_cors_middleware" +# expose shared ARGs +ARG WORKDIR +ARG VENV + +# Set WORKDIR (also creates the dir) +WORKDIR $WORKDIR + +# Install packages to build wheels for uWSGI and other requirements +RUN set -eux ;\ + export DEBIAN_FRONTEND=noninteractive ;\ + apt-get update ;\ + apt-get install -y --no-install-recommends build-essential libpcre3 libpcre3-dev git ;\ + rm -rf /var/lib/apt/lists/* + +# Install uv to replace pip & friends +COPY --from=uv /uv /uvx /bin/ + +# Set a couple uv-related settings +# Wait a bit longer for slow connections +ENV UV_HTTP_TIMEOUT=100 +# Don't cache packages +ENV UV_NO_CACHE=1 + +# Create virtual env to store dependencies, "activate" it +RUN uv venv "$VENV" +ENV VIRTUAL_ENV="$VENV" PATH="$VENV/bin:$PATH" + +# Install runtime dependencies +RUN --mount=target=/build-ctx \ + uv pip install -r /build-ctx/requirements/main.txt +RUN uv pip install uwsgi==$UWSGI_VERSION +# Install extra packages into the virtual env +RUN uv pip install ${EXTRA_PACKAGES} + +# Copy project contents necessary for an editable install +COPY .git .git/ +COPY giftless giftless/ +COPY pyproject.toml . +# Editable-install the giftless package (add a kind of a project path reference in site-packages) +# To detect the package version dynamically, setuptools-scm needs the git binary +RUN uv pip install -e . ### --- Build Final Image --- - -FROM python:3.12-slim - -RUN DEBIAN_FRONTEND=noninteractive apt-get update \ - && apt-get install -y libpcre3 libxml2 tini git \ - && apt-get clean \ - && apt -y autoremove - -RUN mkdir /app - -# Install dependencies -COPY --from=builder /wheels /wheels -RUN pip install /wheels/*.whl - -# Copy project code -COPY . /app -RUN pip install -e /app +FROM python:3.12-slim AS final +LABEL org.opencontainers.image.authors="Shahar Evron " ARG USER_NAME=giftless +# Writable path for local LFS storage ARG STORAGE_DIR=/lfs-storage -ENV GIFTLESS_TRANSFER_ADAPTERS_basic_options_storage_options_path $STORAGE_DIR - -RUN useradd -d /app $USER_NAME -RUN mkdir $STORAGE_DIR -RUN chown $USER_NAME $STORAGE_DIR - -# Pip-install some common WSGI middleware modules -# These are not required in every Giftless installation but are common enough -ARG EXTRA_PACKAGES="wsgi_cors_middleware" -RUN pip install ${EXTRA_PACKAGES} - +# expose shared ARGs +ARG WORKDIR +ARG VENV + +# Set WORKDIR (also creates the dir) +WORKDIR $WORKDIR + +# Create a user and set local storage write permissions +RUN set -eux ;\ + useradd -d "$WORKDIR" "$USER_NAME" ;\ + mkdir "$STORAGE_DIR" ;\ + chown "$USER_NAME" "$STORAGE_DIR" + +# Install runtime dependencies +RUN set -eux ;\ + export DEBIAN_FRONTEND=noninteractive ;\ + apt-get update ;\ + apt-get install -y libpcre3 libxml2 tini ;\ + rm -rf /var/lib/apt/lists/* + +# Use the virtual env with dependencies from builder stage +COPY --from=builder "$VENV" "$VENV" +ENV VIRTUAL_ENV="$VENV" PATH="$VENV/bin:$PATH" +# Copy project source back into the same path referenced by the editable install +COPY --from=builder "$WORKDIR/giftless" "giftless" + +# Set runtime properties USER $USER_NAME +ENV GIFTLESS_TRANSFER_ADAPTERS_basic_options_storage_options_path="$STORAGE_DIR" +ENV UWSGI_MODULE="giftless.wsgi_entrypoint" -WORKDIR /app - -ENV UWSGI_MODULE "giftless.wsgi_entrypoint" - -ENTRYPOINT ["tini", "uwsgi", "--"] +ENTRYPOINT ["tini", "--", "uwsgi"] CMD ["-s", "127.0.0.1:5000", "-M", "-T", "--threads", "2", "-p", "2", \ "--manage-script-name", "--callable", "app"] diff --git a/requirements/dev.txt b/requirements/dev.txt index 76454ae..7fb7f99 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -248,9 +248,7 @@ urllib3==2.0.7 # responses # types-requests uv==0.5.8 - # via - # -c requirements/main.txt - # -r requirements/dev.in + # via -r requirements/dev.in vcrpy==6.0.1 # via pytest-vcr virtualenv==20.25.1 diff --git a/requirements/main.in b/requirements/main.in index 611cda2..20dbb4e 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -22,7 +22,4 @@ boto3~=1.34 # GitHub AA Provider cachetools~=5.3 -# uv: fast pip replacement -uv - importlib-metadata; python_version < '3.13' diff --git a/requirements/main.txt b/requirements/main.txt index 7b5fdec..490e194 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -132,8 +132,6 @@ urllib3==2.0.7 # via # botocore # requests -uv==0.5.8 - # via -r requirements/main.in webargs==8.4.0 # via -r requirements/main.in werkzeug==3.0.3