samgis / dockerfiles /dockerfile-samgis-base
alessandro trinca tornidor
feat: migrate from Poetry to uv as package manager
d53e2be
# Include global ARGs at the dockerfile top
ARG WORKDIR_ROOT="/var/task"
ARG FASTAPI_STATIC="${WORKDIR_ROOT}/static"
FROM dhi.io/python:3.13-dev AS builder_global
ARG WORKDIR_ROOT
ARG MODEL_VARIANT="sam2.1_hiera_base_plus_uint8"
ENV MODEL_VARIANT=${MODEL_VARIANT}
# Set working directory to function root directory
RUN mkdir -p ${WORKDIR_ROOT}
WORKDIR ${WORKDIR_ROOT}
COPY requirements.txt ${WORKDIR_ROOT}/
# Install project dependencies into a venv
RUN python -m venv ${WORKDIR_ROOT}/.venv \
&& ${WORKDIR_ROOT}/.venv/bin/pip install --require-hashes -r ${WORKDIR_ROOT}/requirements.txt
# Download model weights during build (builder has network access)
COPY scripts/download_models.py ${WORKDIR_ROOT}/scripts/download_models.py
RUN ${WORKDIR_ROOT}/.venv/bin/python3 ${WORKDIR_ROOT}/scripts/download_models.py
FROM dhi.io/python:3.13-dev AS dir_creator
ARG WORKDIR_ROOT
# Pre-create workdir and copy libexpat (needed by rasterio's bundled GDAL at runtime)
RUN mkdir -p ${WORKDIR_ROOT} && chown 65532:65532 ${WORKDIR_ROOT}
RUN apt update && apt install -y --no-install-recommends libexpat1 && apt clean && rm -rf /var/lib/apt/lists/*
FROM dhi.io/python:3.13 AS runtime
ARG WORKDIR_ROOT
ENV VIRTUAL_ENV=${WORKDIR_ROOT}/.venv \
PATH="${WORKDIR_ROOT}/.venv/bin:/opt/python/bin:$PATH"
# Copy pre-created workdir with correct ownership
COPY --from=dir_creator --chown=65532:65532 ${WORKDIR_ROOT} ${WORKDIR_ROOT}
# Copy libexpat (required by rasterio's bundled GDAL)
COPY --from=dir_creator /usr/lib/*-linux-gnu/libexpat.so* /usr/lib/
WORKDIR ${WORKDIR_ROOT}
# Copy venv from builder and app files
COPY --from=builder_global --chown=65532:65532 ${WORKDIR_ROOT}/.venv ${WORKDIR_ROOT}/.venv
COPY --chown=65532:65532 ./app.py ./scripts/client_health.py ${WORKDIR_ROOT}/
# Smoke test: verify critical imports work without libGL, libexpat, or system GDAL
RUN ["python3", "-c", "import rasterio; import onnxruntime; import fastapi; import uvicorn"]
### Frontend build (unchanged)
FROM node:24-alpine AS node_fastapi
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY ./static /appnode
WORKDIR /appnode
FROM node_fastapi AS node_prod_deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store; pnpm install --prod --frozen-lockfile
RUN if [ ! -d /appnode/node_modules ]; then echo "no node_modules folder" && exit 1; fi
FROM node_fastapi AS node_build
ARG VITE__MAP_DESCRIPTION
ARG MODEL_VARIANT="sam2.1_hiera_base_plus_uint8"
ENV VITE__INDEX_URL="/" \
VITE__MODEL_VARIANT=${MODEL_VARIANT}
RUN --mount=type=cache,id=pnpm,target=/pnpm/store; pnpm install --frozen-lockfile
RUN --mount=type=cache,id=pnpm,target=/pnpm/store; pnpm build
RUN --mount=type=cache,id=pnpm,target=/pnpm/store; \
pnpm dlx @tailwindcss/cli -i /appnode/src/input.css -o /appnode/dist/output.css
RUN if [ ! -d /appnode/dist ]; then echo "no dist folder" && exit 1; fi
FROM runtime
ARG FASTAPI_STATIC
ARG WORKDIR_ROOT
ARG MODEL_VARIANT="sam2.1_hiera_base_plus_uint8"
ENV MODEL_VARIANT=${MODEL_VARIANT} \
MODEL_FOLDER="${WORKDIR_ROOT}/.samgis/models/${MODEL_VARIANT}"
# Copy downloaded model weights from builder (builder runs as root → /root/.samgis/)
COPY --from=builder_global --chown=65532:65532 /root/.samgis/models/${MODEL_VARIANT} ${WORKDIR_ROOT}/.samgis/models/${MODEL_VARIANT}
COPY --chown=65532:65532 --from=node_prod_deps /appnode/node_modules* ${FASTAPI_STATIC}/node_modules
COPY --chown=65532:65532 --from=node_build /appnode/dist* ${FASTAPI_STATIC}/dist
COPY --chown=65532:65532 static/list_files.html ${FASTAPI_STATIC}/