diff --git a/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java b/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java index 469beb40b..3e2d57485 100644 --- a/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java +++ b/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java @@ -4,6 +4,7 @@ package com.github.dockerjava.core; import java.io.File; +import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; @@ -23,16 +24,16 @@ * character class (must be non-empty) * c matches character c (c != '*', '?', '\\', '[') * '\\' c matches character c - * + * * character-range: * c matches character c (c != '\\', '-', ']') * '\\' c matches character c * lo '-' hi matches character c for lo <= c <= hi - * + * * Match requires pattern to match all of name, not just a substring. * The only possible returned error is ErrBadPattern, when pattern * is malformed. - * + * * On Windows, escaping is disabled. Instead, '\\' is treated as * path separator. * @@ -45,20 +46,24 @@ public class GoLangFileMatch { public static final boolean IS_WINDOWS = File.separatorChar == '\\'; public static boolean match(List patterns, File file) { - return match(patterns, file.getPath()); + return !match(patterns, file.getPath()).isEmpty(); } public static boolean match(String pattern, File file) { return match(pattern, file.getPath()); } - public static boolean match(List patterns, String name) { + /** + * Returns the matching patterns for the given string + */ + public static List match(List patterns, String name) { + List matches = new ArrayList(); for (String pattern : patterns) { if (match(pattern, name)) { - return true; + matches.add(pattern); } } - return false; + return matches; } public static boolean match(String pattern, String name) { diff --git a/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java b/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java index c8d8e1044..371c30fed 100644 --- a/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java +++ b/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java @@ -24,8 +24,7 @@ public GoLangMatchFileFilter(File base, List patterns) { public boolean accept(File file) { String relativePath = FilePathUtil.relativize(base, file); - boolean match = GoLangFileMatch.match(patterns, relativePath); - return !match; + return GoLangFileMatch.match(patterns, relativePath).isEmpty(); } } diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java index 9513678d7..2e8ee8397 100644 --- a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java +++ b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java @@ -10,6 +10,7 @@ import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.Collections2; + import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; @@ -89,12 +90,6 @@ public List getIgnores() throws IOException { } pattern = FilenameUtils.normalize(pattern); try { - // validate pattern and make sure we aren't excluding Dockerfile - if (GoLangFileMatch.match(pattern, "Dockerfile")) { - throw new DockerClientException(String.format( - "Dockerfile is excluded by pattern '%s' on line %s in .dockerignore file", pattern, - lineNumber)); - } ignores.add(pattern); } catch (GoLangFileMatchException e) { throw new DockerClientException(String.format( @@ -169,7 +164,15 @@ public String toString() { public ScannedResult() throws IOException { ignores = getIgnores(); - filesToAdd.add(dockerFile); + + String matchingIgnorePattern = effectiveMatchingIgnorePattern(dockerFile); + + if (matchingIgnorePattern == null) { + filesToAdd.add(dockerFile); + } else { + throw new DockerClientException(String.format( + "Dockerfile is excluded by pattern '%s' in .dockerignore file", matchingIgnorePattern)); + } for (DockerfileStatement statement : getStatements()) { if (statement instanceof DockerfileStatement.Env) { @@ -180,6 +183,56 @@ public ScannedResult() throws IOException { } } + /** + * Returns all matching ignore patterns for the given file name. + */ + private List matchingIgnorePatterns(String fileName) { + List matches = new ArrayList(); + + int lineNumber = 0; + for (String pattern : ignores) { + lineNumber++; + try { + if (GoLangFileMatch.match(pattern, fileName)) { + matches.add(pattern); + } + } catch (GoLangFileMatchException e) { + throw new DockerClientException(String.format( + "Invalid pattern '%s' on line %s in .dockerignore file", pattern, lineNumber)); + } + } + + return matches; + } + + /** + * Returns the matching ignore pattern for the given file or null if it should NOT be ignored. + * Exception rules like "!Dockerfile" will be respected. + */ + private String effectiveMatchingIgnorePattern(File file) { + String relativeFilename = FilePathUtil.relativize(getDockerFolder(), file); + + List matchingPattern = matchingIgnorePatterns(relativeFilename); + + if (matchingPattern.isEmpty()) + return null; + + String lastMatchingPattern = matchingPattern.get(matchingPattern.size() - 1); + + int lastMatchingPatternIndex = ignores.lastIndexOf(lastMatchingPattern); + + if(lastMatchingPatternIndex == ignores.size() - 1) return lastMatchingPattern; + + List remainingIgnorePattern = ignores.subList(lastMatchingPatternIndex + 1, ignores.size()); + + for (String ignorePattern : remainingIgnorePattern) { + if (ignorePattern.equals("!" + relativeFilename)) + return null; + } + + return lastMatchingPattern; + } + private void processAddStatement(DockerfileStatement.Add add) throws IOException { add = add.transform(environmentMap); @@ -202,7 +255,7 @@ private void processAddStatement(DockerfileStatement.Add add) throws IOException Collection files = FileUtils.listFiles(src, new GoLangMatchFileFilter(src, ignores), TrueFileFilter.INSTANCE); filesToAdd.addAll(files); - } else if (!GoLangFileMatch.match(ignores, FilePathUtil.relativize(dockerFolder, src))) { + } else if (effectiveMatchingIgnorePattern(src) == null) { filesToAdd.add(src); } else { throw new DockerClientException(String.format( @@ -234,10 +287,7 @@ private Collection resolveWildcards(File file, List ignores) { } private void processEnvStatement(DockerfileStatement.Env env) { - environmentMap.put(env.variable, env.value); } - } - } diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java b/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java index b37f5b4f7..2c06312a1 100644 --- a/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java +++ b/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java @@ -19,7 +19,7 @@ /** * A statement present in a dockerfile. */ -public abstract class DockerfileStatement { +public abstract class DockerfileStatement> { private DockerfileStatement() { } diff --git a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java index 2fb1c545b..22030cc9f 100644 --- a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java @@ -171,6 +171,14 @@ public void testDockerfileIgnored() throws Exception { dockerClient.buildImageCmd(baseDir).withNoCache().exec(new BuildImageResultCallback()).awaitImageId(); } + @Test + public void testDockerfileNotIgnored() throws Exception { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileNotIgnored") + .getFile()); + + dockerClient.buildImageCmd(baseDir).withNoCache().exec(new BuildImageResultCallback()).awaitImageId(); + } + @Test(expectedExceptions = { DockerClientException.class }) public void testInvalidDockerIgnorePattern() throws Exception { File baseDir = new File(Thread.currentThread().getContextClassLoader() diff --git a/src/test/resources/testDockerfileNotIgnored/.dockerignore b/src/test/resources/testDockerfileNotIgnored/.dockerignore new file mode 100644 index 000000000..5c10f6256 --- /dev/null +++ b/src/test/resources/testDockerfileNotIgnored/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile \ No newline at end of file diff --git a/src/test/resources/testDockerfileNotIgnored/Dockerfile b/src/test/resources/testDockerfileNotIgnored/Dockerfile new file mode 100644 index 000000000..6a8106b86 --- /dev/null +++ b/src/test/resources/testDockerfileNotIgnored/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:latest + +CMD ["echo", "Success"]