# syntax = docker/dockerfile:1.4

# Builds a minimal image with the binary only. It is multi-arch capable,
# cross-building to aarch64 and x86_64. When cross-compiling, Docker sets two
# implicit BUILDARG: BUILDPLATFORM being the host platform and TARGETPLATFORM
# being the platform being built.

# The Debian version and version name must be in sync
ARG DEBIAN_VERSION=11
ARG DEBIAN_VERSION_NAME=bullseye
ARG RUSTC_VERSION=1.72.0
# XXX: Upgrade to 0.10.0 blocked by https://github.com/ziglang/zig/issues/10915#issuecomment-1354548110
# XXX: Upgrade to 0.11.0 blocked by https://github.com/rust-cross/cargo-zigbuild/issues/162
ARG ZIG_VERSION=0.9.1
ARG NODEJS_VERSION=18.17.1
ARG OPA_VERSION=0.55.0
ARG CARGO_AUDITABLE_VERSION=0.6.1
ARG CARGO_CHEF_VERSION=0.1.62
ARG CARGO_ZIGBUILD_VERSION=0.17.3

##########################################
## Build stage that builds the frontend ##
##########################################
FROM --platform=${BUILDPLATFORM} docker.io/library/node:${NODEJS_VERSION}-${DEBIAN_VERSION_NAME} AS frontend

WORKDIR /app/frontend

COPY ./frontend/package.json ./frontend/package-lock.json /app/frontend/
# Network access: to fetch dependencies
RUN --network=default \
  npm ci

COPY ./frontend/ /app/frontend/
COPY ./templates/ /app/templates/
RUN --network=none \
  npm run build

# Move the built files
RUN --network=none \
  mkdir -p /share/assets && \
  cp ./dist/manifest.json /share/manifest.json && \
  rm -f ./dist/index.html* ./dist/manifest.json* && \
  cp ./dist/* /share/assets/

##############################################
## Build stage that builds the OPA policies ##
##############################################
FROM --platform=${BUILDPLATFORM} docker.io/library/buildpack-deps:${DEBIAN_VERSION_NAME} AS policy

ARG BUILDOS
ARG BUILDARCH
ARG OPA_VERSION

# Download Open Policy Agent
ADD --chmod=755 https://github.com/open-policy-agent/opa/releases/download/v${OPA_VERSION}/opa_${BUILDOS}_${BUILDARCH}_static /usr/local/bin/opa

WORKDIR /app/policies
COPY ./policies /app/policies
RUN --network=none  \
  make -B && \
  chmod a+r ./policy.wasm

##########################################################################
## Base image with cargo-chef and the right cross-compilation toolchain ##
##########################################################################
FROM --platform=${BUILDPLATFORM} docker.io/library/rust:${RUSTC_VERSION}-${DEBIAN_VERSION_NAME} AS toolchain

ARG CARGO_AUDITABLE_VERSION
ARG CARGO_CHEF_VERSION
ARG CARGO_ZIGBUILD_VERSION
ARG RUSTC_VERSION
ARG ZIG_VERSION

# Make cargo use the git cli for fetching dependencies
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true

# Install pinned versions of cargo-chef, cargo-zigbuild and cargo-auditable
# Network access: to fetch dependencies
RUN --network=default \
  cargo install --locked \
    cargo-chef@=${CARGO_CHEF_VERSION} \
    cargo-zigbuild@=${CARGO_ZIGBUILD_VERSION} \
    cargo-auditable@=${CARGO_AUDITABLE_VERSION}

# Download zig compiler for cross-compilation
# Network access: to download zig
RUN --network=default \
  curl -L "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz" | tar -J -x -C /usr/local && \
  ln -s "/usr/local/zig-linux-$(uname -m)-${ZIG_VERSION}/zig" /usr/local/bin/zig

# Install all cross-compilation targets
# Network access: to download the targets
RUN --network=default \
  rustup target add  \
    --toolchain "${RUSTC_VERSION}" \
    x86_64-unknown-linux-musl \
    aarch64-unknown-linux-musl

# Set the working directory
WORKDIR /app

#####################################
## Run the planner from cargo-chef ##
#####################################
FROM --platform=${BUILDPLATFORM} toolchain AS planner
COPY ./Cargo.toml ./Cargo.lock /app/
COPY ./crates /app/crates
RUN --network=none \
    cargo chef prepare --recipe-path recipe.json --bin crates/cli

########################
## Actual build stage ##
########################
FROM --platform=${BUILDPLATFORM} toolchain AS builder

# Build dependencies
COPY --from=planner /app/recipe.json recipe.json
# Network access: cargo-chef cook fetches the dependencies
RUN --network=default \
  cargo chef cook \
    --zigbuild \
    --bin mas-cli \
    --release \
    --recipe-path recipe.json \
    --no-default-features \
    --features docker \
    --target x86_64-unknown-linux-musl \
    --target aarch64-unknown-linux-musl \
    --package mas-cli

# Build the rest
COPY ./Cargo.toml ./Cargo.lock /app/
COPY ./crates /app/crates
ENV SQLX_OFFLINE=true
# Network access: cargo auditable needs it
RUN --network=default \
  cargo auditable zigbuild \
    --locked \
    --release \
    --bin mas-cli \
    --no-default-features \
    --features docker \
    --target x86_64-unknown-linux-musl \
    --target aarch64-unknown-linux-musl

# Move the binary to avoid having to guess its name in the next stage
RUN --network=none \
  mv "target/x86_64-unknown-linux-musl/release/mas-cli" /usr/local/bin/mas-cli-amd64
RUN --network=none \
  mv "target/aarch64-unknown-linux-musl/release/mas-cli" /usr/local/bin/mas-cli-arm64

#######################################
## Prepare /usr/local/share/mas-cli/ ##
#######################################
FROM --platform=${BUILDPLATFORM} scratch AS share

COPY --from=frontend /share /share
COPY --from=policy /app/policies/policy.wasm /share/policy.wasm
COPY ./templates/ /share/templates
COPY ./translations/ /share/translations

##################################
## Runtime stage, debug variant ##
##################################
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/static-debian${DEBIAN_VERSION}:debug-nonroot AS debug

ARG TARGETARCH
COPY --from=builder /usr/local/bin/mas-cli-${TARGETARCH} /usr/local/bin/mas-cli
COPY --from=share /share /usr/local/share/mas-cli

WORKDIR /
ENTRYPOINT ["/usr/local/bin/mas-cli"]

###################
## Runtime stage ##
###################
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/static-debian${DEBIAN_VERSION}:nonroot

ARG TARGETARCH
COPY --from=builder /usr/local/bin/mas-cli-${TARGETARCH} /usr/local/bin/mas-cli
COPY --from=share /share /usr/local/share/mas-cli

WORKDIR /
ENTRYPOINT ["/usr/local/bin/mas-cli"]
