From fc8c56b4077a9c9a429a28bcd0a6b866d7f17c55 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 28 Apr 2025 08:59:07 +0200 Subject: [PATCH] select services implicitly declared by a service:xx build dependency Signed-off-by: Nicolas De Loof --- cmd/compose/build.go | 23 +---------------------- pkg/compose/build.go | 29 ++++++++++++++++++++++++++++- {cmd => pkg}/compose/build_test.go | 0 pkg/e2e/build_test.go | 20 ++++++++++++-------- 4 files changed, 41 insertions(+), 31 deletions(-) rename {cmd => pkg}/compose/build_test.go (100%) diff --git a/cmd/compose/build.go b/cmd/compose/build.go index bd3f10b00..58636e0dc 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -27,7 +27,6 @@ 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" @@ -141,13 +140,11 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) } func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error { - project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution) + project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution) if err != nil { return err } - services = addBuildDependencies(services, project) - if err := applyPlatforms(project, false); err != nil { return err } @@ -159,21 +156,3 @@ 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() -} diff --git a/pkg/compose/build.go b/pkg/compose/build.go index a32fe6528..d7c14cfa9 100644 --- a/pkg/compose/build.go +++ b/pkg/compose/build.go @@ -85,7 +85,16 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti policy = types.IncludeDependencies } - err := project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error { + if len(options.Services) > 0 { + // As user requested some services to be built, also include those used as additional_contexts + options.Services = addBuildDependencies(options.Services, project) + } + project, err := project.WithSelectedServices(options.Services) + if err != nil { + return nil, err + } + + err = project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error { if service.Build == nil { return nil } @@ -613,3 +622,21 @@ func parsePlatforms(service types.ServiceConfig) ([]specs.Platform, error) { return ret, nil } + +func addBuildDependencies(services []string, project *types.Project) []string { + servicesWithDependencies := utils.NewSet(services...) + for _, service := range services { + b := project.Services[service].Build + if b != nil { + for _, target := range b.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() +} diff --git a/cmd/compose/build_test.go b/pkg/compose/build_test.go similarity index 100% rename from cmd/compose/build_test.go rename to pkg/compose/build_test.go diff --git a/pkg/e2e/build_test.go b/pkg/e2e/build_test.go index 9231ab1ec..1b31f6fbe 100644 --- a/pkg/e2e/build_test.go +++ b/pkg/e2e/build_test.go @@ -117,14 +117,14 @@ func TestLocalComposeBuild(t *testing.T) { }) t.Run(env+" rebuild when up --build", func(t *testing.T) { - res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "up", "-d", "--build") + res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "up", "-d", "--build") res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"}) res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"}) }) t.Run(env+" build --push ignored for unnamed images", func(t *testing.T) { - res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "build", "--push", "nginx") + res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "build", "--push", "nginx") assert.Assert(t, !strings.Contains(res.Stdout(), "failed to push"), res.Stdout()) }) @@ -232,7 +232,7 @@ func TestBuildTags(t *testing.T) { } func TestBuildImageDependencies(t *testing.T) { - doTest := func(t *testing.T, cli *CLI) { + doTest := func(t *testing.T, cli *CLI, args ...string) { resetState := func() { cli.RunDockerComposeCmd(t, "down", "--rmi=all", "-t=0") res := cli.RunDockerOrExitError(t, "image", "rm", "build-dependencies-service") @@ -250,7 +250,7 @@ func TestBuildImageDependencies(t *testing.T) { Err: "No such image: build-dependencies-service", }) - res = cli.RunDockerComposeCmd(t, "build") + res = cli.RunDockerComposeCmd(t, args...) t.Log(res.Combined()) res = cli.RunDockerCmd(t, @@ -273,7 +273,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=0", "COMPOSE_FILE=./fixtures/build-dependencies/classic.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "--with-dependencies", "service") }) t.Run("BuildKit by dependency order", func(t *testing.T) { @@ -281,7 +282,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_FILE=./fixtures/build-dependencies/classic.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "--with-dependencies", "service") }) t.Run("BuildKit by additional contexts", func(t *testing.T) { @@ -289,7 +291,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "service") }) t.Run("Bake by additional contexts", func(t *testing.T) { @@ -297,7 +300,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_BAKE=1", "COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "service") }) }