# syntax=docker/dockerfile:1
#
# Multi-stage so the private-module credentials (used to fetch the majordomo
# dependency) live ONLY in the build stage via BuildKit secrets and never land
# in the final image. Mirrors mort's Dockerfile secret idiom.

FROM golang:1.26 AS build
ARG GIT_HOST=gitea.stevedudenhoeffer.com
ENV CGO_ENABLED=0 \
    GOFLAGS=-mod=mod \
    GOSUMDB=off \
    GOTOOLCHAIN=auto
ENV GOPRIVATE=${GIT_HOST}/* GONOSUMDB=${GIT_HOST}/*
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=secret,id=REGISTRY_USER \
    --mount=type=secret,id=REGISTRY_PASSWORD \
    --mount=type=cache,target=/go/pkg/mod \
    git config --global url."https://$(cat /run/secrets/REGISTRY_USER):$(cat /run/secrets/REGISTRY_PASSWORD)@${GIT_HOST}/".insteadOf "https://${GIT_HOST}/" \
    && go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build -trimpath -ldflags="-s -w" -o /out/gadfly ./cmd/gadfly

FROM alpine:3.20
# procps provides pkill/pgrep, which entrypoint.sh's per-PR wall-clock backstop
# (GADFLY_PR_BUDGET_SECS) uses to stop the review subtrees — busybox's applets
# are not guaranteed to include them.
RUN apk add --no-cache bash git curl jq ca-certificates nodejs npm procps
# Bundle the Claude Code CLI so the `claude-code` review engine works out of the
# box (GADFLY_MODELS=claude-code or claude-code/<model>). This adds Node + the
# CLI to the image (notably larger); ollama-only users pay the size but nothing
# else. Auth is provided at runtime via CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY.
RUN npm install -g @anthropic-ai/claude-code && npm cache clean --force
# Bundle the OpenCode CLI (opencode.ai) for the `opencode` review engine
# (GADFLY_MODELS=opencode/<model>): a freely-available agentic harness driving an
# ollama-cloud model, used to benchmark it against gadfly's own executus harness
# on the same model. Auth reuses OLLAMA_CLOUD_API_KEY at runtime. opencode ships a
# compiled (Bun) binary; it publishes musl variants (opencode-linux-*-musl) that
# npm auto-selects on alpine via the package "libc" field. libstdc++/libgcc are
# the Bun binary's runtime deps; gcompat is a belt-and-suspenders fallback in case
# npm ever resolves a glibc build here.
RUN apk add --no-cache gcompat libstdc++ libgcc \
    && npm install -g opencode-ai \
    && npm cache clean --force
# Best-effort: confirm the binary runs and pre-warm the openai-compatible provider
# package into opencode's cache so a review doesn't pay a first-run npm fetch. The
# warm-up model call intentionally fails against a dead URL. Never fail the build:
# a musl/runtime quirk here must not break the shared image for ollama/claude
# users — a broken opencode engine degrades to a normal (advisory) pass error.
RUN opencode --version >/dev/null 2>&1 \
    && OPENCODE_CONFIG_CONTENT='{"provider":{"gadfly":{"npm":"@ai-sdk/openai-compatible","options":{"baseURL":"http://127.0.0.1:9/v1"},"models":{"x":{}}}}}' \
       timeout 120 opencode run --model gadfly/x "warm" >/dev/null 2>&1 \
    ; true
COPY --from=build /out/gadfly /usr/local/bin/gadfly
COPY scripts /app/scripts
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh /app/scripts/run.sh /app/scripts/status-board.sh /usr/local/bin/gadfly
ENTRYPOINT ["/entrypoint.sh"]
