# 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@sha256:a87c4bc7bbf75da7d5bb1816f2bdf7640c17eeb42d3ba84c5201827977da8de4 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 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}/