Browse code

Add .dockerignore support

Fixes #2224

Docker-DCO-1.1-Signed-off-by: Travis Cline <travis.cline@gmail.com> (github: tmc)

Travis Cline authored on 2014/02/16 03:38:48
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+bundles
1
+.gopath
... ...
@@ -13,6 +13,7 @@ import (
13 13
 	"os"
14 14
 	"os/exec"
15 15
 	"path"
16
+	"path/filepath"
16 17
 	"runtime"
17 18
 	"strconv"
18 19
 	"strings"
... ...
@@ -163,7 +164,24 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
163 163
 		if err = utils.ValidateContextDirectory(root); err != nil {
164 164
 			return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
165 165
 		}
166
-		context, err = archive.Tar(root, archive.Uncompressed)
166
+		options := &archive.TarOptions{
167
+			Compression: archive.Uncompressed,
168
+		}
169
+		ignoreFile := path.Join(root, ".dockerignore")
170
+		if ignore, err := ioutil.ReadFile(ignoreFile); err == nil {
171
+			for _, pattern := range strings.Split(string(ignore), "\n") {
172
+				ok, err := filepath.Match(pattern, "Dockerfile")
173
+				if err != nil {
174
+					utils.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err)
175
+					continue
176
+				}
177
+				if ok {
178
+					return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern)
179
+				}
180
+				options.Excludes = append(options.Excludes, pattern)
181
+			}
182
+		}
183
+		context, err = archive.TarWithOptions(root, options)
167 184
 	}
168 185
 	var body io.Reader
169 186
 	// Setup an upload progress bar
... ...
@@ -215,6 +215,12 @@ temporary directory on your local host, and then this is sent to the
215 215
 Docker daemon as the context. This way, your local user credentials and
216 216
 vpn's etc can be used to access private repositories.
217 217
 
218
+If a file named ``.dockerignore`` exists in the root of ``PATH`` then it is
219
+interpreted as a newline-separated list of exclusion patterns. Exclusion
220
+patterns match files or directories relative to ``PATH`` that will be excluded
221
+from the context. Globbing is done using Go's
222
+[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules.
223
+
218 224
 See also:
219 225
 
220 226
 [*Dockerfile Reference*](/reference/builder/#dockerbuilder).
... ...
@@ -266,6 +272,30 @@ If you wish to keep the intermediate containers after the build is
266 266
 complete, you must use `--rm=false`. This does not
267 267
 affect the build cache.
268 268
 
269
+    $ docker build .
270
+    Uploading context 18.829 MB
271
+    Uploading context
272
+    Step 0 : FROM busybox
273
+     ---> 769b9341d937
274
+    Step 1 : CMD echo Hello World
275
+     ---> Using cache
276
+     ---> 99cc1ad10469
277
+    Successfully built 99cc1ad10469
278
+    $ echo ".git" > .dockerignore
279
+    $ docker build .
280
+    Uploading context  6.76 MB
281
+    Uploading context
282
+    Step 0 : FROM busybox
283
+     ---> 769b9341d937
284
+    Step 1 : CMD echo Hello World
285
+     ---> Using cache
286
+     ---> 99cc1ad10469
287
+    Successfully built 99cc1ad10469
288
+
289
+This example shows the use of the ``.dockerignore`` file to exclude the ``.git``
290
+directory the context. Its effect can be seen in the changed size of the
291
+uploaded context.
292
+
269 293
     $ sudo docker build -t vieux/apache:2.0 .
270 294
 
271 295
 This will build like the previous example, but it will then tag the
... ...
@@ -1514,3 +1514,53 @@ docker.com>"
1514 1514
 
1515 1515
 	logDone("build - validate escaping whitespace")
1516 1516
 }
1517
+
1518
+func TestDockerignore(t *testing.T) {
1519
+	name := "testbuilddockerignore"
1520
+	defer deleteImages(name)
1521
+	dockerfile := `
1522
+        FROM busybox
1523
+        ADD . /bla
1524
+		RUN [[ -f /bla/src/x.go ]]
1525
+		RUN [[ -f /bla/Makefile ]]
1526
+		RUN [[ ! -e /bla/src/_vendor ]]
1527
+		RUN [[ ! -e /bla/.gitignore ]]
1528
+		RUN [[ ! -e /bla/README.md ]]
1529
+		RUN [[ ! -e /bla/.git ]]`
1530
+	ctx, err := fakeContext(dockerfile, map[string]string{
1531
+		"Makefile":         "all:",
1532
+		".git/HEAD":        "ref: foo",
1533
+		"src/x.go":         "package main",
1534
+		"src/_vendor/v.go": "package main",
1535
+		".gitignore":       "",
1536
+		"README.md":        "readme",
1537
+		".dockerignore":    ".git\npkg\n.gitignore\nsrc/_vendor\n*.md",
1538
+	})
1539
+	defer ctx.Close()
1540
+	if err != nil {
1541
+		t.Fatal(err)
1542
+	}
1543
+	if _, err := buildImageFromContext(name, ctx, true); err != nil {
1544
+		t.Fatal(err)
1545
+	}
1546
+	logDone("build - test .dockerignore")
1547
+}
1548
+
1549
+func TestDockerignoringDockerfile(t *testing.T) {
1550
+	name := "testbuilddockerignoredockerfile"
1551
+	defer deleteImages(name)
1552
+	dockerfile := `
1553
+        FROM scratch`
1554
+	ctx, err := fakeContext(dockerfile, map[string]string{
1555
+		"Dockerfile":    "FROM scratch",
1556
+		".dockerignore": "Dockerfile\n",
1557
+	})
1558
+	defer ctx.Close()
1559
+	if err != nil {
1560
+		t.Fatal(err)
1561
+	}
1562
+	if _, err = buildImageFromContext(name, ctx, true); err == nil {
1563
+		t.Fatalf("Didn't get expected error from ignoring Dockerfile")
1564
+	}
1565
+	logDone("build - test .dockerignore of Dockerfile")
1566
+}