-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git: use custom giturl type to preserve original remote
This resolves a regression introduced in 50e75e3. In this previous patch, I'd incorrectly assumed that scp-like URLs can express a subset of "standard"-URLs and so we can always safely convert them for consistency. This isn't true - the URL "[email protected]:foo" should be resolved to the home directory of the host, however, the converted URL "ssh://[email protected]/foo" will be resolved to the root of the host. To resolve this, we need to not perform this conversion. However, we also need preserve the behaviour of firm distinction between SCP and normal URL types (so as to keep proper port parsing). To do this, we add a new GitURL type to the gitutil package. This new type contains all useful fields shared in common between the standard libraries url package and our custom scp-style url parsing package. This keeps the previous property of a single clean interface to all GitURLs, while also ensuring that we preserve the original URL to pass to the Git CLI (making sure we strip fragments out, which are used as buildkit-level metadata). As a side-effect of this, the client-side calling code for parsing git urls is simplified (so we don't have to do fragment wrangling at every call point). Signed-off-by: Justin Chadwell <[email protected]>
- Loading branch information
Showing
7 changed files
with
215 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ func TestNewGitIdentifier(t *testing.T) { | |
{ | ||
url: "[email protected]:moby/buildkit.git", | ||
expected: GitIdentifier{ | ||
Remote: "ssh://[email protected]/moby/buildkit.git", | ||
Remote: "[email protected]:moby/buildkit.git", | ||
}, | ||
}, | ||
{ | ||
|
@@ -75,13 +75,13 @@ func TestNewGitIdentifier(t *testing.T) { | |
{ | ||
url: "[email protected]:user/repo.git", | ||
expected: GitIdentifier{ | ||
Remote: "ssh://[email protected]/user/repo.git", | ||
Remote: "[email protected]:user/repo.git", | ||
}, | ||
}, | ||
{ | ||
url: "[email protected]:user/repo.git#mybranch:mydir/mysubdir/", | ||
expected: GitIdentifier{ | ||
Remote: "ssh://[email protected]/user/repo.git", | ||
Remote: "[email protected]:user/repo.git", | ||
Ref: "mybranch", | ||
Subdir: "mydir/mysubdir/", | ||
}, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,21 +94,21 @@ func TestParseGitRef(t *testing.T) { | |
{ | ||
ref: "[email protected]:moby/buildkit", | ||
expected: &GitRef{ | ||
Remote: "ssh://[email protected]/moby/buildkit", | ||
Remote: "[email protected]:moby/buildkit", | ||
ShortName: "buildkit", | ||
}, | ||
}, | ||
{ | ||
ref: "[email protected]:moby/buildkit.git", | ||
expected: &GitRef{ | ||
Remote: "ssh://[email protected]/moby/buildkit.git", | ||
Remote: "[email protected]:moby/buildkit.git", | ||
ShortName: "buildkit", | ||
}, | ||
}, | ||
{ | ||
ref: "[email protected]:atlassianlabs/atlassian-docker.git", | ||
expected: &GitRef{ | ||
Remote: "ssh://[email protected]/atlassianlabs/atlassian-docker.git", | ||
Remote: "[email protected]:atlassianlabs/atlassian-docker.git", | ||
ShortName: "atlassian-docker", | ||
}, | ||
}, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,42 +30,94 @@ var supportedProtos = map[string]struct{}{ | |
|
||
var protoRegexp = regexp.MustCompile(`^[a-zA-Z0-9]+://`) | ||
|
||
// ParseURL parses a git URL and returns a parsed URL object. | ||
// URL is a custom URL type that points to a remote Git repository. | ||
// | ||
// ParseURL understands implicit ssh URLs such as "git@host:repo", and | ||
// returns the same response as if the URL were "ssh://git@host/repo". | ||
func ParseURL(remote string) (*url.URL, error) { | ||
// URLs can be parsed from both standard URLs (e.g. | ||
// "https://github.com/moby/buildkit.git"), as well as SCP-like URLs (e.g. | ||
// "[email protected]:moby/buildkit.git"). | ||
// | ||
// See https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols | ||
type GitURL struct { | ||
// Scheme is the protocol over which the git repo can be accessed | ||
Scheme string | ||
|
||
// Host is the remote host that hosts the git repo | ||
Host string | ||
// Path is the path on the host to access the repo | ||
Path string | ||
// User is the username/password to access the host | ||
User *url.Userinfo | ||
// Fragment can contain additional metadata | ||
Fragment *GitURLFragment | ||
|
||
// Remote is a valid URL remote to pass into the Git CLI tooling (i.e. | ||
// without the fragment metadata) | ||
Remote string | ||
} | ||
|
||
// GitURLFragment is the buildkit-specific metadata extracted from the fragment | ||
// of a remote URL. | ||
type GitURLFragment struct { | ||
// Ref is the git reference | ||
Ref string | ||
// Subdir is the sub-directory inside the git repository to use | ||
Subdir string | ||
} | ||
|
||
// splitGitFragment splits a git URL fragment into its respective git | ||
// reference and subdirectory components. | ||
func splitGitFragment(fragment string) *GitURLFragment { | ||
if fragment == "" { | ||
return nil | ||
} | ||
ref, subdir, _ := strings.Cut(fragment, ":") | ||
return &GitURLFragment{Ref: ref, Subdir: subdir} | ||
} | ||
|
||
// ParseURL parses a BuildKit-style Git URL (that may contain additional | ||
// fragment metadata) and returns a parsed GitURL object. | ||
func ParseURL(remote string) (*GitURL, error) { | ||
if proto := protoRegexp.FindString(remote); proto != "" { | ||
proto = strings.ToLower(strings.TrimSuffix(proto, "://")) | ||
if _, ok := supportedProtos[proto]; !ok { | ||
return nil, errors.Wrap(ErrInvalidProtocol, proto) | ||
} | ||
|
||
return url.Parse(remote) | ||
url, err := url.Parse(remote) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return fromURL(url), nil | ||
} | ||
|
||
if sshutil.IsImplicitSSHTransport(remote) { | ||
remote, fragment, _ := strings.Cut(remote, "#") | ||
remote, path, _ := strings.Cut(remote, ":") | ||
user, host, _ := strings.Cut(remote, "@") | ||
if !strings.HasPrefix(path, "/") { | ||
path = "/" + path | ||
} | ||
return &url.URL{ | ||
Scheme: SSHProtocol, | ||
User: url.User(user), | ||
Host: host, | ||
Path: path, | ||
Fragment: fragment, | ||
}, nil | ||
if url, err := sshutil.ParseSCPStyleURL(remote); err == nil { | ||
return fromSCPStyleURL(url), nil | ||
} | ||
|
||
return nil, ErrUnknownProtocol | ||
} | ||
|
||
// SplitGitFragments splits a git URL fragment into its respective git | ||
// reference and subdirectory components. | ||
func SplitGitFragment(fragment string) (ref string, subdir string) { | ||
ref, subdir, _ = strings.Cut(fragment, ":") | ||
return ref, subdir | ||
func fromURL(url *url.URL) *GitURL { | ||
withoutFragment := *url | ||
withoutFragment.Fragment = "" | ||
return &GitURL{ | ||
Scheme: url.Scheme, | ||
User: url.User, | ||
Host: url.Host, | ||
Path: url.Path, | ||
Fragment: splitGitFragment(url.Fragment), | ||
Remote: withoutFragment.String(), | ||
} | ||
} | ||
|
||
func fromSCPStyleURL(url *sshutil.SCPStyleURL) *GitURL { | ||
withoutFragment := *url | ||
withoutFragment.Fragment = "" | ||
return &GitURL{ | ||
Scheme: SSHProtocol, | ||
User: url.User, | ||
Host: url.Host, | ||
Path: url.Path, | ||
Fragment: splitGitFragment(url.Fragment), | ||
Remote: withoutFragment.String(), | ||
} | ||
} |
Oops, something went wrong.