From d39a81e4569945dddb3b47463fd18c34e815cd92 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 27 Feb 2026 17:06:32 -0800 Subject: [PATCH] bake: fix remote named context subdir handling Fix using CopyDirContentsOnly when projecting remote named contexts so subdir contexts keep the same root semantics as local bake runs. Signed-off-by: Tonis Tiigi --- bake/bake.go | 5 +++- tests/bake.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/bake/bake.go b/bake/bake.go index b42c52dcef71..a8c49bf2b0f3 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -1299,6 +1299,7 @@ func updateContext(t *build.Inputs, inp *Input) { for k, v := range t.NamedContexts { if v.Path == "." { t.NamedContexts[k] = build.NamedContext{Path: inp.URL} + continue } if strings.HasPrefix(v.Path, "cwd://") || strings.HasPrefix(v.Path, "target:") || strings.HasPrefix(v.Path, "docker-image:") { continue @@ -1306,7 +1307,9 @@ func updateContext(t *build.Inputs, inp *Input) { if urlutil.IsRemoteURL(v.Path) { continue } - st := llb.Scratch().File(llb.Copy(*inp.State, v.Path, "/"), llb.WithCustomNamef("set context %s to %s", k, v.Path)) + st := llb.Scratch().File(llb.Copy(*inp.State, v.Path, "/", &llb.CopyInfo{ + CopyDirContentsOnly: true, + }), llb.WithCustomNamef("set context %s to %s", k, v.Path)) t.NamedContexts[k] = build.NamedContext{State: &st, Path: inp.URL} } diff --git a/tests/bake.go b/tests/bake.go index 8de076e69cee..da3706785ab7 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -51,6 +51,8 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){ testBakeLocalCwdOverride, testBakeRemoteCmdContextOverride, testBakeRemoteContextSubdir, + testBakeRemoteNamedContextSubdir, + testBakeRemoteNamedContextDot, testBakeRemoteCmdContextEscapeRoot, testBakeRemoteCmdContextEscapeRelative, testBakeRemoteDockerfileCwd, @@ -920,6 +922,80 @@ COPY super-cool.txt / require.FileExists(t, filepath.Join(dirDest, "super-cool.txt")) } +// https://github.com/docker/buildx/issues/3670 +func testBakeRemoteNamedContextSubdir(t *testing.T, sb integration.Sandbox) { + bakefile := []byte(` +target default { + context = "./build" + dockerfile = "Dockerfile" + contexts = { + files = "./files-src/" + } +} +`) + dockerfile := []byte(` +FROM scratch +COPY --from=files file.txt /file.txt +`) + + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + fstest.CreateDir("build", 0700), + fstest.CreateFile("build/Dockerfile", dockerfile, 0600), + fstest.CreateDir("files-src", 0700), + fstest.CreateFile("files-src/file.txt", []byte("hello"), 0600), + ) + dirDest := t.TempDir() + + git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + require.NoError(t, err) + gittestutil.GitInit(git, t) + gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "files-src") + gittestutil.GitCommit(git, t, "initial commit") + addr := gittestutil.GitServeHTTP(git, t) + + out, err := bakeCmd(sb, withDir("/tmp"), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest)) + require.NoError(t, err, out) + require.FileExists(t, filepath.Join(dirDest, "file.txt")) +} + +func testBakeRemoteNamedContextDot(t *testing.T, sb integration.Sandbox) { + bakefile := []byte(` +target default { + context = "./build" + dockerfile = "Dockerfile" + contexts = { + files = "." + } +} +`) + dockerfile := []byte(` +FROM scratch +COPY --from=files marker.txt /marker.txt +`) + + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + fstest.CreateDir("build", 0700), + fstest.CreateFile("build/Dockerfile", dockerfile, 0600), + fstest.CreateFile("marker.txt", []byte("hello"), 0600), + ) + dirDest := t.TempDir() + + git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + require.NoError(t, err) + gittestutil.GitInit(git, t) + gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "marker.txt") + gittestutil.GitCommit(git, t, "initial commit") + addr := gittestutil.GitServeHTTP(git, t) + + out, err := bakeCmd(sb, withDir("/tmp"), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest)) + require.NoError(t, err, out) + require.FileExists(t, filepath.Join(dirDest, "marker.txt")) +} + func testBakeRemoteCmdContextEscapeRoot(t *testing.T, sb integration.Sandbox) { dirSrc := tmpdir( t,