mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #33696 from thaJeztah/fix-git-clone
Fix handling of remote "git@" notation
This commit is contained in:
commit
151c5ea798
4 changed files with 114 additions and 39 deletions
|
@ -15,18 +15,24 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type gitRepo struct {
|
||||
remote string
|
||||
ref string
|
||||
subdir string
|
||||
}
|
||||
|
||||
// Clone clones a repository into a newly created directory which
|
||||
// will be under "docker-build-git"
|
||||
func Clone(remoteURL string) (string, error) {
|
||||
if !urlutil.IsGitTransport(remoteURL) {
|
||||
remoteURL = "https://" + remoteURL
|
||||
}
|
||||
root, err := ioutil.TempDir("", "docker-build-git")
|
||||
repo, err := parseRemoteURL(remoteURL)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u, err := url.Parse(remoteURL)
|
||||
fetch := fetchArgs(repo.remote, repo.ref)
|
||||
|
||||
root, err := ioutil.TempDir("", "docker-build-git")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -35,22 +41,47 @@ func Clone(remoteURL string) (string, error) {
|
|||
return "", errors.Wrapf(err, "failed to init repo at %s: %s", root, out)
|
||||
}
|
||||
|
||||
ref, subdir := getRefAndSubdir(u.Fragment)
|
||||
fetch := fetchArgs(u, ref)
|
||||
|
||||
u.Fragment = ""
|
||||
|
||||
// Add origin remote for compatibility with previous implementation that
|
||||
// used "git clone" and also to make sure local refs are created for branches
|
||||
if out, err := gitWithinDir(root, "remote", "add", "origin", u.String()); err != nil {
|
||||
return "", errors.Wrapf(err, "failed add origin repo at %s: %s", u.String(), out)
|
||||
if out, err := gitWithinDir(root, "remote", "add", "origin", repo.remote); err != nil {
|
||||
return "", errors.Wrapf(err, "failed add origin repo at %s: %s", repo.remote, out)
|
||||
}
|
||||
|
||||
if output, err := gitWithinDir(root, fetch...); err != nil {
|
||||
return "", errors.Wrapf(err, "error fetching: %s", output)
|
||||
}
|
||||
|
||||
return checkoutGit(root, ref, subdir)
|
||||
return checkoutGit(root, repo.ref, repo.subdir)
|
||||
}
|
||||
|
||||
func parseRemoteURL(remoteURL string) (gitRepo, error) {
|
||||
repo := gitRepo{}
|
||||
|
||||
if !isGitTransport(remoteURL) {
|
||||
remoteURL = "https://" + remoteURL
|
||||
}
|
||||
|
||||
var fragment string
|
||||
if strings.HasPrefix(remoteURL, "git@") {
|
||||
// git@.. is not an URL, so cannot be parsed as URL
|
||||
parts := strings.SplitN(remoteURL, "#", 2)
|
||||
|
||||
repo.remote = parts[0]
|
||||
if len(parts) == 2 {
|
||||
fragment = parts[1]
|
||||
}
|
||||
repo.ref, repo.subdir = getRefAndSubdir(fragment)
|
||||
} else {
|
||||
u, err := url.Parse(remoteURL)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
repo.ref, repo.subdir = getRefAndSubdir(u.Fragment)
|
||||
u.Fragment = ""
|
||||
repo.remote = u.String()
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
func getRefAndSubdir(fragment string) (ref string, subdir string) {
|
||||
|
@ -65,11 +96,11 @@ func getRefAndSubdir(fragment string) (ref string, subdir string) {
|
|||
return
|
||||
}
|
||||
|
||||
func fetchArgs(remoteURL *url.URL, ref string) []string {
|
||||
func fetchArgs(remoteURL string, ref string) []string {
|
||||
args := []string{"fetch", "--recurse-submodules=yes"}
|
||||
shallow := true
|
||||
|
||||
if strings.HasPrefix(remoteURL.Scheme, "http") {
|
||||
if urlutil.IsURL(remoteURL) {
|
||||
res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL))
|
||||
if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" {
|
||||
shallow = false
|
||||
|
@ -120,3 +151,9 @@ func gitWithinDir(dir string, args ...string) ([]byte, error) {
|
|||
func git(args ...string) ([]byte, error) {
|
||||
return exec.Command("git", args...).CombinedOutput()
|
||||
}
|
||||
|
||||
// isGitTransport returns true if the provided str is a git transport by inspecting
|
||||
// the prefix of the string for known protocols used in git.
|
||||
func isGitTransport(str string) bool {
|
||||
return urlutil.IsURL(str) || strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "git@")
|
||||
}
|
||||
|
|
|
@ -16,6 +16,38 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseRemoteURL(t *testing.T) {
|
||||
dir, err := parseRemoteURL("git://github.com/user/repo.git")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, dir)
|
||||
assert.Equal(t, gitRepo{"git://github.com/user/repo.git", "master", ""}, dir)
|
||||
|
||||
dir, err = parseRemoteURL("git://github.com/user/repo.git#mybranch:mydir/mysubdir/")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, dir)
|
||||
assert.Equal(t, gitRepo{"git://github.com/user/repo.git", "mybranch", "mydir/mysubdir/"}, dir)
|
||||
|
||||
dir, err = parseRemoteURL("https://github.com/user/repo.git")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, dir)
|
||||
assert.Equal(t, gitRepo{"https://github.com/user/repo.git", "master", ""}, dir)
|
||||
|
||||
dir, err = parseRemoteURL("https://github.com/user/repo.git#mybranch:mydir/mysubdir/")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, dir)
|
||||
assert.Equal(t, gitRepo{"https://github.com/user/repo.git", "mybranch", "mydir/mysubdir/"}, dir)
|
||||
|
||||
dir, err = parseRemoteURL("git@github.com:user/repo.git")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, dir)
|
||||
assert.Equal(t, gitRepo{"git@github.com:user/repo.git", "master", ""}, dir)
|
||||
|
||||
dir, err = parseRemoteURL("git@github.com:user/repo.git#mybranch:mydir/mysubdir/")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, dir)
|
||||
assert.Equal(t, gitRepo{"git@github.com:user/repo.git", "mybranch", "mydir/mysubdir/"}, dir)
|
||||
}
|
||||
|
||||
func TestCloneArgsSmartHttp(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
|
@ -28,7 +60,7 @@ func TestCloneArgsSmartHttp(t *testing.T) {
|
|||
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", q))
|
||||
})
|
||||
|
||||
args := fetchArgs(serverURL, "master")
|
||||
args := fetchArgs(serverURL.String(), "master")
|
||||
exp := []string{"fetch", "--recurse-submodules=yes", "--depth", "1", "origin", "master"}
|
||||
assert.Equal(t, exp, args)
|
||||
}
|
||||
|
@ -44,14 +76,13 @@ func TestCloneArgsDumbHttp(t *testing.T) {
|
|||
w.Header().Set("Content-Type", "text/plain")
|
||||
})
|
||||
|
||||
args := fetchArgs(serverURL, "master")
|
||||
args := fetchArgs(serverURL.String(), "master")
|
||||
exp := []string{"fetch", "--recurse-submodules=yes", "origin", "master"}
|
||||
assert.Equal(t, exp, args)
|
||||
}
|
||||
|
||||
func TestCloneArgsGit(t *testing.T) {
|
||||
u, _ := url.Parse("git://github.com/docker/docker")
|
||||
args := fetchArgs(u, "master")
|
||||
args := fetchArgs("git://github.com/docker/docker", "master")
|
||||
exp := []string{"fetch", "--recurse-submodules=yes", "--depth", "1", "origin", "master"}
|
||||
assert.Equal(t, exp, args)
|
||||
}
|
||||
|
@ -178,3 +209,30 @@ func TestCheckoutGit(t *testing.T) {
|
|||
assert.Equal(t, c.exp, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidGitTransport(t *testing.T) {
|
||||
gitUrls := []string{
|
||||
"git://github.com/docker/docker",
|
||||
"git@github.com:docker/docker.git",
|
||||
"git@bitbucket.org:atlassianlabs/atlassian-docker.git",
|
||||
"https://github.com/docker/docker.git",
|
||||
"http://github.com/docker/docker.git",
|
||||
"http://github.com/docker/docker.git#branch",
|
||||
"http://github.com/docker/docker.git#:dir",
|
||||
}
|
||||
incompleteGitUrls := []string{
|
||||
"github.com/docker/docker",
|
||||
}
|
||||
|
||||
for _, url := range gitUrls {
|
||||
if !isGitTransport(url) {
|
||||
t.Fatalf("%q should be detected as valid Git prefix", url)
|
||||
}
|
||||
}
|
||||
|
||||
for _, url := range incompleteGitUrls {
|
||||
if isGitTransport(url) {
|
||||
t.Fatalf("%q should not be detected as valid Git prefix", url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,12 +29,6 @@ func IsGitURL(str string) bool {
|
|||
return checkURL(str, "git")
|
||||
}
|
||||
|
||||
// IsGitTransport returns true if the provided str is a git transport by inspecting
|
||||
// the prefix of the string for known protocols used in git.
|
||||
func IsGitTransport(str string) bool {
|
||||
return IsURL(str) || strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "git@")
|
||||
}
|
||||
|
||||
// IsTransportURL returns true if the provided str is a transport (tcp, tcp+tls, udp, unix) URL.
|
||||
func IsTransportURL(str string) bool {
|
||||
return checkURL(str, "transport")
|
||||
|
|
|
@ -27,20 +27,6 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func TestValidGitTransport(t *testing.T) {
|
||||
for _, url := range gitUrls {
|
||||
if !IsGitTransport(url) {
|
||||
t.Fatalf("%q should be detected as valid Git prefix", url)
|
||||
}
|
||||
}
|
||||
|
||||
for _, url := range incompleteGitUrls {
|
||||
if IsGitTransport(url) {
|
||||
t.Fatalf("%q should not be detected as valid Git prefix", url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsGIT(t *testing.T) {
|
||||
for _, url := range gitUrls {
|
||||
if !IsGitURL(url) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue