From 142d9f8c88ec4f9f8190e421c942ad7619490313 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 4 Nov 2026 07:08:45 +0000 Subject: [PATCH] fix: give each Dockerfile action image its own ID (#1372) Identical Dockerfile builds in concurrent jobs share one image ID under their per-job tags. When a job ends and removes its tag, the daemon deletes the image while another job's build is about to tag it, failing that job with `No such image`. This also hits `TestRunEvent` in CI, where two parallel subtests build the same action. Label each build with its tag, so every job gets its own image on top of the shared cached layers and removing it never touches another job's. Reviewed-on: https://gitea.com/gitea/runner/pulls/1372 Reviewed-by: bircni Co-authored-by: silverwind --- act/container/docker_build.go | 1 + act/container/docker_build_test.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/act/container/docker_build.go b/act/container/docker_build.go index b112466a..1efe797a 100644 --- a/act/container/docker_build.go +++ b/act/container/docker_build.go @@ -48,6 +48,7 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor { options := client.ImageBuildOptions{ Tags: []string{input.ImageTag}, + Labels: map[string]string{"com.gitea.runner.image": input.ImageTag}, // own image ID per tag, so removing one never deletes another job's identical build Remove: true, AuthConfigs: LoadDockerAuthConfigs(ctx), Dockerfile: input.Dockerfile, diff --git a/act/container/docker_build_test.go b/act/container/docker_build_test.go index 39f27f55..2b71efe2 100644 --- a/act/container/docker_build_test.go +++ b/act/container/docker_build_test.go @@ -18,10 +18,12 @@ import ( "github.com/stretchr/testify/require" ) -func TestDockerBuildReportsDaemonError(t *testing.T) { +func TestDockerBuildLabelsImageWithItsTagAndReportsDaemonError(t *testing.T) { + var labels string daemon := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("API-Version", "1.47") if strings.HasSuffix(request.URL.Path, "/build") { + labels = request.URL.Query().Get("labels") writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusInternalServerError) _, _ = io.WriteString(writer, `{"message":"build rejected"}`) @@ -35,6 +37,7 @@ func TestDockerBuildReportsDaemonError(t *testing.T) { require.ErrorContains(t, NewDockerBuildExecutor(NewDockerBuildExecutorInput{ ImageTag: "action:test", Dockerfile: "Dockerfile", BuildContext: strings.NewReader(""), })(t.Context()), "build rejected") + require.JSONEq(t, `{"com.gitea.runner.image":"action:test"}`, labels) } func TestCreateBuildContextFollowsDockerignoreAndSymlinkedRootButKeepsDockerfile(t *testing.T) {