# 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/). 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/): 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"]