From 02e32a5543b5775e4eda570a935c774869ecc2a2 Mon Sep 17 00:00:00 2001 From: Grzegorz Dlugoszewski Date: Fri, 22 May 2020 08:52:08 +0200 Subject: [PATCH] Handle scp-like syntax in URL parsing, add more tests --- pkg/url.go | 49 +++++++++++++++++++++++++++++++++++++++---------- pkg/url_test.go | 39 ++++++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/pkg/url.go b/pkg/url.go index fce8980..f98824c 100644 --- a/pkg/url.go +++ b/pkg/url.go @@ -1,28 +1,57 @@ package pkg import ( - "net/url" + urlpkg "net/url" "path" + "regexp" "strings" "github.com/pkg/errors" ) -// TODO: maybe use https://github.com/whilp/git-urls? +// scpSyntax matches the SCP-like addresses used by the ssh procotol (eg, [user@]host.xz:path/to/repo.git/). +// See: https://golang.org/src/cmd/go/internal/get/vcs.go +var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) func URLToPath(rawurl string) (string, error) { - parsed, err := url.Parse(rawurl) + url, err := parseURL(rawurl) if err != nil { - return "", errors.Wrap(err, "Failed parsing URL") + return "", err } - repoHost := strings.Split(parsed.Host, ":")[0] + // remove port numbers from host + repoHost := strings.Split(url.Host, ":")[0] - repoPath := parsed.Path - //repoPath = strings.TrimSuffix(repoPath, ".git/") - //repoPath = strings.TrimSuffix(repoPath, ".git") - - localPath := path.Join(repoHost, repoPath) + // remove trailing ".git" from repo name + localPath := path.Join(repoHost, url.Path) localPath = strings.TrimSuffix(localPath, ".git") + + // remove tilde (~) char from username + localPath = strings.ReplaceAll(localPath, "~", "") + return localPath, nil } + +func parseURL(rawurl string) (url *urlpkg.URL, err error) { + // If rawurl matches SCP-like syntax, convert it into a URL. + // eg, "git@github.com:user/repo" becomes "ssh://git@github.com/user/repo". + if m := scpSyntax.FindStringSubmatch(rawurl); m != nil { + url = &urlpkg.URL{ + Scheme: "ssh", + User: urlpkg.User(m[1]), + Host: m[2], + Path: m[3], + } + } else { + url, err = urlpkg.Parse(rawurl) + if err != nil { + return nil, errors.Wrap(err, "Failed parsing URL") + } + } + + if url.Host == "" && url.Path == "" { + return nil, errors.New("Parsed URL is empty") + } + + return url, nil +} diff --git a/pkg/url_test.go b/pkg/url_test.go index a283cb0..7b5d706 100644 --- a/pkg/url_test.go +++ b/pkg/url_test.go @@ -19,18 +19,15 @@ func TestURLParse(t *testing.T) { in string want string }{ - {"ssh://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, {"ssh://user@github.com/grdl/git-get.git", "github.com/grdl/git-get"}, - {"ssh://user@github.com/~user/grdl/git-get.git", "github.com/~user/grdl/git-get"}, // TODO: is this what we want or should the result be github.com/grdl/git-get? + {"ssh://user@github.com:1234/grdl/git-get.git", "github.com/grdl/git-get"}, + {"ssh://user@github.com/~user/grdl/git-get.git", "github.com/user/grdl/git-get"}, + {"git+ssh://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, {"git@github.com:grdl/git-get.git", "github.com/grdl/git-get"}, - {"git@github.com:/~user/grdl/git-get.git", "github.com/grdl/git-get"}, - - {"https://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, - {"https://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, - {"https://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, - {"https://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, - + {"git@github.com:/~user/grdl/git-get.git", "github.com/user/grdl/git-get"}, + {"git://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, + {"git://github.com/~user/grdl/git-get.git", "github.com/user/grdl/git-get"}, {"https://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, {"http://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, {"https://github.com/grdl/git-get", "github.com/grdl/git-get"}, @@ -42,12 +39,11 @@ func TestURLParse(t *testing.T) { {"https://github.com/grdl/git-get/", "github.com/grdl/git-get"}, {"https://github.com/grdl/git-get/////", "github.com/grdl/git-get"}, {"https://github.com/grdl/git-get.git/////", "github.com/grdl/git-get"}, - + {"ftp://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, + {"ftps://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, + {"rsync://github.com/grdl/git-get.git", "github.com/grdl/git-get"}, {"local/grdl/git-get/", "local/grdl/git-get"}, {"file://local/grdl/git-get", "local/grdl/git-get"}, - {"https://github.com/grdl/git-get/////", "github.com/grdl/git-get"}, - {"https://github.com/grdl/git-get/////", "github.com/grdl/git-get"}, - {"https://github.com/grdl/git-get/////", "github.com/grdl/git-get"}, } for _, test := range tests { @@ -61,3 +57,20 @@ func TestURLParse(t *testing.T) { } } } + +func TestInvalidURLParse(t *testing.T) { + invalidURLs := []string{ + "", + //TODO: This URL is technically a correct scp-like syntax. Not sure how to handle it + "github.com:grdl/git-git.get.git", + "git@github.com:1234:grdl/git-get.git", + } + + for _, url := range invalidURLs { + got, err := URLToPath(url) + if err == nil { + t.Errorf("Wrong result of parsing invalid URL: %s, got: %s, want: error", url, got) + } + } + +}