ARG REGISTRY=dhi.io/
ARG AWS_CLI_VERSION=2.27.26
ARG DHI_PYTHON_VERSION=3.13
ARG DOCKER_VERSION=28.1.1
ARG KUBECTL_VERSION=1.34.1
ARG UV_VERSION=0.8.13

FROM amazon/aws-cli:${AWS_CLI_VERSION} AS aws-cli
FROM docker:${DOCKER_VERSION}-cli AS docker-cli
FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv
FROM rancher/kubectl:v${KUBECTL_VERSION} AS kubectl
FROM ${REGISTRY}python:${DHI_PYTHON_VERSION}-dev AS python

FROM alpine:3.23 AS helm
ARG HELM_VERSION=3.18.1
RUN apk add --no-cache curl \
 && [ $(uname -m) = aarch64 ] && ARCH=arm64 || ARCH=amd64 \
 && curl -fsSL https://get.helm.sh/helm-v${HELM_VERSION}-linux-${ARCH}.tar.gz \
    | tar -zxvf - \
 && mv linux-${ARCH}/helm /helm

####################
##### BASE #####
####################
FROM python AS base

USER root

RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
    --mount=type=cache,target=/var/cache/apt,sharing=locked \
    apt-get update \
 && apt-get install -y --no-install-recommends \
        curl \
        git \
        libjemalloc2 \
        passwd

ARG USER_ID=65532
ARG GROUP_ID=65532
RUN (getent group nonroot > /dev/null 2>&1 && groupmod -g ${GROUP_ID} nonroot || groupadd -g ${GROUP_ID} nonroot) \
 && (id nonroot > /dev/null 2>&1 && usermod -u ${USER_ID} -g ${GROUP_ID} nonroot || useradd -u ${USER_ID} -g ${GROUP_ID} -m -d /home/nonroot nonroot) \
 && chown -R ${USER_ID}:${GROUP_ID} /home/nonroot

COPY --from=uv /uv /uvx /usr/local/bin/

ARG UV_PROJECT_ENVIRONMENT=/opt/python
ENV PATH=${UV_PROJECT_ENVIRONMENT}/bin:$PATH
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV UV_COMPILE_BYTECODE=1
# Inspect AI relies on the metadata to determine installation status:
ENV UV_NO_INSTALLER_METADATA=0
ENV UV_LINK_MODE=copy

####################
##### BUILDERS #####
####################
FROM base AS builder-base
WORKDIR /source
COPY pyproject.toml uv.lock ./
COPY services/modules services/modules

FROM builder-base AS builder-runner
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync \
        --extra=runner \
        --extra=inspect \
        --locked \
        --no-dev \
        --no-install-project

FROM builder-base AS builder-api
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync \
        --extra=api \
        --locked \
        --no-dev \
        --no-install-project

################
##### PROD #####
################
FROM base AS runner

# Use jemalloc instead of glibc malloc to reduce long-run fragmentation.
# glibc per-thread arenas accumulate freed pages without returning them to the
# kernel; jemalloc's decay-based release is built for long-lived processes.
ENV LD_PRELOAD=libjemalloc.so.2
ENV MALLOC_CONF=background_thread:true,dirty_decay_ms:30000,muzzy_decay_ms:30000,metadata_thp:auto

COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker
COPY --from=docker-cli /usr/local/libexec/docker/cli-plugins/docker-buildx /usr/local/libexec/docker/cli-plugins/docker-buildx
COPY --from=helm /helm /usr/local/bin/helm
COPY --from=kubectl /bin/kubectl /usr/local/bin/

WORKDIR /home/nonroot/app
COPY --from=builder-runner ${UV_PROJECT_ENVIRONMENT} ${UV_PROJECT_ENVIRONMENT}
COPY --chown=nonroot:nonroot pyproject.toml uv.lock README.md .python-version ./
COPY --chown=nonroot:nonroot hawk ./hawk
RUN --mount=type=cache,target=/root/.cache/uv \
    --mount=source=services/modules,target=services/modules \
    uv sync \
        --extra=runner \
        --extra=inspect \
        --locked \
        --no-dev

USER nonroot
STOPSIGNAL SIGINT
ENTRYPOINT ["python", "-m", "hawk.runner.entrypoint"]

FROM base AS api
# Install graphviz. x11-common's postinst needs update-rc.d from init-system-helpers
RUN apt-get update \
 && apt-get install -y init-system-helpers graphviz \
 && rm -rf /var/lib/apt/lists/*

COPY --from=aws-cli /usr/local/aws-cli/v2/current /usr/local
COPY --from=helm /helm /usr/local/bin/helm

WORKDIR /home/nonroot/app
COPY --from=builder-api ${UV_PROJECT_ENVIRONMENT} ${UV_PROJECT_ENVIRONMENT}
COPY --chown=nonroot:nonroot pyproject.toml uv.lock README.md .python-version ./
COPY --chown=nonroot:nonroot hawk ./hawk
RUN --mount=type=cache,target=/root/.cache/uv \
    --mount=source=services/modules,target=services/modules \
    uv sync \
        --extra=api \
        --locked \
        --no-dev

RUN mkdir -p /home/nonroot/.aws /home/nonroot/.kube /home/nonroot/.minikube \
 && chown -R nonroot:nonroot /home/nonroot/.aws /home/nonroot/.kube /home/nonroot/.minikube

USER nonroot
ENTRYPOINT [ "uvicorn", "hawk.api.server:app" ]
# --no-access-log: uvicorn's plaintext access log is replaced by the
# structured `hawk.api.access` middleware in hawk.api.server.
CMD [ "--host=0.0.0.0", "--port=8080", "--no-access-log" ]

####################
##### JANITOR #####
####################
FROM builder-base AS builder-janitor
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync \
        --extra=janitor \
        --locked \
        --no-dev \
        --no-install-project

FROM base AS janitor
COPY --from=helm /helm /usr/local/bin/helm

WORKDIR /home/nonroot/app
COPY --from=builder-janitor ${UV_PROJECT_ENVIRONMENT} ${UV_PROJECT_ENVIRONMENT}
COPY --chown=nonroot:nonroot pyproject.toml uv.lock README.md ./
COPY --chown=nonroot:nonroot hawk ./hawk
RUN --mount=type=cache,target=/root/.cache/uv \
    --mount=source=services/modules,target=services/modules \
    uv sync \
        --extra=janitor \
        --locked \
        --no-dev

USER nonroot
ENTRYPOINT ["python", "-m", "hawk.janitor"]
