diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..37abdef44f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +bundles +.gopath diff --git a/api/client/commands.go b/api/client/commands.go index dcd882470c..c386e7a09c 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "runtime" "strconv" "strings" @@ -163,7 +164,24 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if err = utils.ValidateContextDirectory(root); err != nil { return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err) } - context, err = archive.Tar(root, archive.Uncompressed) + options := &archive.TarOptions{ + Compression: archive.Uncompressed, + } + ignoreFile := path.Join(root, ".dockerignore") + if ignore, err := ioutil.ReadFile(ignoreFile); err == nil { + for _, pattern := range strings.Split(string(ignore), "\n") { + ok, err := filepath.Match(pattern, "Dockerfile") + if err != nil { + utils.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err) + continue + } + if ok { + return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern) + } + options.Excludes = append(options.Excludes, pattern) + } + } + context, err = archive.TarWithOptions(root, options) } var body io.Reader // Setup an upload progress bar diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 600a89b68a..cb5d39d93a 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -215,6 +215,12 @@ temporary directory on your local host, and then this is sent to the Docker daemon as the context. This way, your local user credentials and vpn's etc can be used to access private repositories. +If a file named ``.dockerignore`` exists in the root of ``PATH`` then it is +interpreted as a newline-separated list of exclusion patterns. Exclusion +patterns match files or directories relative to ``PATH`` that will be excluded +from the context. Globbing is done using Go's +[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. + See also: [*Dockerfile Reference*](/reference/builder/#dockerbuilder). @@ -266,6 +272,30 @@ If you wish to keep the intermediate containers after the build is complete, you must use `--rm=false`. This does not affect the build cache. + $ docker build . + Uploading context 18.829 MB + Uploading context + Step 0 : FROM busybox + ---> 769b9341d937 + Step 1 : CMD echo Hello World + ---> Using cache + ---> 99cc1ad10469 + Successfully built 99cc1ad10469 + $ echo ".git" > .dockerignore + $ docker build . + Uploading context 6.76 MB + Uploading context + Step 0 : FROM busybox + ---> 769b9341d937 + Step 1 : CMD echo Hello World + ---> Using cache + ---> 99cc1ad10469 + Successfully built 99cc1ad10469 + +This example shows the use of the ``.dockerignore`` file to exclude the ``.git`` +directory the context. Its effect can be seen in the changed size of the +uploaded context. + $ sudo docker build -t vieux/apache:2.0 . This will build like the previous example, but it will then tag the diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 6086258d10..2f2f2b6e0c 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -1514,3 +1514,53 @@ docker.com>" logDone("build - validate escaping whitespace") } + +func TestDockerignore(t *testing.T) { + name := "testbuilddockerignore" + defer deleteImages(name) + dockerfile := ` + FROM busybox + ADD . /bla + RUN [[ -f /bla/src/x.go ]] + RUN [[ -f /bla/Makefile ]] + RUN [[ ! -e /bla/src/_vendor ]] + RUN [[ ! -e /bla/.gitignore ]] + RUN [[ ! -e /bla/README.md ]] + RUN [[ ! -e /bla/.git ]]` + ctx, err := fakeContext(dockerfile, map[string]string{ + "Makefile": "all:", + ".git/HEAD": "ref: foo", + "src/x.go": "package main", + "src/_vendor/v.go": "package main", + ".gitignore": "", + "README.md": "readme", + ".dockerignore": ".git\npkg\n.gitignore\nsrc/_vendor\n*.md", + }) + defer ctx.Close() + if err != nil { + t.Fatal(err) + } + if _, err := buildImageFromContext(name, ctx, true); err != nil { + t.Fatal(err) + } + logDone("build - test .dockerignore") +} + +func TestDockerignoringDockerfile(t *testing.T) { + name := "testbuilddockerignoredockerfile" + defer deleteImages(name) + dockerfile := ` + FROM scratch` + ctx, err := fakeContext(dockerfile, map[string]string{ + "Dockerfile": "FROM scratch", + ".dockerignore": "Dockerfile\n", + }) + defer ctx.Close() + if err != nil { + t.Fatal(err) + } + if _, err = buildImageFromContext(name, ctx, true); err == nil { + t.Fatalf("Didn't get expected error from ignoring Dockerfile") + } + logDone("build - test .dockerignore of Dockerfile") +}