diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 1da0b31c55..dce178a71b 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -8,7 +8,6 @@ import ( "io" "io/ioutil" "os" - "path/filepath" "regexp" "runtime" "time" @@ -20,10 +19,8 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/builder/dockerignore" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/streamformatter" @@ -208,22 +205,13 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { } if buildCtx == nil { - f, err := os.Open(filepath.Join(contextDir, ".dockerignore")) - if err != nil && !os.IsNotExist(err) { + excludes, err := build.ReadDockerignore(contextDir) + if err != nil { return err } - defer f.Close() - - var excludes []string - if err == nil { - excludes, err = dockerignore.ReadAll(f) - if err != nil { - return err - } - } if err := build.ValidateContextDirectory(contextDir, excludes); err != nil { - return errors.Errorf("Error checking context: '%s'.", err) + return errors.Errorf("error checking context: '%s'.", err) } // And canonicalize dockerfile name to a platform-independent one @@ -232,20 +220,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { return errors.Errorf("cannot canonicalize dockerfile path %s: %v", relDockerfile, err) } - // If .dockerignore mentions .dockerignore or the Dockerfile then make - // sure we send both files over to the daemon because Dockerfile is, - // obviously, needed no matter what, and .dockerignore is needed to know - // if either one needs to be removed. The daemon will remove them - // if necessary, after it parses the Dockerfile. Ignore errors here, as - // they will have been caught by validateContextDirectory above. - // Excludes are used instead of includes to maintain the order of files - // in the archive. - if keep, _ := fileutils.Matches(".dockerignore", excludes); keep { - excludes = append(excludes, "!.dockerignore") - } - if keep, _ := fileutils.Matches(relDockerfile, excludes); keep && !options.dockerfileFromStdin() { - excludes = append(excludes, "!"+relDockerfile) - } + excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, options.dockerfileFromStdin()) compression := archive.Uncompressed if options.compress { diff --git a/cli/command/image/build/context.go b/cli/command/image/build/context.go index af1892b96c..83a90c9f15 100644 --- a/cli/command/image/build/context.go +++ b/cli/command/image/build/context.go @@ -143,7 +143,7 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error) return "", "", errors.Wrapf(err, "unable to 'git clone' to temporary context directory") } - absContextDir, err = resolveAndValidateContextPath(absContextDir) + absContextDir, err = ResolveAndValidateContextPath(absContextDir) if err != nil { return "", "", err } @@ -173,7 +173,7 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read // the relative path of the dockerfile in that context directory, and a non-nil // error on success. func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, error) { - localDir, err := resolveAndValidateContextPath(localDir) + localDir, err := ResolveAndValidateContextPath(localDir) if err != nil { return "", "", err } @@ -191,9 +191,9 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, er return localDir, relDockerfile, err } -// resolveAndValidateContextPath uses the given context directory for a `docker build` +// ResolveAndValidateContextPath uses the given context directory for a `docker build` // and returns the absolute path to the context directory. -func resolveAndValidateContextPath(givenContextDir string) (string, error) { +func ResolveAndValidateContextPath(givenContextDir string) (string, error) { absContextDir, err := filepath.Abs(givenContextDir) if err != nil { return "", errors.Errorf("unable to get absolute context directory of given context directory %q: %v", givenContextDir, err) diff --git a/cli/command/image/build/dockerignore.go b/cli/command/image/build/dockerignore.go new file mode 100644 index 0000000000..497c3f24f3 --- /dev/null +++ b/cli/command/image/build/dockerignore.go @@ -0,0 +1,39 @@ +package build + +import ( + "os" + "path/filepath" + + "github.com/docker/docker/builder/dockerignore" + "github.com/docker/docker/pkg/fileutils" +) + +// ReadDockerignore reads the .dockerignore file in the context directory and +// returns the list of paths to exclude +func ReadDockerignore(contextDir string) ([]string, error) { + var excludes []string + + f, err := os.Open(filepath.Join(contextDir, ".dockerignore")) + switch { + case os.IsNotExist(err): + return excludes, nil + case err != nil: + return nil, err + } + defer f.Close() + + return dockerignore.ReadAll(f) +} + +// TrimBuildFilesFromExcludes removes the named Dockerfile and .dockerignore from +// the list of excluded files. The daemon will remove them from the final context +// but they must be in available in the context when passed to the API. +func TrimBuildFilesFromExcludes(excludes []string, dockerfile string, dockerfileFromStdin bool) []string { + if keep, _ := fileutils.Matches(".dockerignore", excludes); keep { + excludes = append(excludes, "!.dockerignore") + } + if keep, _ := fileutils.Matches(dockerfile, excludes); keep && !dockerfileFromStdin { + excludes = append(excludes, "!"+dockerfile) + } + return excludes +}