From 1f39ad2001cb82fc55b52b5755ace2704a82d31b Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 29 Aug 2025 14:49:16 -0700 Subject: [PATCH] vendor: update buildkit to v0.24.0-rc2 Signed-off-by: Tonis Tiigi --- bake/remote.go | 6 +- build/utils.go | 2 +- commands/history/utils.go | 2 +- go.mod | 2 +- go.sum | 4 +- .../moby/buildkit/client/llb/source.go | 61 ++++++-- .../frontend/dockerfile/dfgitutil/git_ref.go | 142 +++++++++++++++--- .../buildkit/frontend/dockerui/context.go | 40 +++-- .../frontend/dockerui/namedcontext.go | 10 +- .../moby/buildkit/solver/pb/attr.go | 1 + .../moby/buildkit/solver/pb/caps.go | 23 ++- .../moby/buildkit/util/gitutil/git_url.go | 29 +++- .../moby/buildkit/util/sshutil/scpurl.go | 34 ++++- vendor/modules.txt | 2 +- 14 files changed, 284 insertions(+), 74 deletions(-) diff --git a/bake/remote.go b/bake/remote.go index ab8f330b7..94b1da494 100644 --- a/bake/remote.go +++ b/bake/remote.go @@ -32,8 +32,12 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name var sessions []session.Attachable var filename string - st, ok := dockerui.DetectGitContext(url, false) + keepGitDir := false + st, ok, err := dockerui.DetectGitContext(url, &keepGitDir) if ok { + if err != nil { + return nil, nil, err + } if ssh, err := build.CreateSSH([]*buildflags.SSH{{ ID: "default", Paths: strings.Split(os.Getenv("BUILDX_BAKE_GIT_SSH"), ","), diff --git a/build/utils.go b/build/utils.go index 5ab306f1b..a7b6b19ec 100644 --- a/build/utils.go +++ b/build/utils.go @@ -36,7 +36,7 @@ func IsRemoteURL(c string) bool { if isHTTPURL(c) { return true } - if _, err := dfgitutil.ParseGitRef(c); err == nil { + if _, ok, _ := dfgitutil.ParseGitRef(c); ok { return true } return false diff --git a/commands/history/utils.go b/commands/history/utils.go index df7427577..75fce0261 100644 --- a/commands/history/utils.go +++ b/commands/history/utils.go @@ -332,7 +332,7 @@ func valueFiler(key, value, sep string) matchFunc { recValue = v } else { if context, ok := rec.FrontendAttrs["context"]; ok { - if ref, err := dfgitutil.ParseGitRef(context); err == nil { + if ref, _, err := dfgitutil.ParseGitRef(context); err == nil { recValue = ref.Remote } } diff --git a/go.mod b/go.mod index cbb94a9af..04274e95d 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/hashicorp/hcl/v2 v2.23.0 github.com/in-toto/in-toto-golang v0.9.0 github.com/mitchellh/hashstructure/v2 v2.0.2 - github.com/moby/buildkit v0.24.0-rc1 + github.com/moby/buildkit v0.24.0-rc2 github.com/moby/go-archive v0.1.0 github.com/moby/sys/atomicwriter v0.1.0 github.com/moby/sys/mountinfo v0.7.2 diff --git a/go.sum b/go.sum index 6106610f1..647ea0ee2 100644 --- a/go.sum +++ b/go.sum @@ -255,8 +255,8 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/buildkit v0.24.0-rc1 h1:taA+MPeYWtGyRQ1SGbdTHVk5khWKFR7f9WI2coW/Ggs= -github.com/moby/buildkit v0.24.0-rc1/go.mod h1:4qovICAdR2H4C7+EGMRva5zgHW1gyhT4/flHI7F5F9k= +github.com/moby/buildkit v0.24.0-rc2 h1:1Z9+R162yauf3SYvn8cGjBPnglMDK4bO+00dNlJSgZI= +github.com/moby/buildkit v0.24.0-rc2/go.mod h1:4qovICAdR2H4C7+EGMRva5zgHW1gyhT4/flHI7F5F9k= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= diff --git a/vendor/github.com/moby/buildkit/client/llb/source.go b/vendor/github.com/moby/buildkit/client/llb/source.go index d6eba0758..5d1f6f104 100644 --- a/vendor/github.com/moby/buildkit/client/llb/source.go +++ b/vendor/github.com/moby/buildkit/client/llb/source.go @@ -247,9 +247,13 @@ const ( // Formats that utilize SSH may need to supply credentials as a [GitOption]. // You may need to check the source code for a full list of supported formats. // +// Fragment can be used to pass ref:subdir format that can set in (old-style) +// Docker Git URL format after # . This is provided for backwards compatibility. +// It is recommended to leave it empty and call GitRef(), GitSubdir() options instead. +// // By default the git repository is cloned with `--depth=1` to reduce the amount of data downloaded. // Additionally the ".git" directory is removed after the clone, you can keep ith with the [KeepGitDir] [GitOption]. -func Git(url, ref string, opts ...GitOption) State { +func Git(url, fragment string, opts ...GitOption) State { remote, err := gitutil.ParseURL(url) if errors.Is(err, gitutil.ErrUnknownProtocol) { url = "https://" + url @@ -259,6 +263,20 @@ func Git(url, ref string, opts ...GitOption) State { url = remote.Remote } + gi := &GitInfo{ + AuthHeaderSecret: GitAuthHeaderKey, + AuthTokenSecret: GitAuthTokenKey, + } + ref, subdir, ok := strings.Cut(fragment, ":") + if ref != "" { + GitRef(ref).SetGitOption(gi) + } + if ok && subdir != "" { + GitSubDir(subdir).SetGitOption(gi) + } + for _, o := range opts { + o.SetGitOption(gi) + } var id string if err != nil { // If we can't parse the URL, just use the full URL as the ID. The git @@ -269,18 +287,13 @@ func Git(url, ref string, opts ...GitOption) State { // for different protocols (e.g. https and ssh) that have the same // host/path/fragment combination. id = remote.Host + path.Join("/", remote.Path) - if ref != "" { - id += "#" + ref + if gi.Ref != "" || gi.SubDir != "" { + id += "#" + gi.Ref + if gi.SubDir != "" { + id += ":" + gi.SubDir + } } } - - gi := &GitInfo{ - AuthHeaderSecret: GitAuthHeaderKey, - AuthTokenSecret: GitAuthTokenKey, - } - for _, o := range opts { - o.SetGitOption(gi) - } attrs := map[string]string{} if gi.KeepGitDir { attrs[pb.AttrKeepGitDir] = "true" @@ -328,6 +341,11 @@ func Git(url, ref string, opts ...GitOption) State { addCap(&gi.Constraints, pb.CapSourceGitChecksum) } + if gi.SkipSubmodules { + attrs[pb.AttrGitSkipSubmodules] = "true" + addCap(&gi.Constraints, pb.CapSourceGitSkipSubmodules) + } + addCap(&gi.Constraints, pb.CapSourceGit) source := NewSource("git://"+id, attrs, gi.Constraints) @@ -352,6 +370,27 @@ type GitInfo struct { KnownSSHHosts string MountSSHSock string Checksum string + Ref string + SubDir string + SkipSubmodules bool +} + +func GitRef(v string) GitOption { + return gitOptionFunc(func(gi *GitInfo) { + gi.Ref = v + }) +} + +func GitSubDir(v string) GitOption { + return gitOptionFunc(func(gi *GitInfo) { + gi.SubDir = v + }) +} + +func GitSkipSubmodules() GitOption { + return gitOptionFunc(func(gi *GitInfo) { + gi.SkipSubmodules = true + }) } func KeepGitDir() GitOption { diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dfgitutil/git_ref.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dfgitutil/git_ref.go index a2b37f8e0..f1cb45f23 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerfile/dfgitutil/git_ref.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dfgitutil/git_ref.go @@ -3,6 +3,7 @@ package dfgitutil import ( "net/url" + "strconv" "strings" cerrdefs "github.com/containerd/errdefs" @@ -23,9 +24,12 @@ type GitRef struct { // e.g., "bar" for "https://github.com/foo/bar.git" ShortName string - // Commit is a commit hash, a tag, or branch name. - // Commit is optional. - Commit string + // Ref is a commit hash, a tag, or branch name. + // Ref is optional. + Ref string + + // Checksum is a commit hash. + Checksum string // SubDir is a directory path inside the repo. // SubDir is optional. @@ -46,12 +50,16 @@ type GitRef struct { // Discouraged, although not deprecated. // Instead, consider using an encrypted TCP connection such as "git@github.com/foo/bar.git" or "https://github.com/foo/bar.git". UnencryptedTCP bool + + // KeepGitDir is true for URL that controls whether to keep the .git directory. + KeepGitDir *bool + + // Submodules is true for URL that controls whether to fetch git submodules. + Submodules *bool } -// var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`) - // ParseGitRef parses a git ref. -func ParseGitRef(ref string) (*GitRef, error) { +func ParseGitRef(ref string) (*GitRef, bool, error) { res := &GitRef{} var ( @@ -60,21 +68,25 @@ func ParseGitRef(ref string) (*GitRef, error) { ) if strings.HasPrefix(ref, "./") || strings.HasPrefix(ref, "../") { - return nil, cerrdefs.ErrInvalidArgument + return nil, false, errors.WithStack(cerrdefs.ErrInvalidArgument) } else if strings.HasPrefix(ref, "github.com/") { res.IndistinguishableFromLocal = true // Deprecated - remote = gitutil.FromURL(&url.URL{ - Scheme: "https", - Host: "github.com", - Path: strings.TrimPrefix(ref, "github.com/"), - }) + u, err := url.Parse(ref) + if err != nil { + return nil, false, err + } + u.Scheme = "https" + remote, err = gitutil.FromURL(u) + if err != nil { + return nil, false, err + } } else { remote, err = gitutil.ParseURL(ref) if errors.Is(err, gitutil.ErrUnknownProtocol) { - return nil, err + return nil, false, err } if err != nil { - return nil, err + return nil, false, err } switch remote.Scheme { @@ -86,7 +98,7 @@ func ParseGitRef(ref string) (*GitRef, error) { // An HTTP(S) URL is considered to be a valid git ref only when it has the ".git[...]" suffix. case gitutil.HTTPProtocol, gitutil.HTTPSProtocol: if !strings.HasSuffix(remote.Path, ".git") { - return nil, cerrdefs.ErrInvalidArgument + return nil, false, errors.WithStack(cerrdefs.ErrInvalidArgument) } } } @@ -96,11 +108,107 @@ func ParseGitRef(ref string) (*GitRef, error) { _, res.Remote, _ = strings.Cut(res.Remote, "://") } if remote.Opts != nil { - res.Commit, res.SubDir = remote.Opts.Ref, remote.Opts.Subdir + res.Ref, res.SubDir = remote.Opts.Ref, remote.Opts.Subdir } repoSplitBySlash := strings.Split(res.Remote, "/") res.ShortName = strings.TrimSuffix(repoSplitBySlash[len(repoSplitBySlash)-1], ".git") - return res, nil + if err := res.loadQuery(remote.Query); err != nil { + return nil, true, err + } + + return res, true, nil +} + +func (gf *GitRef) loadQuery(query url.Values) error { + if len(query) == 0 { + return nil + } + var tag, branch string + for k, v := range query { + switch len(v) { + case 0, 1: + if len(v) == 0 || v[0] == "" { + switch k { + case "submodules", "keep-git-dir": + v = nil + default: + return errors.Errorf("query %q has no value", k) + } + } + // NOP + default: + return errors.Errorf("query %q has multiple values", k) + } + switch k { + case "ref": + if gf.Ref != "" && gf.Ref != v[0] { + return errors.Errorf("ref conflicts: %q vs %q", gf.Ref, v[0]) + } + gf.Ref = v[0] + case "tag": + tag = v[0] + case "branch": + branch = v[0] + case "subdir": + if gf.SubDir != "" && gf.SubDir != v[0] { + return errors.Errorf("subdir conflicts: %q vs %q", gf.SubDir, v[0]) + } + gf.SubDir = v[0] + case "checksum", "commit": + gf.Checksum = v[0] + case "keep-git-dir": + var vv bool + if len(v) == 0 { + vv = true + } else { + var err error + vv, err = strconv.ParseBool(v[0]) + if err != nil { + return errors.Errorf("invalid keep-git-dir value: %q", v[0]) + } + } + gf.KeepGitDir = &vv + case "submodules": + var vv bool + if len(v) == 0 { + vv = true + } else { + var err error + vv, err = strconv.ParseBool(v[0]) + if err != nil { + return errors.Errorf("invalid submodules value: %q", v[0]) + } + } + gf.Submodules = &vv + default: + return errors.Errorf("unexpected query %q", k) + } + } + if tag != "" { + const tagPrefix = "refs/tags/" + if !strings.HasPrefix(tag, tagPrefix) { + tag = tagPrefix + tag + } + if gf.Ref != "" && gf.Ref != tag { + return errors.Errorf("ref conflicts: %q vs %q", gf.Ref, tag) + } + gf.Ref = tag + } + if branch != "" { + if tag != "" { + // TODO: consider allowing this, when the tag actually exists on the branch + return errors.New("branch conflicts with tag") + } + const branchPrefix = "refs/heads/" + if !strings.HasPrefix(branch, branchPrefix) { + branch = branchPrefix + branch + } + if gf.Ref != "" && gf.Ref != branch { + return errors.Errorf("ref conflicts: %q vs %q", gf.Ref, branch) + } + gf.Ref = branch + } + return nil } diff --git a/vendor/github.com/moby/buildkit/frontend/dockerui/context.go b/vendor/github.com/moby/buildkit/frontend/dockerui/context.go index 8aa6a2d8a..88d68569b 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerui/context.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerui/context.go @@ -69,11 +69,14 @@ func (bc *Client) initContext(ctx context.Context) (*buildContext, error) { bctx.dockerfileLocalName = v } - keepGit := false + var keepGit *bool if v, err := strconv.ParseBool(opts[keyContextKeepGitDirArg]); err == nil { - keepGit = v + keepGit = &v } - if st, ok := DetectGitContext(opts[localNameContext], keepGit); ok { + if st, ok, err := DetectGitContext(opts[localNameContext], keepGit); ok { + if err != nil { + return nil, err + } bctx.context = st bctx.dockerfile = st } else if st, filename, ok := DetectHTTPContext(opts[localNameContext]); ok { @@ -140,22 +143,33 @@ func (bc *Client) initContext(ctx context.Context) (*buildContext, error) { return bctx, nil } -func DetectGitContext(ref string, keepGit bool) (*llb.State, bool) { - g, err := dfgitutil.ParseGitRef(ref) +func DetectGitContext(ref string, keepGit *bool) (*llb.State, bool, error) { + g, isGit, err := dfgitutil.ParseGitRef(ref) if err != nil { - return nil, false + return nil, isGit, err } - commit := g.Commit - if g.SubDir != "" { - commit += ":" + g.SubDir + gitOpts := []llb.GitOption{ + llb.GitRef(g.Ref), + WithInternalName("load git source " + ref), } - gitOpts := []llb.GitOption{WithInternalName("load git source " + ref)} - if keepGit { + if g.KeepGitDir != nil && *g.KeepGitDir { gitOpts = append(gitOpts, llb.KeepGitDir()) } + if keepGit != nil && *keepGit { + gitOpts = append(gitOpts, llb.KeepGitDir()) + } + if g.SubDir != "" { + gitOpts = append(gitOpts, llb.GitSubDir(g.SubDir)) + } + if g.Checksum != "" { + gitOpts = append(gitOpts, llb.GitChecksum(g.Checksum)) + } + if g.Submodules != nil && !*g.Submodules { + gitOpts = append(gitOpts, llb.GitSkipSubmodules()) + } - st := llb.Git(g.Remote, commit, gitOpts...) - return &st, true + st := llb.Git(g.Remote, "", gitOpts...) + return &st, true, nil } func DetectHTTPContext(ref string) (*llb.State, string, bool) { diff --git a/vendor/github.com/moby/buildkit/frontend/dockerui/namedcontext.go b/vendor/github.com/moby/buildkit/frontend/dockerui/namedcontext.go index 9598d0b2d..2033b3749 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerui/namedcontext.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerui/namedcontext.go @@ -138,17 +138,23 @@ func (nc *NamedContext) load(ctx context.Context, count int) (*llb.State, *docke } return &st, &img, nil case "git": - st, ok := DetectGitContext(nc.input, true) + st, ok, err := DetectGitContext(nc.input, nil) if !ok { return nil, nil, errors.Errorf("invalid git context %s", nc.input) } + if err != nil { + return nil, nil, err + } return st, nil, nil case "http", "https": - st, ok := DetectGitContext(nc.input, true) + st, ok, err := DetectGitContext(nc.input, nil) if !ok { httpst := llb.HTTP(nc.input, llb.WithCustomName("[context "+nc.nameWithPlatform+"] "+nc.input)) st = &httpst } + if err != nil { + return nil, nil, err + } return st, nil, nil case "oci-layout": refSpec := strings.TrimPrefix(vv[1], "//") diff --git a/vendor/github.com/moby/buildkit/solver/pb/attr.go b/vendor/github.com/moby/buildkit/solver/pb/attr.go index 1157d9877..f45a90622 100644 --- a/vendor/github.com/moby/buildkit/solver/pb/attr.go +++ b/vendor/github.com/moby/buildkit/solver/pb/attr.go @@ -7,6 +7,7 @@ const AttrAuthTokenSecret = "git.authtokensecret" const AttrKnownSSHHosts = "git.knownsshhosts" const AttrMountSSHSock = "git.mountsshsock" const AttrGitChecksum = "git.checksum" +const AttrGitSkipSubmodules = "git.skipsubmodules" const AttrLocalSessionID = "local.session" const AttrLocalUniqueID = "local.unique" diff --git a/vendor/github.com/moby/buildkit/solver/pb/caps.go b/vendor/github.com/moby/buildkit/solver/pb/caps.go index 75c298ae0..2790c7e0f 100644 --- a/vendor/github.com/moby/buildkit/solver/pb/caps.go +++ b/vendor/github.com/moby/buildkit/solver/pb/caps.go @@ -23,14 +23,15 @@ const ( CapSourceLocalDiffer apicaps.CapID = "source.local.differ" CapSourceMetadataTransfer apicaps.CapID = "source.local.metadatatransfer" - CapSourceGit apicaps.CapID = "source.git" - CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir" - CapSourceGitFullURL apicaps.CapID = "source.git.fullurl" - CapSourceGitHTTPAuth apicaps.CapID = "source.git.httpauth" - CapSourceGitKnownSSHHosts apicaps.CapID = "source.git.knownsshhosts" - CapSourceGitMountSSHSock apicaps.CapID = "source.git.mountsshsock" - CapSourceGitSubdir apicaps.CapID = "source.git.subdir" - CapSourceGitChecksum apicaps.CapID = "source.git.checksum" + CapSourceGit apicaps.CapID = "source.git" + CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir" + CapSourceGitFullURL apicaps.CapID = "source.git.fullurl" + CapSourceGitHTTPAuth apicaps.CapID = "source.git.httpauth" + CapSourceGitKnownSSHHosts apicaps.CapID = "source.git.knownsshhosts" + CapSourceGitMountSSHSock apicaps.CapID = "source.git.mountsshsock" + CapSourceGitSubdir apicaps.CapID = "source.git.subdir" + CapSourceGitChecksum apicaps.CapID = "source.git.checksum" + CapSourceGitSkipSubmodules apicaps.CapID = "source.git.skipsubmodules" CapSourceHTTP apicaps.CapID = "source.http" CapSourceHTTPAuth apicaps.CapID = "source.http.auth" @@ -229,6 +230,12 @@ func init() { Status: apicaps.CapStatusExperimental, }) + Caps.Init(apicaps.Cap{ + ID: CapSourceGitSkipSubmodules, + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + Caps.Init(apicaps.Cap{ ID: CapSourceHTTP, Enabled: true, diff --git a/vendor/github.com/moby/buildkit/util/gitutil/git_url.go b/vendor/github.com/moby/buildkit/util/gitutil/git_url.go index 1c506322a..80717a67a 100644 --- a/vendor/github.com/moby/buildkit/util/gitutil/git_url.go +++ b/vendor/github.com/moby/buildkit/util/gitutil/git_url.go @@ -47,16 +47,17 @@ type GitURL struct { Path string // User is the username/password to access the host User *url.Userinfo + // Query is the query parameters for the URL + Query url.Values // Opts can contain additional metadata Opts *GitURLOpts - // Remote is a valid URL remote to pass into the Git CLI tooling (i.e. // without the fragment metadata) Remote string } // GitURLOpts is the buildkit-specific metadata extracted from the fragment -// of a remote URL. +// or the query of a remote URL. type GitURLOpts struct { // Ref is the git reference Ref string @@ -86,11 +87,11 @@ func ParseURL(remote string) (*GitURL, error) { if err != nil { return nil, err } - return FromURL(url), nil + return FromURL(url) } if url, err := sshutil.ParseSCPStyleURL(remote); err == nil { - return fromSCPStyleURL(url), nil + return fromSCPStyleURL(url) } return nil, ErrUnknownProtocol @@ -105,28 +106,40 @@ func IsGitTransport(remote string) bool { return sshutil.IsImplicitSSHTransport(remote) } -func FromURL(url *url.URL) *GitURL { +func FromURL(url *url.URL) (*GitURL, error) { withoutOpts := *url withoutOpts.Fragment = "" + withoutOpts.RawQuery = "" + q := url.Query() + if len(q) == 0 { + q = nil + } return &GitURL{ Scheme: url.Scheme, User: url.User, Host: url.Host, Path: url.Path, + Query: q, Opts: parseOpts(url.Fragment), Remote: withoutOpts.String(), - } + }, nil } -func fromSCPStyleURL(url *sshutil.SCPStyleURL) *GitURL { +func fromSCPStyleURL(url *sshutil.SCPStyleURL) (*GitURL, error) { withoutOpts := *url withoutOpts.Fragment = "" + withoutOpts.Query = nil + q := url.Query + if len(q) == 0 { + q = nil + } return &GitURL{ Scheme: SSHProtocol, User: url.User, Host: url.Host, Path: url.Path, + Query: q, Opts: parseOpts(url.Fragment), Remote: withoutOpts.String(), - } + }, nil } diff --git a/vendor/github.com/moby/buildkit/util/sshutil/scpurl.go b/vendor/github.com/moby/buildkit/util/sshutil/scpurl.go index 10491f32f..5d85f09a0 100644 --- a/vendor/github.com/moby/buildkit/util/sshutil/scpurl.go +++ b/vendor/github.com/moby/buildkit/util/sshutil/scpurl.go @@ -1,13 +1,14 @@ package sshutil import ( - "errors" "fmt" "net/url" "regexp" + + "github.com/pkg/errors" ) -var gitSSHRegex = regexp.MustCompile("^([a-zA-Z0-9-_]+)@([a-zA-Z0-9-.]+):(.*?)(?:#(.*))?$") +var gitSSHRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)@([a-zA-Z0-9-.]+):(.*?)(?:\?(.*?))?(?:#(.*))?$`) func IsImplicitSSHTransport(s string) bool { return gitSSHRegex.MatchString(s) @@ -18,6 +19,7 @@ type SCPStyleURL struct { Host string Path string + Query url.Values Fragment string } @@ -26,18 +28,34 @@ func ParseSCPStyleURL(raw string) (*SCPStyleURL, error) { if matches == nil { return nil, errors.New("invalid scp-style url") } + + rawQuery := matches[4] + vals := url.Values{} + if rawQuery != "" { + var err error + vals, err = url.ParseQuery(rawQuery) + if err != nil { + return nil, errors.Wrap(err, "invalid query in scp-style url") + } + } + return &SCPStyleURL{ User: url.User(matches[1]), Host: matches[2], Path: matches[3], - Fragment: matches[4], + Query: vals, + Fragment: matches[5], }, nil } -func (url *SCPStyleURL) String() string { - base := fmt.Sprintf("%s@%s:%s", url.User.String(), url.Host, url.Path) - if url.Fragment == "" { - return base +func (u *SCPStyleURL) String() string { + s := fmt.Sprintf("%s@%s:%s", u.User.String(), u.Host, u.Path) + + if len(u.Query) > 0 { + s += "?" + u.Query.Encode() } - return base + "#" + url.Fragment + if u.Fragment != "" { + s += "#" + u.Fragment + } + return s } diff --git a/vendor/modules.txt b/vendor/modules.txt index 585ea6d73..0eebe28cc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -450,7 +450,7 @@ github.com/mitchellh/go-wordwrap # github.com/mitchellh/hashstructure/v2 v2.0.2 ## explicit; go 1.14 github.com/mitchellh/hashstructure/v2 -# github.com/moby/buildkit v0.24.0-rc1 +# github.com/moby/buildkit v0.24.0-rc2 ## explicit; go 1.23.0 github.com/moby/buildkit/api/services/control github.com/moby/buildkit/api/types