From c704fed563429ef1960279e0c3868db4e2ed8389 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 21 Aug 2026 18:29:55 +0200 Subject: [PATCH] ci: build with buildctl instead of buildah (infra#241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rootless buildah in gitea-runner's hardened securityContext (capabilities: drop [ALL], allowPrivilegeEscalation: false) cannot remap the UID/GID range newuidmap/newgidmap need on a fresh base-image pull, and the pod has no OCI runtime at all. buildctl talks to the host's buildkitd over a socket, so the privileged layer extraction happens on the host and the pod's securityContext stops being in the picture. Same pattern as mathias/infra's segment-embedder.yml and postgres18.yml. A template is the highest-leverage place to fix this. Every repo generated from here inherits its CI, so a buildah-based template does not just carry the bug — it manufactures new instances of it. A fleet inventory for infra#241 found nine repos still invoking buildah, and two of them are the templates. Note the failure is latent, not immediate: warm base-image layers mask it, so a generated repo builds fine until the first genuinely cold pull. That is exactly the situation a NEW repo is in. Verified by running the exact command against this repo's real build context: buildctl --addr unix:///run/buildkit/buildkitd.sock build \ --frontend dockerfile.v0 --local context=. --local dockerfile=. \ --output type=oci,dest=$TAR -> exporting manifest sha256:5a841529... DONE, 6.8MB OCI archive That check matters here because this repo's own CI cannot validate the change: the build job is deliberately skipped on the raw template (2ba2698), since __PROJECT_NAME__ is not a legal image name. The next generated repo is the first place it runs for real. skopeo copy replaces `buildah push` — buildctl has no registry-push equivalent — and runs twice to preserve the :sha and :latest tags the previous version pushed. --- .gitea/workflows/cd.yml | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/cd.yml b/.gitea/workflows/cd.yml index 46b64a7..0c3cdae 100644 --- a/.gitea/workflows/cd.yml +++ b/.gitea/workflows/cd.yml @@ -54,18 +54,41 @@ jobs: SHA=$(git rev-parse --short HEAD) echo "sha-tag=${SHA}" >> "$GITHUB_OUTPUT" + # infra#241: buildctl, NOT buildah. Rootless buildah in gitea-runner's + # hardened securityContext (capabilities: drop [ALL], + # allowPrivilegeEscalation: false) cannot remap the UID/GID range + # newuidmap/newgidmap need on a fresh base-image pull, and it has no OCI + # runtime at all. buildctl talks to the host's buildkitd over a socket, so + # the privileged extraction happens in buildkitd on the host and never in + # this pod. Same pattern as mathias/infra's segment-embedder.yml. + # + # This matters MORE in a template than anywhere else: every repo generated + # from here inherits its CI, so a buildah-based template manufactures new + # instances of a bug that has already cost this estate several red builds. - name: Build and push to local registry run: | + set -e + BUILDKIT_HOST="unix:///run/buildkit/buildkitd.sock" REGISTRY="localhost:5000" REF="${REGISTRY}/${{ env.IMAGE }}:${{ steps.meta.outputs.sha-tag }}" - buildah build \ - --label "org.opencontainers.image.revision=${{ github.sha }}" \ - -t ${REF} \ - -t ${REGISTRY}/${{ env.IMAGE }}:latest \ - . - buildah push --tls-verify=false ${REF} - buildah push --tls-verify=false ${REGISTRY}/${{ env.IMAGE }}:latest - echo "✓ Image pushed to ${REF}" + TAR_FILE="$(mktemp)" + trap 'rm -f "$TAR_FILE"' EXIT + + buildctl --addr "$BUILDKIT_HOST" build \ + --frontend dockerfile.v0 \ + --local context=. \ + --local dockerfile=. \ + --opt label:org.opencontainers.image.revision="${{ github.sha }}" \ + --output type=oci,dest="$TAR_FILE" + + # buildctl has no registry-push equivalent to `buildah push`; skopeo + # copies the OCI archive up. Two copies because the buildah version + # tagged both :sha and :latest. + skopeo copy --dest-tls-verify=false \ + oci-archive:"$TAR_FILE" docker://${REF} + skopeo copy --dest-tls-verify=false \ + oci-archive:"$TAR_FILE" docker://${REGISTRY}/${{ env.IMAGE }}:latest + echo "✓ Image pushed to ${REF} and :latest" deploy: name: Deploy via GitOps