Repository navigation
.dockerignore patterns with a leading slash (e.g. /foo/a.py) are not applied, so matching files are still added to the build context #2700
Description
Location (docker-java/docker-java @ default branch 002b6eb):
docker-java/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java:98
pattern = FilenameUtils.normalize(pattern)FilenameUtils.normalize(pattern) keeps the leading /, and getIgnores adds the pattern unchanged.
Problem
When a .dockerignore entry starts with / (e.g. /foo/a.py), the leading slash is preserved after FilenameUtils.normalize at Dockerfile.java:98 and passed on to GoLangFileMatch.match. The resulting regex is anchored on a literal / at the start, which never matches the relative file paths of the build context. As a result the pattern has no effect and the file remains in filesToAdd, even though the Docker documentation states that /foo/bar and foo/bar should exclude the same file.
Reproduction
Test (from the applied patch):
package com.github.dockerjava.core.dockerfile;
import com.google.common.base.Function;
import org.junit.Test;
import java.io.File;
import java.util.Collection;
import static com.google.common.collect.Collections2.transform;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
public class DockerfileLeadingSlashIgnoreTest {
private static final Function<File, String> TO_FILE_NAMES = File::getName;
@Test
public void leadingSlashPatternShouldExcludeFile() throws Exception {
File baseDir = fileFromBuildTestResource("dockerignore/LeadingSlashPattern");
Dockerfile dockerfile = new Dockerfile(new File(baseDir, "Dockerfile"), baseDir);
Dockerfile.ScannedResult result = dockerfile.parse();
Collection<String> filesToAdd = transform(result.filesToAdd, TO_FILE_NAMES);
assertThat(filesToAdd,
containsInAnyOrder("Dockerfile", ".dockerignore", "b.py"));
}
private File fileFromBuildTestResource(String resource) {
return new File(Thread.currentThread().getContextClassLoader()
.getResource("buildTests/" + resource).getFile());
}
}Build context resources:
.dockerignore→/foo/a.pyDockerfile→FROM alpine:3.1foo/a.py,foo/b.py
Failing assertion on HEAD:
[ERROR] com.github.dockerjava.core.dockerfile.DockerfileLeadingSlashIgnoreTest.leadingSlashPatternShouldExcludeFile Time elapsed: 0.003 s <<< FAILURE!
java.lang.AssertionError:
Expected: iterable with items ["Dockerfile", ".dockerignore", "b.py"] in any order
The assertion fails because a.py is not excluded and remains in filesToAdd — the exact symptom described above.
Environment:
- Commit:
002b6eb7aa2b892be729c7c2a5e95f782924db38(default branch, 2026-06-22) - Toolchain:
openjdk version "21.0.2" 2024-01-16,Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
The control run without the test patch exits 0 on the same suite.
Already fixed in docker/docker-py
PR: docker/docker-py#1709 — "Leading slash in .dockerignore should be ignored" (merged 2017-08-15T22:40:17Z, merge commit 92a2e48e174). It strips any leading / from each .dockerignore pattern before the patterns are used for exclusion, so /foo/a.py and foo/a.py behave identically.
How docker-py fixed it:
--- a/docker/utils/build.py
+++ b/docker/utils/build.py
@@ -26,6 +26,7 @@ def exclude_paths(root, patterns, dockerfile=None):
if dockerfile is None:
dockerfile = 'Dockerfile'
+ patterns = [p.lstrip('/') for p in patterns]
exceptions = [p for p in patterns if p.startswith('!')]
include_patterns = [p[1:] for p in exceptions]--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -768,6 +768,11 @@ def test_single_subdir_single_filename(self):
self.all_paths - set(['foo/a.py'])
)
+ def test_single_subdir_single_filename_leading_slash(self):
+ assert self.exclude(['/foo/a.py']) == convert_paths(
+ self.all_paths - set(['foo/a.py'])
+ )
+
def test_single_subdir_with_path_traversal(self):
assert self.exclude(['foo/whoops/../a.py']) == convert_paths(
self.all_paths - set(['foo/a.py'])Suggested fix
Strip leading slashes from each .dockerignore pattern before matching, as docker-py does:
diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java b/docker-java-core/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java
--- a/docker-java-core/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java
+++ b/docker-java-core/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java
@@ -91,7 +91,8 @@
List<String> dockerIgnoreFileContent = FileUtils.readLines(dockerIgnoreFile);
for (String pattern : dockerIgnoreFileContent) {
lineNumber++;
- pattern = pattern.trim();
+ // a leading slash refers to the build context root, same as no slash
+ pattern = pattern.trim().replaceFirst("^/+", "");
if (pattern.isEmpty()) {
continue; // skip empty lines
}Verified on 002b6eb (OpenJDK 21.0.2, Maven 3.9.6): the test above fails without this patch and passes with it; no other test results change.
Activity