###################################
## Dockerfile Stages Build Graph ##
###################################

# fidesplus:<version>
# - base -> classify_setup -> dev
# - base -> classify_setup -> prod_build -> prod
# - base -> classify_setup -> prod_build -> test
# - base -> classify_setup -> prod_edge
# - base -> classify_setup -> prod_prerelease

# fidesplus:<version>-slim
# - base -> slim_ci_deps (local dev + CI; source bind-mounted at runtime)
# - base -> slim_dev_prerelease
# - base -> slim_prod_build -> slim_prod
# - base -> slim_prod_build -> slim_test -> fides_ci
# - base -> slim_prod_edge
# - base -> slim_prod_prerelease

############################
## Build libpbac (Go→C)  ##
############################
FROM golang:1.24-bookworm AS libpbac_builder
WORKDIR /build
COPY policy-engine/ .
RUN go build -buildmode=c-shared -o libpbac.so ./cmd/libpbac/

########################
## Base configuration ##
########################

# If you update the python version, also update `DEFAULT_PYTHON_VERSION` in the GitHub workflow files
FROM python:3.13-slim-bookworm AS base

# Add the fidesuser user but don't switch to it yet
RUN addgroup --system --gid 1000 fidesgroup
RUN adduser --system --uid 1000 --home /home/fidesuser fidesuser

#######################
## System packages   ##
#######################

# Installed once up front so later COPY/uv sync layers stay cacheable.
# curl/g++/gcc/gnupg: MSSQL + build deps; gcc-12/g++-12: fasttext
# libpq/unixodbc/freetds: Postgres + PyMSSQL
# libpango/libglib/shared-mime-info: WeasyPrint PDF generation
RUN : \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
    build-essential \
    curl \
    g++-12 \
    gcc-12 \
    git \
    gnupg \
    unzip \
    libpq-dev \
    unixodbc-dev \
    libssl-dev \
    libffi-dev \
    libkrb5-dev \
    unixodbc \
    freetds-dev \
    freetds-bin \
    python-dev-is-python3 \
    libpango1.0-dev \
    libglib2.0-dev \
    shared-mime-info \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install uv: venv holds third-party deps only; monorepo code runs from /fidesplus/src (PYTHONPATH).
WORKDIR /fidesplus
ENV UV_COMPILE_BYTECODE=0
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
    mv /root/.local/bin/uv /bin/uv
COPY --chown=fidesuser:fidesgroup pyproject.toml uv.lock README.md ./
COPY --chown=fidesuser:fidesgroup policy-engine/ policy-engine/
COPY --chown=fidesuser:fidesgroup src/fides/ src/fides/
RUN --mount=type=cache,target=/root/.cache/uv \
    uv venv && \
    uv pip install setuptools==80.10.2 wheel pybind11 hatchling hatch-vcs && \
    uv sync --no-dev --locked --no-install-project && \
    chown -R fidesuser:fidesgroup /fidesplus/.venv
ENV PATH="/fidesplus/.venv/bin:${PATH}"
ENV PYTHONPATH="/fidesplus/src"

# Remove the system pip bundled in the python:3.13-slim base image. Nothing in the
# image uses it (all installs go through uv; the uv venv is pip-less by design), and
# the base image ships an outdated pip that recurrently trips vuln scanners
# (e.g. CVE-2026-3219, CVE-2026-6357). Removing it ends this class of CVE for good.
# Target the system interpreter explicitly: PATH above points `python` at the
# pip-less venv. (ENG-3615)
RUN /usr/local/bin/python -m pip uninstall -y pip

RUN mkdir -p /fidesplus/src/fides/bin
COPY --from=libpbac_builder --chown=fidesuser:fidesgroup /build/libpbac.so /fidesplus/src/fides/bin/libpbac.so
COPY --from=libpbac_builder --chown=fidesuser:fidesgroup /build/libpbac.so /usr/local/share/fides/libpbac.so
COPY --chown=fidesuser:fidesgroup scripts/ensure_libpbac_in_tree.sh /fidesplus/scripts/ensure_libpbac_in_tree.sh
RUN chmod +x /fidesplus/scripts/ensure_libpbac_in_tree.sh

# Immediately flush to stdout, globally
ENV PYTHONUNBUFFERED=TRUE

# Enable detection of running within Docker
ENV RUNNING_IN_DOCKER=TRUE

#####################################
## GPP JS Integration Dependencies ##
#####################################

# Our privacy-experience APIs rely on using a JavaScript module to encode GPP
# strings (see gpp_utils.py), so we install Node Version Manager and Node v24
USER fidesuser
ENV NODE_VERSION=24.15.0
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
ENV NVM_DIR="/home/fidesuser/.nvm"
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
ENV PATH="${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}"

