# Lint phase. script/lint builds it alone. The linter is run directly:
# `make lint` and script/lint are themselves a docker build.
# golangci/golangci-lint:v2.12.2, 2026-10-04
FROM golangci/golangci-lint@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint

# The linter compiles every package, and govips needs the libvips
# headers for that. This image is Debian and has no apk, so they come
# from apt-get rather than script/bootstrap.
RUN apt-get update \
    && apt-get install -y --no-install-recommends libvips-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN golangci-lint run --config .golangci.yml ./...

# Test phase. script/test builds it alone.
# golang:1.25.4-alpine, 2026-02-25
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS test

WORKDIR /src

# script/bootstrap installs the build dependencies (a C compiler and the
# libvips and libheif headers) and downloads the Go modules.
COPY script/ ./script/
COPY go.mod go.sum ./
RUN script/bootstrap

COPY . .

# Without -v first; on a failure, again with -v for the details, and
# the step fails even if the second run passes.
RUN go test -count=1 -timeout 90s -race -cover ./... || \
    { echo "--- Rerunning with -v for details ---"; \
      go test -count=1 -timeout 90s -race -v ./...; exit 1; }

# Build stage. Nothing is wanted from the two phases above: these copies
# make BuildKit build them first, so this stage runs only when lint and
# test passed.
# golang:1.25.4-alpine, 2026-02-25
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder

COPY --from=lint /src/go.sum /dev/null
COPY --from=test /src/go.sum /dev/null

WORKDIR /src

# Build dependencies and Go modules, as in the test phase
COPY script/ ./script/
COPY go.mod go.sum ./
RUN script/bootstrap

# Copy source code
COPY . .

# VERSION is declared here, not earlier: a new value reruns only the
# build, not script/bootstrap. Given none, the version is
# `git describe --tags --always` of the .git in the build context (git
# comes from script/bootstrap): the tag on a tagged commit, tag-N-gHASH
# after one, the short commit when no tag is reachable. A context that
# carries .git and still yields no version fails the build; one without
# .git, as from a source tarball, stamps an empty version. CGO stays
# enabled for govips; -trimpath keeps build paths out of the binary, and
# -s -w leave out the symbol table and debug information.
ARG VERSION
RUN version="${VERSION:-$(git describe --tags --always)}"; \
    if [ -e .git ] && { [ -z "$version" ] || [ "$version" = dev ] || \
        [ "$version" = unknown ]; }; then \
        echo "the build context carries .git but yields no version" >&2; \
        exit 1; \
    fi; \
    CGO_ENABLED=1 GOTOOLCHAIN=auto go build -trimpath \
        -ldflags "-s -w -X main.Version=${version}" \
        -o /pixad ./cmd/pixad

# Runtime stage, and the last one: a plain `docker build .` builds this
# stage and what it depends on, and nothing else.
# alpine:3.21, 2026-02-25
FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709

# Install runtime dependencies only
RUN apk add --no-cache \
    vips \
    libheif \
    ca-certificates \
    tzdata \
    su-exec

# Copy binary from builder
COPY --from=builder /pixad /usr/local/bin/pixad
COPY deploy/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

# Create non-root user, config directory, and data directory. pixad
# gets uid and gid 65532, which host login and system accounts do not
# use: a bind-mounted /var/lib/pixa is given to pixad, and on the host
# it must not belong to a person's account.
RUN addgroup -g 65532 pixad && \
    adduser -D -H -s /sbin/nologin -u 65532 -G pixad pixad && \
    mkdir -p /var/lib/pixa /etc/pixa && \
    chown pixad:pixad /var/lib/pixa

# No USER: the entrypoint must start as root to give a bind-mounted
# /var/lib/pixa to pixad; it then runs the server as pixad.
WORKDIR /var/lib/pixa

EXPOSE 8080

# Shell form so the probe follows PORT; a port set only in a mounted
# config file is not seen here.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD wget --spider -q "http://localhost:${PORT:-8080}/.well-known/healthcheck.json" || exit 1

# Settings come from PORT and the PIXA_ environment variables; only
# PIXA_SIGNING_KEY is required. A config file mounted at
# /etc/pixa/config.yml is optional and is read when present.
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
