build: allow client side querystring resolution

For the cases where frontend/buildkit doesn't support new
Git Querystring format, allow resolving the URL into LLB
on client side and then build from input.

Note that this produces slightly different provenance
where context is not set as string, so added opt-in
via environment variable for now.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2025-09-08 17:40:31 -07:00
parent ad9d476457
commit c737e84f94
+75 -15
View File
@@ -10,6 +10,7 @@ import (
"slices"
"strconv"
"strings"
"sync"
"syscall"
awsconfig "github.com/aws/aws-sdk-go-v2/config"
@@ -29,6 +30,7 @@ import (
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/ociindex"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend/dockerui"
gateway "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
@@ -44,6 +46,15 @@ import (
"github.com/tonistiigi/fsutil"
)
var sendGitQueryAsInput = sync.OnceValue(func() bool {
if v, ok := os.LookupEnv("BUILDX_SEND_GIT_QUERY_AS_INPUT"); ok {
if vv, err := strconv.ParseBool(v); err == nil {
return vv
}
}
return false
})
func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *Options, bopts gateway.BuildOpts, cfg *confutil.Config, pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) {
nodeDriver := node.Driver
defers := make([]func(), 0, 2)
@@ -298,6 +309,13 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
so.Exports = opt.Exports
so.Session = slices.Clone(opt.Session)
for k, v := range opt.BuildArgs {
so.FrontendAttrs["build-arg:"+k] = v
}
for k, v := range opt.Labels {
so.FrontendAttrs["label:"+k] = v
}
releaseLoad, err := loadInputs(ctx, nodeDriver, &opt.Inputs, pw, &so)
if err != nil {
return nil, nil, err
@@ -324,12 +342,6 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
if opt.NoCache {
so.FrontendAttrs["no-cache"] = ""
}
for k, v := range opt.BuildArgs {
so.FrontendAttrs["build-arg:"+k] = v
}
for k, v := range opt.Labels {
so.FrontendAttrs["label:"+k] = v
}
for k, v := range node.ProxyConfig {
if _, ok := opt.BuildArgs[k]; !ok {
@@ -472,9 +484,13 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
}
target.FrontendAttrs["context"] = inp.ContextPath
gitRef, err := gitutil.ParseURL(inp.ContextPath)
if err == nil && len(gitRef.Query) > 0 {
caps["moby.buildkit.frontend.gitquerystring"] = struct{}{}
if err := processGitURL(inp.ContextPath, "context", target, caps); err != nil {
return nil, err
}
if st, ok := target.FrontendInputs["context"]; ok {
if dockerfileReader == nil && !filepath.IsAbs(inp.DockerfilePath) {
target.FrontendInputs["dockerfile"] = st
}
}
default:
@@ -536,12 +552,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
if IsRemoteURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") {
target.FrontendAttrs["context:"+k] = v.Path
gitRef, err := gitutil.ParseURL(v.Path)
if err == nil && len(gitRef.Query) > 0 {
if _, ok := caps["moby.buildkit.frontend.gitquerystring"]; !ok {
caps["moby.buildkit.frontend.gitquerystring+forward"] = struct{}{}
}
}
processGitURL(v.Path, "context:"+k, target, caps)
continue
}
@@ -672,6 +683,55 @@ func createTempDockerfile(r io.Reader, multiReader *SyncMultiReader) (string, er
return dir, err
}
func processGitURL(url string, name string, target *client.SolveOpt, caps map[string]struct{}) error {
gitRef, err := gitutil.ParseURL(url)
if err != nil {
return err
}
if len(gitRef.Query) == 0 {
return nil
}
if !sendGitQueryAsInput() {
capName := "moby.buildkit.frontend.gitquerystring"
if name != "context" {
capName += "+forward"
}
caps[capName] = struct{}{}
return nil
}
var keepGitDir *bool
if name == "context" {
if v, ok := target.FrontendAttrs["build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR"]; ok {
if vv, err := strconv.ParseBool(v); err == nil {
keepGitDir = &vv
}
}
}
st, ok, err := dockerui.DetectGitContext(url, keepGitDir)
if err != nil {
return err
}
if !ok {
return nil
}
if target.FrontendInputs == nil {
target.FrontendInputs = make(map[string]llb.State)
}
if name == "context" {
target.FrontendInputs["context"] = *st
delete(target.FrontendAttrs, "context")
} else {
inputName := "git_state_" + name
target.FrontendInputs[inputName] = *st
target.FrontendAttrs[name] = "input:" + inputName
}
return nil
}
// handle https://github.com/moby/moby/pull/10858
func handleLowercaseDockerfile(dir, p string) string {
if filepath.Base(p) != "Dockerfile" {