diff --git a/Dockerfile b/Dockerfile index 9bc18a5..84c032a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,4 @@ +ARG GO_BASE=golang:1.25-alpine # The control plane's image. # # novox/hq ADR 0006: this image is pinned by digest in the bundle the host carries, fetched on a @@ -11,7 +12,7 @@ # (novox/hq ADR 0004). Adding them "just in case" would put a trust store in the one image whose # whole argument is that it contains nothing to reason about. -FROM golang:1.25-alpine AS build +FROM ${GO_BASE} AS build WORKDIR /src # Dependencies first, so a change to the source does not refetch them. diff --git a/cmd/mesh-builder/Dockerfile b/cmd/mesh-builder/Dockerfile index 3c6cc5f..3951536 100644 --- a/cmd/mesh-builder/Dockerfile +++ b/cmd/mesh-builder/Dockerfile @@ -1,17 +1,19 @@ +ARG ALPINE_BASE=alpine:3 +ARG GO_BASE=golang:1.25-alpine # The builder, as a module ships one. # # Not FROM scratch, unlike the control plane: this one runs git and a container client, so it # needs a filesystem with them on it. That is the honest cost of a machine whose job is to build — # and it is why building is a MODULE on a machine that has a runtime rather than something the # control plane does (novox/hq ADR 0005). -FROM golang:1.25-alpine AS build +FROM ${GO_BASE} AS build WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -trimpath -ldflags '-s -w' -o /mesh-builder ./cmd/mesh-builder -FROM alpine:3 +FROM ${ALPINE_BASE} # git to clone what it is asked to build, and the docker client to build and push it. The daemon # is the machine's, reached through its socket — a build machine shares the runtime it was given # rather than running one inside itself. diff --git a/internal/builder/builder.go b/internal/builder/builder.go index ca7c3d6..f78ef31 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -397,13 +397,13 @@ func one(ctx context.Context, run Runner, publish Publisher, module, a.From, strings.Join(copies, ", ")) } if len(bases) > 0 { - // Said, not yet refused: the mesh's own images start FROM a public base — the control - // plane's, the builder's, the tool runtime's — and refusing those refuses genesis. - // They declare their bases next; until then a base fetched on its own is named here, - // with the remedy, every build. - say("recipe", "UNDECLARED base(s) %s in %s — declare each under build.on as "+ - "{arg, image@sha256:…} and read it from that argument (novox/hq ADR 0097)", - strings.Join(bases, ", "), a.From) + // Refused, since the mesh's own images declare theirs (ADR 0097): a base fetched on + // its own is a build that works when a public registry answers, which is sometimes. + return catalogue.Built{}, fmt.Errorf( + "%s: the recipe %s starts FROM %s, which the manifest does not declare. Declare "+ + "each under build.on as {\"arg\": \"\", \"image\": \"@sha256:…\"} "+ + "and start FROM ${} (novox/hq ADR 0097)", + module, a.From, strings.Join(bases, ", ")) } invocation := append([]string{"build", "-f", a.From, "-t", local}, args...) if a.Target != "" { diff --git a/internal/builder/standing_on_test.go b/internal/builder/standing_on_test.go index 61a94ab..9f87782 100644 --- a/internal/builder/standing_on_test.go +++ b/internal/builder/standing_on_test.go @@ -124,8 +124,7 @@ FROM golang:1.25-alpine AS go if strings.Join(copies, "|") != "${MC_BASE} (a build argument the manifest does not declare)|vendor/tool:latest" { t.Fatalf("copies out of undeclared images: %v", copies) } - // A base fetched on its own is named apart: the mesh's own images still start FROM one, so - // it is said rather than refused until they declare theirs. + // A base fetched on its own is named apart, and refused like a copy (ADR 0097). if strings.Join(bases, "|") != "golang:1.25-alpine" { t.Fatalf("undeclared bases: %v", bases) } diff --git a/module.json b/module.json index 3b80760..dbb7f0c 100644 --- a/module.json +++ b/module.json @@ -66,6 +66,12 @@ "kind": "image", "from": "Dockerfile" } + ], + "on": [ + { + "arg": "GO_BASE", + "image": "golang@sha256:1ae0735f00daffa3aaf1363a5184c0d2dc55c78e3db4ec70241cdac97bf84b59" + } ] } }