mirror of
https://github.com/hanzoai/ci.git
synced 2026-10-20 15:28:05 +00:00
The official golang images set GOTOOLCHAIN=local, so a go.mod requiring a newer Go than the base image does not degrade to a download — it dies mid-build: go: go.mod requires go >= 1.26.5 (running go 1.26.4; GOTOOLCHAIN=local) visor v1.108.16 shipped that failure. The reason it reached a release build is that nothing local can see it: a dev box runs GOTOOLCHAIN=auto and simply fetches what the module asks for, so `go build` is green right up until the image build. The break is introduced by editing go.mod, a file with nothing to do with Docker. It is not one repo's problem. Sweeping every Dockerfile across the orgs — all variants, all subdirectories, not just repo roots — found 54 builder stages already below their own go.mod in 23 repos, and only 7 of 223 Go stages setting GOTOOLCHAIN=auto. Fixing those 54 fixes today; this makes the 55th impossible, which is the part worth having. bin/gover is that check, run by the build lane before any build work so a mismatch costs seconds instead of a binfmt install and a layer cache. It refuses only what cannot build: an image BELOW the module floor. Pinning ahead of go.mod is valid and stays silent, because a newer toolchain compiling an older directive always works. A floating tag against a patch-pinned go.mod warns and passes — it builds today, and a gate that fails what builds is a gate people learn to skip. The error names the remediation rather than just saying no: pin the base to the version go.mod asks for, and add ENV GOTOOLCHAIN=auto so the next bump downloads its toolchain instead of failing. This repo's own module and image move to go1.26.5 and adopt that same GOTOOLCHAIN=auto line, so the pipeline holds itself to the rule it enforces. bin/gover_test.sh pins both directions — the refusals and the allowances (newer-than-floor, alpine suffix vs Go patch, ARG defaults, and a multi-module repo judged by its nearest go.mod). Offline, no registry. Co-authored-by: Hanzo Dev <[email protected]>
42 lines
2.1 KiB
Docker
42 lines
2.1 KiB
Docker
# syntax=docker/dockerfile:1
|
|
#
|
|
# ci — the ci.hanzo.ai dashboard. Pure-Go, no cgo, no node: the page is
|
|
# server-rendered from a template compiled into the binary, and the design
|
|
# tokens it spends are @hanzo/brand's published stylesheet, go:embed-ed beside
|
|
# it. So the image is still the binary and a CA bundle — nothing served from
|
|
# disk, nothing to go stale against the code, and no JS toolchain on the path
|
|
# that ships the board you read when the builds are broken.
|
|
FROM golang:1.26.5-alpine AS builder
|
|
WORKDIR /build
|
|
# Resolve through the module proxy: proxy.golang.org and sum.golang.org agree
|
|
# and neither can change under us, which a direct fetch against a moved tag
|
|
# cannot promise.
|
|
ENV GOPROXY=https://proxy.golang.org,direct
|
|
# The base image above is pinned to exactly the Go go.mod asks for, so nothing
|
|
# is downloaded here — the pin is what makes this build hermetic. GOTOOLCHAIN
|
|
# is set to auto anyway, because the golang images default it to `local` and
|
|
# that turns the NEXT go.mod bump from "fetches the toolchain it needs" into
|
|
# "dies mid-build with go.mod requires go >= X". The pin is the fast path; this
|
|
# is the one that keeps a version bump from being a build break. bin/gover
|
|
# gates the same rule for every repo this pipeline builds.
|
|
ENV GOTOOLCHAIN=auto
|
|
COPY go.mod ./
|
|
RUN --mount=type=cache,id=ci-gomod,target=/go/pkg/mod go mod download
|
|
COPY . .
|
|
RUN --mount=type=cache,id=ci-gomod,target=/go/pkg/mod \
|
|
--mount=type=cache,target=/root/.cache/go-build \
|
|
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /build/ci .
|
|
|
|
FROM alpine:3.21
|
|
RUN apk add --no-cache ca-certificates tzdata \
|
|
&& addgroup -S hanzo && adduser -S hanzo -G hanzo
|
|
COPY --from=builder /build/ci /app/ci
|
|
USER hanzo
|
|
EXPOSE 8080
|
|
# Liveness only. Readiness deliberately does not gate on having a snapshot — see
|
|
# the /healthz comment in main.go: a Hanzo Git outage must render as a dashboard
|
|
# saying so, not as this pod leaving the load balancer as well.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
|
CMD wget -qO- http://127.0.0.1:8080/healthz || exit 1
|
|
ENTRYPOINT ["/app/ci"]
|