fix(ci): authenticate go mod download against the private cad module

Build & Import (run #68) failed: mathias/cad is PRIVATE (unlike
mcp-chassis, the only other internal module a cad-* Dockerfile has fetched
so far, which is public) — 'go mod download' inside the Docker build had
no credential and died on 'terminal prompts disabled'.

Fix mirrors this repo's own existing GIT_ASKPASS pattern (cd.yml's
oathcandidate step, for the private swedsl dependency): a BuildKit secret
mount feeds a GIT_ASKPASS helper for the one RUN that needs it. The token
is never written to a persisted layer — the askpass script and its
reference to /run/secrets/gitea_token are created and removed within the
same RUN instruction. cd.yml writes the token to a file only for
build-image.sh's subprocess and passes it via BUILD_SECRETS (the script's
existing mechanism — 'the caller writes the secret to PATH itself').

Verified by running build-image.sh's actual buildctl invocation locally
against the real koala buildkitd (same daemon CI uses) with a real token —
'go mod download' completes in 1.9s, full build succeeds. Reproduced the
CI failure log before writing this fix, per house discipline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 13:31:59 +02:00
co-authored by Claude Sonnet 5
parent 97d0f56383
commit d49bb5fa6c
2 changed files with 36 additions and 2 deletions
+19 -1
View File
@@ -146,11 +146,29 @@ jobs:
# byte-identical copy of mathias/infra's ci-templates/build-image.sh. # byte-identical copy of mathias/infra's ci-templates/build-image.sh.
# Do not edit it here -- edit the canonical file, run `task ci:sync`, # Do not edit it here -- edit the canonical file, run `task ci:sync`,
# and infra's drift gate names any repo still carrying an old copy. # and infra's drift gate names any repo still carrying an old copy.
#
# cad-atlas#11: the build now fetches git.d-ma.be/mathias/cad, a
# PRIVATE module (unlike mcp-chassis, the only other internal module
# any cad-* repo's Dockerfile has fetched so far, which is public) --
# `go mod download` inside the Dockerfile needs to authenticate. The
# token is written to a file ONLY for the build-image.sh subprocess and
# passed as a BuildKit secret (build-image.sh's existing BUILD_SECRETS
# mechanism: "the caller writes the secret to PATH itself... a secret
# VALUE must never be passed here"), never as a build-arg or env var
# that would land in the image or this log.
env: env:
SHA_TAG: ${{ steps.meta.outputs.sha-tag }} SHA_TAG: ${{ steps.meta.outputs.sha-tag }}
GIT_SHA: ${{ github.sha }} GIT_SHA: ${{ github.sha }}
BUILD_ARGS: VERSION=${{ steps.meta.outputs.version }} BUILD_ARGS: VERSION=${{ steps.meta.outputs.version }}
run: bash .gitea/build-image.sh DMABE_GITEA_API_TOKEN: ${{ secrets.DMABE_GITEA_API_TOKEN }}
run: |
set -euo pipefail
TOKEN_FILE=$(mktemp)
printf '%s' "$DMABE_GITEA_API_TOKEN" > "$TOKEN_FILE"
chmod 600 "$TOKEN_FILE"
export BUILD_SECRETS="id=gitea_token,src=${TOKEN_FILE}"
bash .gitea/build-image.sh
shred -u "$TOKEN_FILE"
deploy: deploy:
name: Deploy via GitOps name: Deploy via GitOps
+17 -1
View File
@@ -6,6 +6,18 @@ WORKDIR /src
# proxy.golang.org. Gitea returns http:// in its go-import meta tag, so # proxy.golang.org. Gitea returns http:// in its go-import meta tag, so
# rewrite to https and bypass the module proxy + sumdb (mirrors mathias/cad's # rewrite to https and bypass the module proxy + sumdb (mirrors mathias/cad's
# own Dockerfile — same trick, same reason). # own Dockerfile — same trick, same reason).
#
# UNLIKE mcp-chassis (public, what cad's own Dockerfile fetches this way),
# mathias/cad is a PRIVATE repo, so the plain insteadOf rewrite above is not
# enough — `go mod download` needs to authenticate. Use a GIT_ASKPASS helper
# reading a BuildKit secret mount (mirrors the GIT_ASKPASS pattern this
# repo's own cd.yml already uses for the oathcandidate module's private
# swedsl dependency): the token is never written to a persisted layer — the
# askpass script and its reference to /run/secrets/gitea_token are created
# and removed within the same RUN instruction, and the secret mount itself
# is never part of any layer diff. Caller passes it via
# `BUILD_SECRETS="id=gitea_token,src=<path>"` (build-image.sh's existing
# mechanism) — see cd.yml.
RUN apk add --no-cache git && \ RUN apk add --no-cache git && \
git config --global url."https://git.d-ma.be/".insteadOf "http://git.d-ma.be/" git config --global url."https://git.d-ma.be/".insteadOf "http://git.d-ma.be/"
ENV GOPRIVATE=git.d-ma.be ENV GOPRIVATE=git.d-ma.be
@@ -13,7 +25,11 @@ ENV GOPROXY=direct
ENV GOSUMDB=off ENV GOSUMDB=off
RUN go install github.com/a-h/templ/cmd/templ@latest RUN go install github.com/a-h/templ/cmd/templ@latest
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN --mount=type=secret,id=gitea_token \
printf '#!/bin/sh\ncase "$1" in *sername*) echo oauth2 ;; *) cat /run/secrets/gitea_token ;; esac\n' > /tmp/askpass.sh && \
chmod 700 /tmp/askpass.sh && \
GIT_ASKPASS=/tmp/askpass.sh GIT_TERMINAL_PROMPT=0 go mod download && \
rm -f /tmp/askpass.sh
COPY . . COPY . .
ARG VERSION=dev ARG VERSION=dev
RUN templ generate && CGO_ENABLED=0 go build -trimpath \ RUN templ generate && CGO_ENABLED=0 go build -trimpath \