From 5e6951c5715c6c98049bd4df257994e3c332c1c7 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Tue, 2 Sep 2025 09:45:48 -0700 Subject: [PATCH] git querystring frontend capability detection Signed-off-by: Tonis Tiigi --- build/opt.go | 23 ++++++++++++++++++++++- commands/build.go | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/build/opt.go b/build/opt.go index 56879b379..73227f3ec 100644 --- a/build/opt.go +++ b/build/opt.go @@ -38,6 +38,7 @@ import ( "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/apicaps" "github.com/moby/buildkit/util/entitlements" + "github.com/moby/buildkit/util/gitutil" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/tonistiigi/fsutil" @@ -404,6 +405,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro dockerfileName = inp.DockerfilePath dockerfileSrcName = inp.DockerfilePath toRemove []string + caps = map[string]struct{}{} ) switch { @@ -469,6 +471,12 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro target.FrontendAttrs["dockerfilekey"] = "dockerfile" } target.FrontendAttrs["context"] = inp.ContextPath + + gitRef, err := gitutil.ParseURL(inp.ContextPath) + if err == nil && len(gitRef.Query) > 0 { + caps["moby.buildkit.frontend.gitquerystring"] = struct{}{} + } + default: return nil, errors.Errorf("unable to prepare context: path %q not found", inp.ContextPath) } @@ -516,7 +524,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro target.FrontendAttrs["filename"] = dockerfileName for k, v := range inp.NamedContexts { - target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward" + caps["moby.buildkit.frontend.contexts+forward"] = struct{}{} if v.State != nil { target.FrontendAttrs["context:"+k] = "input:" + k if target.FrontendInputs == nil { @@ -528,6 +536,12 @@ 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{}{} + } + } continue } @@ -557,6 +571,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro target.FrontendAttrs["context:"+k] = "oci-layout://" + storeName + ":" + tag + "@" + dig continue } + st, err := os.Stat(v.Path) if err != nil { return nil, errors.Wrapf(err, "failed to get build context %v", k) @@ -580,6 +595,12 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro } } + if len(caps) > 0 { + keys := slices.Collect(maps.Keys(caps)) + slices.Sort(keys) + target.FrontendAttrs["frontend.caps"] = strings.Join(keys, ",") + } + inp.DockerfileMappingSrc = dockerfileSrcName inp.DockerfileMappingDst = dockerfileName return release, nil diff --git a/commands/build.go b/commands/build.go index 16a395b85..30565fe6e 100644 --- a/commands/build.go +++ b/commands/build.go @@ -692,6 +692,11 @@ func wrapBuildError(err error, bake bool) error { msg += " Named contexts are supported since Dockerfile v1.4. Use #syntax directive in Dockerfile or update to latest BuildKit." return &wrapped{err, msg} } + if st.Code() == codes.Unimplemented && strings.Contains(st.Message(), "unsupported frontend capability moby.buildkit.frontend.gitquerystring") { + msg := "current frontend does not support Git URLs with query string components." + msg += " Git URLs with query string are supported since Dockerfile v1.18 and BuildKit v0.24. Use BUILDKIT_SYNTAX build-arg, #syntax directive in Dockerfile or update to latest BuildKit." + return &wrapped{err, msg} + } } return err }