# Now install the node_modules needed for GPP JS integration
COPY --chown=fidesuser:fidesgroup ./src/fidesplus/gpp_js_integration/package.json /fidesplus/src/fidesplus/gpp_js_integration/package.json
COPY --chown=fidesuser:fidesgroup ./src/fidesplus/gpp_js_integration/package-lock.json /fidesplus/src/fidesplus/gpp_js_integration/package-lock.json
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm install

# Set working directory to fidesplus and switch back to root
WORKDIR /fidesplus
USER root

#############################
## Local dev / CI deps     ##
#############################
FROM base AS slim_ci_deps

# Bind-mount provides source at runtime (docker-compose); PYTHONPATH imports from src.
ENV PYTHONPATH="/fidesplus/src"
WORKDIR /fidesplus

ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}

# Console scripts (fides, fidesplus) require editable root install; sync alone omits them.
# src/fidesplus is copied for build-time -e .; live code comes from the bind mount at runtime.
COPY --chown=fidesuser:fidesgroup src/fidesplus/ src/fidesplus/

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group dev --locked --no-install-project && \
    uv pip install --no-deps -e .

# Applies to all users (pytest runs as fidesuser via docker exec in CI).
RUN git config --system --add safe.directory /fidesplus

USER fidesuser

# GPP encoder JS is gitignored; bake compiled/ into the image for compose anonymous volumes.
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm install && npm run build

WORKDIR /fidesplus
ENV USER=fidesuser
ENV DATAHUB_TELEMETRY_ENABLED=false

EXPOSE 8080
CMD ["fidesplus"]

# Backward-compatible alias for scripts that still pass --target=slim_dev.
FROM slim_ci_deps AS slim_dev


#####################################
## Prerelease Development Setup ##
#####################################
FROM base AS slim_dev_prerelease
ARG PRERELEASE_VERSION

# Install dev/test dependencies (kept out of base so prod images are clean)
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group dev --locked --no-install-project

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group dev --locked --no-install-project && \
    uv pip install --no-deps -e .

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory to fidesplus
WORKDIR /fidesplus

EXPOSE 8080
CMD ["fidesplus"]

##################################
## Production Application Setup ##
##################################
FROM base AS slim_prod_build

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus
# CTL manifests (check_fides_annotations / fides evaluate). Separate layer so cache
# invalidates when manifests change even if the rest of the tree is unchanged.
COPY --chown=fidesuser:fidesgroup .fides/ /fidesplus/.fides/

# Set working directory to fidesplus
WORKDIR /fidesplus

# Pre-bake version since .git is excluded from Docker build context
ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}
ENV FIDESPLUS_VERSION=${FIDESPLUS_VERSION}


# Console scripts only (editable root); ethyca-fides/fidesplus import from /fidesplus/src via PYTHONPATH.
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-dev --no-install-project && \
    uv pip install --no-deps -e .

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

FROM slim_prod_build AS slim_prod

# Git is only needed for build (pip install of git-based deps).
# Remove it now to reduce image size.
USER root
RUN apt-get remove -y git && apt-get autoremove -y && apt-get clean
USER fidesuser

EXPOSE 8080
CMD ["fidesplus"]

############################
## Production Test Setup  ##
############################
FROM slim_prod_build AS slim_test

USER root
# Add dev deps to venv; app code stays on PYTHONPATH from slim_prod_build COPY + ENV.
# Re-install console scripts after dev sync (sync alone drops -e . entry points).
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group dev --locked --no-install-project && \
    uv pip install --no-deps -e .
USER fidesuser

# CI bind-mounts the host checkout at /fidesplus; docker exec runs as fidesuser (see CI_ARGS_EXEC).
# Mark the mount safe so ctl tests can run git against a repo owned by another UID (Git 2.35+).
RUN git config --global --add safe.directory /fidesplus

# Alias for CI/docs: tests/fides (ethyca/fidesplus:local-slim). Not encoders `test` (TensorFlow/ML).
FROM slim_test AS fides_ci

#######################################
## Production Application Edge Setup ##
#######################################

# Monorepo: ethyca-fides on PYTHONPATH (/fidesplus/src), not site-packages.

FROM base AS slim_prod_edge

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

# Pre-bake version since .git is excluded from Docker build context
ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}
ENV FIDESPLUS_VERSION=${FIDESPLUS_VERSION}



RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-dev --no-install-project && \
    uv pip install --no-deps -e .

# Git is only needed for build (pip install of git-based deps).
# Remove it now to reduce image size.
RUN apt-get remove -y git && apt-get autoremove -y && apt-get clean

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

EXPOSE 8080
CMD ["fidesplus"]


#############################################
## Production Application Prerelease Setup ##
#############################################
FROM base AS slim_prod_prerelease

ARG PRERELEASE_VERSION

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

# Pre-bake version since .git is excluded from Docker build context
ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}
ENV FIDESPLUS_VERSION=${FIDESPLUS_VERSION}



RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-dev --no-install-project && \
    uv pip install --no-deps -e .

# Git is only needed for build (pip install of git-based deps).
# Remove it now to reduce image size.
RUN apt-get remove -y git && apt-get autoremove -y && apt-get clean

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

EXPOSE 8080
CMD ["fidesplus"]


####################
## Classify Setup ##
####################
FROM base AS classify_setup

ENV TF_CPP_MIN_LOG_LEVEL=3  TFHUB_CACHE_DIR="/tf_models"

RUN mkdir -p /tf_models && chown fidesuser:fidesgroup /tf_models

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group ml --locked --no-install-project

# TensorFlow loads system OpenSSL 3.x; pymssql bundles FreeTDS with OpenSSL 1.1.1k.
# Whichever loads first into the process wins.  If TF wins, FreeTDS TLS handshakes
# fail ("Adaptive Server connection failed").  A sitecustomize.py that imports pymssql
# ensures its bundled OpenSSL is loaded before TF in every Python process.
RUN python -c "import sysconfig, pathlib; \
    pathlib.Path(sysconfig.get_path('purelib'), 'sitecustomize.py').write_text( \
    'try:\n    import pymssql\nexcept ImportError:\n    pass\n')"

# en_core_web_sm / en_core_web_lg installed via uv sync --group ml (en-core-web-sm, en-core-web-lg packages)

# Required to install with no-deps which can't be done in requirements.txt just yet (PR https://github.com/pypa/pip/pull/10837)
#RUN pip install --no-deps tensorflow-io

# pre-downloads the required model
RUN python -c "import tensorflow_hub; tensorflow_hub.load('https://tfhub.dev/google/universal-sentence-encoder-large/5')"

##################
## Encoders Dev ##
##################
FROM classify_setup AS dev

# Install dev/test dependencies (kept out of base so prod images are clean)
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group dev --locked --no-install-project

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

RUN uv pip install --no-deps -e .

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

EXPOSE 8080
CMD ["fidesplus"]

##############################
## Encoders Prod Edge ##
##############################

# Monorepo: ethyca-fides on PYTHONPATH (/fidesplus/src), not site-packages.

FROM classify_setup AS prod_edge

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

# Pre-bake version since .git is excluded from Docker build context
ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}
ENV FIDESPLUS_VERSION=${FIDESPLUS_VERSION}



RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-dev --no-install-project && \
    uv pip install --no-deps -e .

# Git is only needed for build (pip install of git-based deps).
# Remove it now to reduce image size.
RUN apt-get remove -y git && apt-get autoremove -y && apt-get clean

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

EXPOSE 8080
CMD ["fidesplus"]


###################
## Encoders Prod ##
###################
FROM classify_setup AS prod_build

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

# Pre-bake version since .git is excluded from Docker build context
ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}
ENV FIDESPLUS_VERSION=${FIDESPLUS_VERSION}


# Console scripts only (editable root); ethyca-fides/fidesplus import from /fidesplus/src via PYTHONPATH.
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-dev --no-install-project && \
    uv pip install --no-deps -e .

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

FROM prod_build AS prod

# Git is only needed for build (pip install of git-based deps).
# Remove it now to reduce image size.
USER root
RUN apt-get remove -y git && apt-get autoremove -y && apt-get clean
USER fidesuser

EXPOSE 8080
CMD ["fidesplus"]

###########################
## Encoders Test Setup   ##
###########################
FROM prod_build AS test

USER root
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --group dev --locked --no-install-project && \
    uv pip install --no-deps -e .
USER fidesuser

##############################
## Encoders Prod Prerelease ##
##############################
FROM classify_setup AS prod_prerelease

# Copy application files after setup is complete to avoid busting the cache
COPY --chown=fidesuser:fidesgroup . /fidesplus

# Set working directory to fidesplus
WORKDIR /fidesplus

# Pre-bake version since .git is excluded from Docker build context
ARG FIDESPLUS_VERSION=0.0.0+docker
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FIDESPLUS_VERSION}
ENV FIDESPLUS_VERSION=${FIDESPLUS_VERSION}



RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-dev --no-install-project && \
    uv pip install --no-deps -e .

# Git is only needed for build (pip install of git-based deps).
# Remove it now to reduce image size.
RUN apt-get remove -y git && apt-get autoremove -y && apt-get clean

USER fidesuser

# This prevents getpwuid() crashes when running as non-default UIDs
ENV USER=fidesuser

# Disable third-party telemetry
ENV DATAHUB_TELEMETRY_ENABLED=false

# Set working directory to GPP JS integration and build
WORKDIR /fidesplus/src/fidesplus/gpp_js_integration
RUN npm run build

# Set working directory back to fidesplus
WORKDIR /fidesplus

EXPOSE 8080
CMD ["fidesplus"]
