diff --git a/cmd/eagle/internal/base/vcs_url.go b/cmd/eagle/internal/base/vcs_url.go new file mode 100644 index 000000000..6d3aef12a --- /dev/null +++ b/cmd/eagle/internal/base/vcs_url.go @@ -0,0 +1,58 @@ +package base + +import ( + "errors" + "net/url" + "regexp" + "strings" +) + +var ( + scpSyntaxRe = regexp.MustCompile(`^(\w+)@([\w.-]+):(.*)$`) + scheme = []string{"git", "https", "http", "git+ssh", "ssh", "file", "ftp", "ftps"} +) + +// ParseVCSUrl ref https://github.com/golang/go/blob/master/src/cmd/go/internal/vcs/vcs.go +// see https://go-review.googlesource.com/c/go/+/12226/ +// git url define https://git-scm.com/docs/git-clone#_git_urls +func ParseVCSUrl(repo string) (*url.URL, error) { + var ( + repoURL *url.URL + err error + ) + + if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil { + // Match SCP-like syntax and convert it to a URL. + // Eg, "git@github.com:user/repo" becomes + // "ssh://git@github.com/user/repo". + repoURL = &url.URL{ + Scheme: "ssh", + User: url.User(m[1]), + Host: m[2], + Path: m[3], + } + } else { + if !strings.Contains(repo, "//") { + repo = "//" + repo + } + if strings.HasPrefix(repo, "//git@") { + repo = "ssh:" + repo + } else if strings.HasPrefix(repo, "//") { + repo = "https:" + repo + } + repoURL, err = url.Parse(repo) + if err != nil { + return nil, err + } + } + + // Iterate over insecure schemes too, because this function simply + // reports the state of the repo. If we can't see insecure schemes then + // we can't report the actual repo URL. + for _, s := range scheme { + if repoURL.Scheme == s { + return repoURL, nil + } + } + return nil, errors.New("unable to parse repo url") +} diff --git a/cmd/eagle/internal/base/vcs_url_test.go b/cmd/eagle/internal/base/vcs_url_test.go new file mode 100644 index 000000000..621eb9d6e --- /dev/null +++ b/cmd/eagle/internal/base/vcs_url_test.go @@ -0,0 +1,55 @@ +package base + +import ( + "net" + "strings" + "testing" +) + +func TestParseVCSUrl(t *testing.T) { + repos := []string{ + // ssh://[user@]host.xz[:port]/path/to/repo.git/ + "ssh://git@github.com:7875/go-eagle/eagle.git", + // git://host.xz[:port]/path/to/repo.git/ + "git://github.com:7875/go-eagle/eagle.git", + // http[s]://host.xz[:port]/path/to/repo.git/ + "https://github.com:7875/go-eagle/eagle.git", + // ftp[s]://host.xz[:port]/path/to/repo.git/ + "ftps://github.com:7875/go-eagle/eagle.git", + //[user@]host.xz:path/to/repo.git/ + "git@github.com:go-eagle/eagle.git", + // ssh://[user@]host.xz[:port]/~[user]/path/to/repo.git/ + "ssh://git@github.com:7875/go-eagle/eagle.git", + // git://host.xz[:port]/~[user]/path/to/repo.git/ + "git://github.com:7875/go-eagle/eagle.git", + //[user@]host.xz:/~[user]/path/to/repo.git/ + "git@github.com:go-eagle/eagle.git", + ///path/to/repo.git/ + "~/go-eagle/eagle.git", + // file:///path/to/repo.git/ + "file://~/go-eagle/eagle.git", + } + for _, repo := range repos { + url, err := ParseVCSUrl(repo) + if err != nil { + t.Fatal(repo, err) + } + urlPath := strings.TrimLeft(url.Path, "/") + if urlPath != "go-eagle/eagle.git" { + t.Fatal(repo, "parse url failed", urlPath) + } + } +} + +func TestParseSsh(t *testing.T) { + repo := "ssh://git@github.com:7875/go-eagle/eagle.git" + url, err := ParseVCSUrl(repo) + if err != nil { + t.Fatal(err) + } + host, _, err := net.SplitHostPort(url.Host) + if err != nil { + host = url.Host + } + t.Log(host, url.Path) +}