1 Commits
Author SHA1 Message Date
clawbot 6da05c5d6c Leave out of the build context what the app's .dockerignore names (closes #274)
Check / check (pull_request) Skipped
Docker does not apply .dockerignore to a build context sent as a tar,
so upaas sent every file in the clone. The build now reads the ignore
file as docker build does, <Dockerfile>.dockerignore next to the
Dockerfile if there is one, otherwise .dockerignore at the root, with
the ignorefile reader of moby/patternmatcher, and passes its patterns
as exclude patterns. As the docker command line does, the Dockerfile
and .dockerignore are never left out. The Dockerfile path is read as
Docker reads it, as a path inside the clone. The ignore file is read
through os.Root, so it cannot be read from outside the clone. An ignore
file that cannot be read, or a malformed pattern, fails the build.

Model: opus-5-5
2026-10-03 00:49:57 +00:00
2 changed files with 38 additions and 0 deletions
+6
View File
@@ -7,6 +7,7 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/archive"
"github.com/moby/patternmatcher"
@@ -41,6 +42,11 @@ func tarBuildContext(contextDir, dockerfile string) (io.ReadCloser, error) {
// Dockerfile if there is one, otherwise from .dockerignore at the root of the
// context. Without either file there are no patterns.
func readDockerignore(contextDir, dockerfile string) ([]string, error) {
// Docker reads the Dockerfile path as a path inside the build context:
// cleaned, with a leading / and any .. that would lead out of the context
// dropped, so ./Dockerfile and /Dockerfile both name the Dockerfile at the
// root.
dockerfile = strings.TrimPrefix(filepath.Join("/", dockerfile), "/")
if dockerfile == "" {
dockerfile = defaultDockerfileName
}
+32
View File
@@ -105,6 +105,38 @@ func TestPerformBuildFollowsDockerignore(t *testing.T) {
}
}
// TestPerformBuildReadsDockerfilePathInsideContext checks that ./Dockerfile
// and /Dockerfile name the Dockerfile at the root of the context, as Docker
// reads them, so an ignore file that names the Dockerfile does not leave it
// out.
func TestPerformBuildReadsDockerfilePathInsideContext(t *testing.T) {
t.Parallel()
for _, dockerfile := range []string{"./Dockerfile", "/Dockerfile"} {
t.Run(dockerfile, func(t *testing.T) {
t.Parallel()
contextDir := t.TempDir()
writeFiles(t, contextDir, map[string]string{
defaultDockerignoreName: "Dockerfile\nsecret.txt\n",
defaultDockerfileName: "",
testMainGo: "",
testSecretFile: "",
})
got, err := buildContextFiles(t, contextDir, dockerfile)
if err != nil {
t.Fatal(err)
}
want := []string{defaultDockerignoreName, defaultDockerfileName, testMainGo}
if !slices.Equal(got, want) {
t.Errorf("build context holds %q, want %q", got, want)
}
})
}
}
// TestPerformBuildFailsOnMalformedDockerignore checks that a pattern the
// docker command line would reject fails the build.
func TestPerformBuildFailsOnMalformedDockerignore(t *testing.T) {