Merge pull request #100 from andrewhsu/fix-build-git

[17.06] Fix handling of remote "git@" notation
This commit is contained in:
Andrew Hsu
2017-07-12 14:43:27 -07:00
committed by GitHub
2 changed files with 84 additions and 19 deletions
+46 -15
View File
@@ -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 !urlutil.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
@@ -12,8 +12,43 @@ import (
"runtime"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"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)
@@ -26,7 +61,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"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)
@@ -44,7 +79,7 @@ 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"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)
@@ -52,8 +87,7 @@ func TestCloneArgsDumbHttp(t *testing.T) {
}
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"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)