vendor: update buildkit to v0.24.0-rc2

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2025-08-29 14:49:16 -07:00
parent ce3592e4ab
commit 1f39ad2001
14 changed files with 284 additions and 74 deletions
+50 -11
View File
@@ -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 {
+125 -17
View File
@@ -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
}
+27 -13
View File
@@ -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) {
+8 -2
View File
@@ -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], "//")
+1
View File
@@ -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"
+15 -8
View File
@@ -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,
+21 -8
View File
@@ -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
}
+26 -8
View File
@@ -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
}