Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 43 additions & 12 deletions client/llb/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,45 +249,62 @@ const (
//
// 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].
//
// Git is planned to be deprecated. Use [Git2] instead whenever possible.
func Git(url, ref string, opts ...GitOption) State {
// The logic is complicated for compatibility reason.
remote, err := gitutil.ParseURL(url)
if errors.Is(err, gitutil.ErrUnknownProtocol) {
url = "https://" + url
remote, err = gitutil.ParseURL(url)
}
if remote != nil {
url = remote.Remote
if remote.Opts != nil {
opts = append(opts, GitChecksum(remote.Opts.Checksum))
}
}
if url != "" {
opts = append(opts, GitFullURL(url))
}

var id string
if err != nil {
// If we can't parse the URL, just use the full URL as the ID. The git
// operation will fail later on.
id = url
} else {
// We construct the ID manually here, so that we can create the same ID
// 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
}
return git(url, nil, opts...)
}

opts = append(opts, GitIDSuffix(ref))
return Git2(remote.GitURLBase, opts...)
}

// Git2 is similar to Git but takes [gitutil.GitURLBase] as the argument.
func Git2(remote gitutil.GitURLBase, opts ...GitOption) State {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want to go with numeric functions in here. GitFromRemote ?

// We construct the ID manually here, so that we can create the same ID
// for different protocols (e.g. https and ssh) that have the same
// host/path/fragment combination.
id := remote.Host + path.Join("/", remote.Path)
return git(id, &remote, opts...)
}

func git(id string, remote *gitutil.GitURLBase, opts ...GitOption) State {
gi := &GitInfo{
AuthHeaderSecret: GitAuthHeaderKey,
AuthTokenSecret: GitAuthTokenKey,
}
for _, o := range opts {
o.SetGitOption(gi)
}
if gi.IDSuffix != "" {
id += "#" + gi.IDSuffix
}
attrs := map[string]string{}
if gi.KeepGitDir {
attrs[pb.AttrKeepGitDir] = "true"
addCap(&gi.Constraints, pb.CapSourceGitKeepDir)
}
if url != "" {
attrs[pb.AttrFullRemoteURL] = url
if gi.FullURL != "" {
attrs[pb.AttrFullRemoteURL] = gi.FullURL
addCap(&gi.Constraints, pb.CapSourceGitFullURL)
}
if gi.AuthTokenSecret != "" {
Expand Down Expand Up @@ -352,6 +369,8 @@ type GitInfo struct {
KnownSSHHosts string
MountSSHSock string
Checksum string
FullURL string
IDSuffix string // Appended after '#'. Exists only for sake of compatibility.
}

func KeepGitDir() GitOption {
Expand Down Expand Up @@ -386,6 +405,18 @@ func GitChecksum(v string) GitOption {
})
}

func GitFullURL(v string) GitOption {
return gitOptionFunc(func(gi *GitInfo) {
gi.FullURL = v
})
}

func GitIDSuffix(v string) GitOption {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to make this public if only used internally. Same for prop.

return gitOptionFunc(func(gi *GitInfo) {
gi.IDSuffix = v
})
}

// AuthOption can be used with either HTTP or Git sources.
type AuthOption interface {
GitOption
Expand Down
18 changes: 16 additions & 2 deletions frontend/dockerfile/dfgitutil/git_ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type GitRef struct {
// Commit is optional.
Commit string

// Checksum verifies the Commit if specified.
// Checksum is optional.
Checksum string

// SubDir is a directory path inside the repo.
// SubDir is optional.
SubDir string
Expand All @@ -46,6 +50,12 @@ type GitRef struct {
// Discouraged, although not deprecated.
// Instead, consider using an encrypted TCP connection such as "[email protected]/foo/bar.git" or "https://github.com/foo/bar.git".
UnencryptedTCP bool

gitURL *gitutil.GitURL
}

func (r *GitRef) GitURL() *gitutil.GitURL {
return r.gitURL
}

// var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
Expand All @@ -63,11 +73,14 @@ func ParseGitRef(ref string) (*GitRef, error) {
return nil, cerrdefs.ErrInvalidArgument
} else if strings.HasPrefix(ref, "github.com/") {
res.IndistinguishableFromLocal = true // Deprecated
remote = gitutil.FromURL(&url.URL{
remote, err = gitutil.FromURL(&url.URL{
Scheme: "https",
Host: "github.com",
Path: strings.TrimPrefix(ref, "github.com/"),
})
if err != nil {
return nil, err
}
} else {
remote, err = gitutil.ParseURL(ref)
if errors.Is(err, gitutil.ErrUnknownProtocol) {
Expand Down Expand Up @@ -96,11 +109,12 @@ 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.Commit, res.Checksum, res.SubDir = remote.Opts.Ref, remote.Opts.Checksum, remote.Opts.Subdir
}

repoSplitBySlash := strings.Split(res.Remote, "/")
res.ShortName = strings.TrimSuffix(repoSplitBySlash[len(repoSplitBySlash)-1], ".git")
res.gitURL = remote

return res, nil
}
11 changes: 10 additions & 1 deletion frontend/dockerfile/dfgitutil/git_ref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ func TestParseGitRef(t *testing.T) {
ref: ".git",
expected: nil,
},
{
ref: "https://github.com/docker/docker.git?ref=v1.0.0&subdir=/subdir",
expected: &GitRef{
Remote: "https://github.com/docker/docker.git",
ShortName: "docker",
Commit: "v1.0.0",
SubDir: "/subdir",
},
},
}
for _, tt := range cases {
t.Run(tt.ref, func(t *testing.T) {
Expand All @@ -147,7 +156,7 @@ func TestParseGitRef(t *testing.T) {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.expected, got)
require.EqualExportedValues(t, tt.expected, got)
}
})
}
Expand Down
Loading