cli/command/image: move build-context detection to build

Removes direct imports of github.com/docker/docker/builder in
the image package, to be moved later.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2025-07-16 15:12:37 +02:00
parent d8089e7d1b
commit 260f1dbebb
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
2 changed files with 71 additions and 36 deletions

View File

@ -28,7 +28,6 @@ import (
buildtypes "github.com/docker/docker/api/types/build" buildtypes "github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
registrytypes "github.com/docker/docker/api/types/registry" registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/streamformatter"
"github.com/moby/go-archive" "github.com/moby/go-archive"
@ -76,12 +75,6 @@ func (o buildOptions) dockerfileFromStdin() bool {
return o.dockerfileName == "-" return o.dockerfileName == "-"
} }
// contextFromStdin returns true when the user specified that the build context
// should be read from stdin
func (o buildOptions) contextFromStdin() bool {
return o.context == "-"
}
func newBuildOptions() buildOptions { func newBuildOptions() buildOptions {
ulimits := make(map[string]*container.Ulimit) ulimits := make(map[string]*container.Ulimit)
return buildOptions{ return buildOptions{
@ -189,21 +182,24 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
buildCtx io.ReadCloser buildCtx io.ReadCloser
dockerfileCtx io.ReadCloser dockerfileCtx io.ReadCloser
contextDir string contextDir string
tempDir string
relDockerfile string relDockerfile string
progBuff io.Writer progBuff io.Writer
buildBuff io.Writer buildBuff io.Writer
remote string remote string
) )
contextType, err := build.DetectContextType(options.context)
if err != nil {
return err
}
if options.dockerfileFromStdin() { if options.dockerfileFromStdin() {
if options.contextFromStdin() { if contextType == build.ContextTypeStdin {
return errors.New("invalid argument: can't use stdin for both build context and dockerfile") return errors.New("invalid argument: can't use stdin for both build context and dockerfile")
} }
dockerfileCtx = dockerCli.In() dockerfileCtx = dockerCli.In()
} }
specifiedContext := options.context
progBuff = dockerCli.Out() progBuff = dockerCli.Out()
buildBuff = dockerCli.Out() buildBuff = dockerCli.Out()
if options.quiet { if options.quiet {
@ -217,13 +213,19 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
} }
} }
switch { switch contextType {
case options.contextFromStdin(): case build.ContextTypeStdin:
// buildCtx is tar archive. if stdin was dockerfile then it is wrapped // buildCtx is tar archive. if stdin was dockerfile then it is wrapped
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName) buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
case isLocalDir(specifiedContext): if err != nil {
contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, options.dockerfileName) return fmt.Errorf("unable to prepare context from STDIN: %w", err)
if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) { }
case build.ContextTypeLocal:
contextDir, relDockerfile, err = build.GetContextFromLocalDir(options.context, options.dockerfileName)
if err != nil {
return errors.Errorf("unable to prepare context: %s", err)
}
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
// Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx // Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx
dockerfileCtx, err = os.Open(options.dockerfileName) dockerfileCtx, err = os.Open(options.dockerfileName)
if err != nil { if err != nil {
@ -231,24 +233,23 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
} }
defer dockerfileCtx.Close() defer dockerfileCtx.Close()
} }
case urlutil.IsGitURL(specifiedContext): case build.ContextTypeGit:
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, options.dockerfileName) var tempDir string
case urlutil.IsURL(specifiedContext): tempDir, relDockerfile, err = build.GetContextFromGitURL(options.context, options.dockerfileName)
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, specifiedContext, options.dockerfileName)
default:
return errors.Errorf("unable to prepare context: path %q not found", specifiedContext)
}
if err != nil { if err != nil {
if options.quiet && urlutil.IsURL(specifiedContext) {
_, _ = fmt.Fprintln(dockerCli.Err(), progBuff)
}
return errors.Errorf("unable to prepare context: %s", err) return errors.Errorf("unable to prepare context: %s", err)
} }
defer func() {
if tempDir != "" { _ = os.RemoveAll(tempDir)
defer os.RemoveAll(tempDir) }()
contextDir = tempDir contextDir = tempDir
case build.ContextTypeRemote:
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, options.context, options.dockerfileName)
if err != nil && options.quiet {
_, _ = fmt.Fprintln(dockerCli.Err(), progBuff)
}
default:
return errors.Errorf("unable to prepare context: path %q not found", options.context)
} }
// read from a directory into tar archive // read from a directory into tar archive
@ -415,11 +416,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
return nil return nil
} }
func isLocalDir(c string) bool {
_, err := os.Stat(c)
return err == nil
}
type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error) type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
// validateTag checks if the given image name can be resolved. // validateTag checks if the given image name can be resolved.

View File

@ -0,0 +1,39 @@
package build
import (
"fmt"
"os"
"github.com/docker/docker/builder/remotecontext/urlutil"
)
// ContextType describes the type (source) of build-context specified.
type ContextType string
const (
ContextTypeStdin ContextType = "stdin" // ContextTypeStdin indicates that the build-context is a TAR archive passed through STDIN.
ContextTypeLocal ContextType = "local" // ContextTypeLocal indicates that the build-context is a local directory.
ContextTypeRemote ContextType = "remote" // ContextTypeRemote indicates that the build-context is a remote URL.
ContextTypeGit ContextType = "git" // ContextTypeGit indicates that the build-context is a GIT URL.
)
// DetectContextType detects the type (source) of the build-context.
func DetectContextType(specifiedContext string) (ContextType, error) {
switch {
case specifiedContext == "-":
return ContextTypeStdin, nil
case isLocalDir(specifiedContext):
return ContextTypeLocal, nil
case urlutil.IsGitURL(specifiedContext):
return ContextTypeGit, nil
case urlutil.IsURL(specifiedContext):
return ContextTypeRemote, nil
default:
return "", fmt.Errorf("unable to prepare context: path %q not found", specifiedContext)
}
}
func isLocalDir(c string) bool {
_, err := os.Stat(c)
return err == nil
}