diff --git a/bake/remote.go b/bake/remote.go index 2f4dfc41a546..18eef16bc045 100644 --- a/bake/remote.go +++ b/bake/remote.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "os" + "strconv" "strings" "github.com/docker/buildx/build" @@ -33,7 +34,8 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name var filename string keepGitDir := false - st, ok, err := dockerui.DetectGitContext(url, &keepGitDir) + gitAdvice, _ := strconv.ParseBool(os.Getenv("BUILDX_BAKE_GIT_ADVICE")) + st, ok, err := dockerui.DetectGitContext(url, &keepGitDir, llb.GitAdvice(gitAdvice)) if ok { if err != nil { return nil, nil, err diff --git a/build/opt.go b/build/opt.go index fb8b5fe393d2..a0a42aa0ed90 100644 --- a/build/opt.go +++ b/build/opt.go @@ -1107,7 +1107,11 @@ func resolveRemotePolicyContextState(contextPath string, target *client.SolveOpt } keepGitDir := false - if st, ok, _ := dockerui.DetectGitContext(contextPath, &keepGitDir); ok { + var gitAdvice bool + if target != nil { + gitAdvice, _ = strconv.ParseBool(target.FrontendAttrs["build-arg:BUILDKIT_GIT_ADVICE"]) + } + if st, ok, _ := dockerui.DetectGitContext(contextPath, &keepGitDir, llb.GitAdvice(gitAdvice)); ok { return st } @@ -1213,7 +1217,8 @@ func processGitURL(url string, name string, target *client.SolveOpt, caps map[st } } - st, ok, err := dockerui.DetectGitContext(url, keepGitDir) + gitAdvice, _ := strconv.ParseBool(target.FrontendAttrs["build-arg:BUILDKIT_GIT_ADVICE"]) + st, ok, err := dockerui.DetectGitContext(url, keepGitDir, llb.GitAdvice(gitAdvice)) if err != nil { return err } diff --git a/build/opt_test.go b/build/opt_test.go index 9327d211d37c..85955841308a 100644 --- a/build/opt_test.go +++ b/build/opt_test.go @@ -10,6 +10,7 @@ import ( "github.com/docker/buildx/util/ocilayout" "github.com/docker/buildx/util/progress" "github.com/moby/buildkit/client" + "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/ociindex" gateway "github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/solver/pb" @@ -21,6 +22,62 @@ import ( "github.com/stretchr/testify/require" ) +func TestGitAdviceContexts(t *testing.T) { + previous := sendGitQueryAsInput + sendGitQueryAsInput = func() bool { return true } + t.Cleanup(func() { sendGitQueryAsInput = previous }) + + const url = "https://example.com/repo.git?branch=main" + for _, name := range []string{"context", "context:src", "policy"} { + t.Run(name, func(t *testing.T) { + for _, tc := range []struct { + name string + arg string + want string + }{ + {name: "unset"}, + {name: "enabled", arg: "1", want: "true"}, + {name: "true", arg: "true", want: "true"}, + {name: "disabled", arg: "0"}, + {name: "false", arg: "false"}, + } { + t.Run(tc.name, func(t *testing.T) { + target := &client.SolveOpt{FrontendAttrs: map[string]string{}} + if tc.arg != "" { + target.FrontendAttrs["build-arg:BUILDKIT_GIT_ADVICE"] = tc.arg + } + var st *llb.State + if name == "policy" { + st = resolveRemotePolicyContextState(url, target) + } else { + require.NoError(t, processGitURL(url, name, target, map[string]struct{}{})) + inputName := "context" + if name != "context" { + inputName = "git_state_" + name + } + input, ok := target.FrontendInputs[inputName] + require.True(t, ok) + st = &input + } + require.NotNil(t, st) + def, err := st.Marshal(t.Context()) + require.NoError(t, err) + require.NotEmpty(t, def.Def) + var op pb.Op + require.NoError(t, op.UnmarshalVT(def.Def[0])) + source := op.GetSource() + require.NotNil(t, source) + if tc.want == "" { + require.NotContains(t, source.Attrs, pb.AttrGitAdvice) + } else { + require.Equal(t, tc.want, source.Attrs[pb.AttrGitAdvice]) + } + }) + } + }) + } +} + func TestCacheOptions_DerivedVars(t *testing.T) { t.Setenv("ACTIONS_RUNTIME_TOKEN", "sensitive_token") t.Setenv("ACTIONS_CACHE_URL", "https://cache.github.com") diff --git a/tests/bake.go b/tests/bake.go index 279bc3c1143e..4635f1728423 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -49,6 +49,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){ testBakeFileRelativePaths, testBakeLocalExportDeleteMode, testBakeRemote, + testBakeRemoteGitAdvice, testBakeRemoteAuth, testBakeRemoteCmdContext, testBakeRemoteLocalOverride, @@ -1011,6 +1012,46 @@ EOT require.FileExists(t, filepath.Join(dirDest, "foo")) } +func testBakeRemoteGitAdvice(t *testing.T, sb integration.Sandbox) { + skipNoCompatBuildKit(t, sb, ">= 0.33.0-0", "Git advice control requires BuildKit v0.33") + + for _, tc := range []struct { + name string + value string + wantAdvice bool + }{ + {name: "default"}, + {name: "enabled", value: "1", wantAdvice: true}, + {name: "true", value: "true", wantAdvice: true}, + {name: "disabled", value: "0"}, + {name: "false", value: "false"}, + } { + t.Run(tc.name, func(t *testing.T) { + // Use distinct commits so cached checkouts cannot hide advice output. + bakefile := "# " + tc.name + "\ntarget \"default\" {}" + dir := tmpdir(t, fstest.CreateFile("docker-bake.hcl", []byte(bakefile), 0600)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) + require.NoError(t, err) + gittestutil.GitInit(git, t) + gittestutil.GitAdd(git, t, "docker-bake.hcl") + gittestutil.GitCommit(git, t, "initial commit") + gittestutil.GitTag(git, t, "v1") + addr := gittestutil.GitServeHTTP(git, t) + + out, err := bakeCmd(sb, + withEnv("BUILDX_BAKE_GIT_ADVICE="+tc.value), + withArgs(addr+"?tag=v1&keep-git-dir=true", "--print", "--progress=plain"), + ) + require.NoError(t, err, out) + if tc.wantAdvice { + require.Contains(t, out, "detached HEAD") + } else { + require.NotContains(t, out, "detached HEAD") + } + }) + } +} + func testBakeRemoteAuth(t *testing.T, sb integration.Sandbox) { bakefile := []byte(` target "default" {