From dc6fb7f5f2b8443c8fa5ee0031e080e85d80bdd2 Mon Sep 17 00:00:00 2001 From: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com> Date: Wed, 14 Oct 2026 01:54:53 +0000 Subject: [PATCH] ci(buildkite): stop the coverage build racing suites over its image tag (#13439) Every agent shares one Docker daemon, and the coverage build used the same `authelia:dist` tag that every suite's `pre-command` loads, retags and removes. When another build's suites loaded their image between this build's `docker build` and the `docker save` in `pre-artifact`, the tag was gone (`reference does not exist`) or pointed at the other build's image. Since `pre-artifact` had no `pipefail`, the failed save uploaded an empty archive, the job passed and every suite in the build failed on load. The coverage image is now built under a per-job tag through a new `--tag` flag on `authelia-scripts docker build`. `pre-artifact` takes `authelia:dist` only to save the archive, inside the same lock the suites hold around their load, and `pre-exit` removes the per-job tag. With every write to `authelia:dist` under that lock, suites can no longer pick up another build's image between their load and retag either. `pre-artifact` now fails the job when the save fails, and skips the save when the build itself failed or was cancelled. Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com> --- .buildkite/hooks/pre-artifact | 11 +++++++++-- .buildkite/hooks/pre-exit | 1 + .buildkite/pipeline.sh | 2 +- cmd/authelia-scripts/cmd/docker.go | 7 ++++--- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.buildkite/hooks/pre-artifact b/.buildkite/hooks/pre-artifact index 326ec2471..640e99b01 100755 --- a/.buildkite/hooks/pre-artifact +++ b/.buildkite/hooks/pre-artifact @@ -14,6 +14,13 @@ if [[ "${BUILDKITE_LABEL}" == ":hammer_and_wrench: Unit Test" ]]; then rm -rf goreleaser fi -if [[ "${BUILDKITE_LABEL}" == ":docker: Build Image [coverage]" ]]; then - docker save "${DOCKER_IMAGE}" | zstdmt -T0 -12 > "authelia-image-coverage.tar.zst" +if [[ "${BUILDKITE_LABEL}" == ":docker: Build Image [coverage]" ]] && [[ "${BUILDKITE_COMMAND_EXIT_STATUS}" == 0 ]]; then + set -o pipefail + ( + flock 9 + docker tag "authelia:dist-${BUILDKITE_JOB_ID}" "${DOCKER_IMAGE}" && docker save "${DOCKER_IMAGE}" | zstdmt -T0 -12 > "authelia-image-coverage.tar.zst" + rc=$? + docker rmi "${DOCKER_IMAGE}" > /dev/null 2>&1 + exit "${rc}" + ) 9> /buildkite/.cache/authelia-image-load.lock || exit 1 fi diff --git a/.buildkite/hooks/pre-exit b/.buildkite/hooks/pre-exit index d931d2fa1..c264eca68 100755 --- a/.buildkite/hooks/pre-exit +++ b/.buildkite/hooks/pre-exit @@ -22,6 +22,7 @@ if [[ ! "${BUILDKITE_COMMAND}" =~ buildkite-agent\ pipeline\ upload ]]; then docker network ls -q --filter "label=com.docker.compose.project=${PROJECT}" | xargs -r docker network rm docker images --filter "reference=${PROJECT}-*" --format '{{.Repository}}:{{.Tag}}' | xargs -r docker rmi -f > /dev/null 2>&1 [[ -n "${SUITE_IMAGE}" ]] && docker rmi -f "${SUITE_IMAGE}" > /dev/null 2>&1 + [[ "${BUILDKITE_LABEL}" == ":docker: Build Image [coverage]" ]] && docker rmi -f "authelia:dist-${BUILDKITE_JOB_ID}" > /dev/null 2>&1 if [[ "${BUILDKITE_LABEL}" == ":grype: Vulnerability Scanning" ]]; then docker images --filter "reference=authelia/authelia:*" --filter "reference=authelia/authelia-cve:*" --format '{{.Repository}}:{{.Tag}}' | xargs -r docker rmi > /dev/null 2>&1 diff --git a/.buildkite/pipeline.sh b/.buildkite/pipeline.sh index e5f46e7a7..531adefd1 100755 --- a/.buildkite/pipeline.sh +++ b/.buildkite/pipeline.sh @@ -87,7 +87,7 @@ steps: if: build.env("CI_BYPASS") != "true" - label: ":docker: Build Image [coverage]" - command: "authelia-scripts docker build --container=coverage" + command: "authelia-scripts docker build --container=coverage --tag=authelia:dist-\$\${BUILDKITE_JOB_ID}" retry: manual: permit_on_passed: true diff --git a/cmd/authelia-scripts/cmd/docker.go b/cmd/authelia-scripts/cmd/docker.go index 8d0efd199..a651aff15 100644 --- a/cmd/authelia-scripts/cmd/docker.go +++ b/cmd/authelia-scripts/cmd/docker.go @@ -15,7 +15,7 @@ import ( "github.com/spf13/cobra" ) -var container string +var container, intermediateTag string var ( containers = []string{"dev", "coverage"} @@ -61,6 +61,7 @@ func newDockerBuildCmd() (cmd *cobra.Command) { } cmd.PersistentFlags().StringVar(&container, "container", defaultContainer, "target container among: "+strings.Join(containers, ", ")) + cmd.PersistentFlags().StringVar(&intermediateTag, "tag", IntermediateDockerImageName, "local tag the image is built as") return cmd } @@ -91,7 +92,7 @@ func cmdDockerBuildRun(_ *cobra.Command, _ []string) { docker := &Docker{} - err = docker.Tag(IntermediateDockerImageName, DockerImageName) + err = docker.Tag(intermediateTag, DockerImageName) if err != nil { log.Fatal(err) } @@ -163,7 +164,7 @@ func dockerBuildOfficialImage(arch string) error { log.Fatal(err) } - return docker.Build(IntermediateDockerImageName, dockerfile, ".", buildMetaData) + return docker.Build(intermediateTag, dockerfile, ".", buildMetaData) } func login(docker *Docker, registry string) {