pkg/compose: align classic builder implementation with docker/cli

This aligns the implementation closer to the implementation in docker/cli,
with the refactor done in [cli@260f1db]; this removes some direct uses of
the github.com/docker/docker/builder/remotecontext/urlutil package, which
won't be included in the new Moby modules.

There's still some remaining uses in the `dockerFilePath` utility (which
may need to be updated to also account for remote contexts that are not
"git"), so possibly we can remove the use in that utility as well.

[cli@260f1db]: https://github.com/docker/cli/commit/260f1dbebb9f3d70d2bdda67e511a6bee9fe365b

Signed-off-by: Sebastiaan van Stijn <[email protected]>
This commit is contained in:
Sebastiaan van Stijn
2025-10-13 16:53:00 +02:00
committed by Nicolas De loof
parent a03f2562df
commit 63920c4cc0
2 changed files with 42 additions and 38 deletions
+2 -2
View File
@@ -37,10 +37,10 @@ import (
"github.com/containerd/errdefs"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/image/build"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/moby/buildkit/client"
gitutil "github.com/moby/buildkit/frontend/dockerfile/dfgitutil"
"github.com/moby/buildkit/util/progress/progressui"
@@ -505,7 +505,7 @@ func dockerFilePath(ctxName string, dockerfile string) string {
if dockerfile == "" {
return ""
}
if urlutil.IsGitURL(ctxName) {
if contextType, _ := build.DetectContextType(ctxName); contextType == build.ContextTypeGit {
return dockerfile
}
if !filepath.IsAbs(dockerfile) {
+40 -36
View File
@@ -34,7 +34,6 @@ import (
buildtypes "github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
@@ -48,17 +47,9 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
buildCtx io.ReadCloser
dockerfileCtx io.ReadCloser
contextDir string
tempDir string
relDockerfile string
err error
)
dockerfileName := dockerFilePath(service.Build.Context, service.Build.Dockerfile)
specifiedContext := service.Build.Context
progBuff := s.stdout()
buildBuff := s.stdout()
if len(service.Build.Platforms) > 1 {
return "", fmt.Errorf("the classic builder doesn't support multi-arch build, set DOCKER_BUILDKIT=1 to use BuildKit")
}
@@ -80,34 +71,51 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
}
service.Build.Labels[api.ImageBuilderLabel] = "classic"
switch {
case isLocalDir(specifiedContext):
dockerfileName := dockerFilePath(service.Build.Context, service.Build.Dockerfile)
specifiedContext := service.Build.Context
progBuff := s.stdout()
buildBuff := s.stdout()
contextType, err := build.DetectContextType(specifiedContext)
if err != nil {
return "", err
}
switch contextType {
case build.ContextTypeStdin:
return "", fmt.Errorf("building from STDIN is not supported")
case build.ContextTypeLocal:
contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, dockerfileName)
if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
// Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx
if err != nil {
return "", fmt.Errorf("unable to prepare context: %w", err)
}
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
// Dockerfile is outside build-context; read the Dockerfile and pass it as dockerfileCtx
dockerfileCtx, err = os.Open(dockerfileName)
if err != nil {
return "", fmt.Errorf("unable to open Dockerfile: %w", err)
}
defer dockerfileCtx.Close() //nolint:errcheck
}
case urlutil.IsGitURL(specifiedContext):
case build.ContextTypeGit:
var tempDir string
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, dockerfileName)
case urlutil.IsURL(specifiedContext):
if err != nil {
return "", fmt.Errorf("unable to prepare context: %w", err)
}
defer func() {
_ = os.RemoveAll(tempDir)
}()
contextDir = tempDir
case build.ContextTypeRemote:
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, specifiedContext, dockerfileName)
if err != nil {
return "", fmt.Errorf("unable to prepare context: %w", err)
}
default:
return "", fmt.Errorf("unable to prepare context: path %q not found", specifiedContext)
}
if err != nil {
return "", fmt.Errorf("unable to prepare context: %w", err)
}
if tempDir != "" {
defer os.RemoveAll(tempDir) //nolint:errcheck
contextDir = tempDir
}
// read from a directory into tar archive
if buildCtx == nil {
excludes, err := build.ReadDockerignore(contextDir)
@@ -125,7 +133,7 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, false)
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
ExcludePatterns: excludes,
ChownOpts: &archive.ChownOpts{},
ChownOpts: &archive.ChownOpts{UID: 0, GID: 0},
})
if err != nil {
return "", err
@@ -145,6 +153,7 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
return "", err
}
// Setup an upload progress bar
progressOutput := streamformatter.NewProgressOutput(progBuff)
body := progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
@@ -166,16 +175,16 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
RegistryToken: authConfig.RegistryToken,
}
}
buildOptions := imageBuildOptions(s.dockerCli, project, service, options)
buildOpts := imageBuildOptions(s.dockerCli, project, service, options)
imageName := api.GetImageNameOrDefault(service, project.Name)
buildOptions.Tags = append(buildOptions.Tags, imageName)
buildOptions.Dockerfile = relDockerfile
buildOptions.AuthConfigs = authConfigs
buildOptions.Memory = options.Memory
buildOpts.Tags = append(buildOpts.Tags, imageName)
buildOpts.Dockerfile = relDockerfile
buildOpts.AuthConfigs = authConfigs
buildOpts.Memory = options.Memory
ctx, cancel := context.WithCancel(ctx)
defer cancel()
response, err := s.apiClient().ImageBuild(ctx, body, buildOptions)
response, err := s.apiClient().ImageBuild(ctx, body, buildOpts)
if err != nil {
return "", err
}
@@ -206,11 +215,6 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
return imageID, nil
}
func isLocalDir(c string) bool {
_, err := os.Stat(c)
return err == nil
}
func imageBuildOptions(dockerCli command.Cli, project *types.Project, service types.ServiceConfig, options api.BuildOptions) buildtypes.ImageBuildOptions {
config := service.Build
return buildtypes.ImageBuildOptions{