Repository: docker/compose
Pull request #12683 (2025-03-28 08:51:32)

include implicit build dependencies in build command
**What I did**
include dependent services declared as additional_context running `docker compose build` with a service name

**Related issue**
fixes https://github.com/docker/compose/issues/12673


Patch:
diff --git a/cmd/compose/build.go b/cmd/compose/build.go
index 951449214..bd3f10b00 100644
--- a/cmd/compose/build.go
+++ b/cmd/compose/build.go
@@ -27,6 +27,7 @@ import (
 	"github.com/docker/cli/cli/command"
 	cliopts "github.com/docker/cli/opts"
 	ui "github.com/docker/compose/v2/pkg/progress"
+	"github.com/docker/compose/v2/pkg/utils"
 	buildkit "github.com/moby/buildkit/util/progress/progressui"
 	"github.com/spf13/cobra"
 
@@ -145,6 +146,8 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
 		return err
 	}
 
+	services = addBuildDependencies(services, project)
+
 	if err := applyPlatforms(project, false); err != nil {
 		return err
 	}
@@ -156,3 +159,21 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
 
 	return backend.Build(ctx, project, apiBuildOptions)
 }
+
+func addBuildDependencies(services []string, project *types.Project) []string {
+	servicesWithDependencies := utils.NewSet(services...)
+	for _, service := range services {
+		build := project.Services[service].Build
+		if build != nil {
+			for _, target := range build.AdditionalContexts {
+				if s, found := strings.CutPrefix(target, types.ServicePrefix); found {
+					servicesWithDependencies.Add(s)
+				}
+			}
+		}
+	}
+	if len(servicesWithDependencies) > len(services) {
+		return addBuildDependencies(servicesWithDependencies.Elements(), project)
+	}
+	return servicesWithDependencies.Elements()
+}


Test patch:
diff --git a/cmd/compose/build_test.go b/cmd/compose/build_test.go
new file mode 100644
index 000000000..fa0a9e2c4
--- /dev/null
+++ b/cmd/compose/build_test.go
@@ -0,0 +1,57 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"slices"
+	"testing"
+
+	"github.com/compose-spec/compose-go/v2/types"
+	"gotest.tools/v3/assert"
+)
+
+func Test_addBuildDependencies(t *testing.T) {
+	project := &types.Project{Services: types.Services{
+		"test": types.ServiceConfig{
+			Build: &types.BuildConfig{
+				AdditionalContexts: map[string]string{
+					"foo": "service:foo",
+					"bar": "service:bar",
+				},
+			},
+		},
+		"foo": types.ServiceConfig{
+			Build: &types.BuildConfig{
+				AdditionalContexts: map[string]string{
+					"zot": "service:zot",
+				},
+			},
+		},
+		"bar": types.ServiceConfig{
+			Build: &types.BuildConfig{},
+		},
+		"zot": types.ServiceConfig{
+			Build: &types.BuildConfig{},
+		},
+	}}
+
+	services := addBuildDependencies([]string{"test"}, project)
+	expected := []string{"test", "foo", "bar", "zot"}
+	slices.Sort(services)
+	slices.Sort(expected)
+	assert.DeepEqual(t, services, expected)
+}