fix: Git ssh-scp syntax (#69)

* fix(deps): proper git ssh scheme

* feat(deps): git scp style

Adds support for the git ssh-scp style (`git@github.com:user/repo.git`),
which is an alternative form for ssh (note no ssh://).

* fix(deps): githubSlug also matching gitSSH

* test(deps): other hosts than github
This commit is contained in:
Tom 2020-01-29 16:58:25 +01:00 committed by GitHub
parent 7b8a7836a4
commit 8f14d63f95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 55 deletions

View file

@ -64,7 +64,7 @@ func TestParseDependency(t *testing.T) {
}, },
{ {
name: "SSH", name: "SSH",
path: "git+ssh://git@github.com:jsonnet-bundler/jsonnet-bundler.git", path: "git+ssh://git@github.com/jsonnet-bundler/jsonnet-bundler.git",
want: &deps.Dependency{ want: &deps.Dependency{
Source: deps.Source{ Source: deps.Source{
GitSource: &deps.Git{ GitSource: &deps.Git{

View file

@ -87,7 +87,7 @@ func (gs *Git) LegacyName() string {
} }
var gitProtoFmts = map[string]string{ var gitProtoFmts = map[string]string{
GitSchemeSSH: GitSchemeSSH + "%s:%s/%s.git", GitSchemeSSH: GitSchemeSSH + "%s/%s/%s.git",
GitSchemeHTTPS: GitSchemeHTTPS + "%s/%s/%s", GitSchemeHTTPS: GitSchemeHTTPS + "%s/%s/%s",
} }
@ -100,20 +100,18 @@ func (gs *Git) Remote() string {
// regular expressions for matching package uris // regular expressions for matching package uris
const ( const (
gitSSHExp = `git\+ssh://git@(?P<host>[^:]+):(?P<user>[^/]+)/(?P<repo>[^/]+).git` gitSSHExp = `ssh://git@(?P<host>.+)/(?P<user>.+)/(?P<repo>.+).git`
gitSCPExp = `^git@(?P<host>.+):(?P<user>.+)/(?P<repo>.+).git`
githubSlugExp = `github.com/(?P<user>[-_a-zA-Z0-9]+)/(?P<repo>[-_a-zA-Z0-9]+)` githubSlugExp = `github.com/(?P<user>[-_a-zA-Z0-9]+)/(?P<repo>[-_a-zA-Z0-9]+)`
) )
var ( var (
gitSSHRegex = regexp.MustCompile(gitSSHExp) gitSSHRegex = regexp.MustCompile(gitSSHExp)
gitSSHWithVersionRegex = regexp.MustCompile(gitSSHExp + `@(?P<version>.*)`)
gitSSHWithPathRegex = regexp.MustCompile(gitSSHExp + `/(?P<subdir>.*)`)
gitSSHWithPathAndVersionRegex = regexp.MustCompile(gitSSHExp + `/(?P<subdir>.*)@(?P<version>.*)`)
githubSlugRegex = regexp.MustCompile(githubSlugExp) githubSlugRegex = regexp.MustCompile(githubSlugExp)
githubSlugWithVersionRegex = regexp.MustCompile(githubSlugExp + `@(?P<version>.*)`)
githubSlugWithPathRegex = regexp.MustCompile(githubSlugExp + `/(?P<subdir>.*)`) VersionRegex = `@(?P<version>.*)`
githubSlugWithPathAndVersionRegex = regexp.MustCompile(githubSlugExp + `/(?P<subdir>.*)@(?P<version>.*)`) PathRegex = `/(?P<subdir>.*)`
PathAndVersionRegex = `/(?P<subdir>.*)@(?P<version>.*)`
) )
func parseGit(uri string) *Dependency { func parseGit(uri string) *Dependency {
@ -125,10 +123,16 @@ func parseGit(uri string) *Dependency {
var version string var version string
switch { switch {
case githubSlugRegex.MatchString(uri): case reMatch(gitSSHExp, uri):
gs, version = parseGitHub(uri) gs, version = match(uri, gitSSHExp)
case gitSSHRegex.MatchString(uri): gs.Scheme = GitSchemeSSH
gs, version = parseGitSSH(uri) case reMatch(gitSCPExp, uri):
gs, version = match(uri, gitSCPExp)
gs.Scheme = GitSchemeSSH
case reMatch(githubSlugExp, uri):
gs, version = match(uri, githubSlugExp)
gs.Scheme = GitSchemeHTTPS
gs.Host = "github.com"
default: default:
return nil return nil
} }
@ -144,33 +148,16 @@ func parseGit(uri string) *Dependency {
return &d return &d
} }
func parseGitSSH(p string) (gs *Git, version string) { func match(p string, exp string) (gs *Git, version string) {
gs, version = match(p, []*regexp.Regexp{
gitSSHWithPathAndVersionRegex,
gitSSHWithPathRegex,
gitSSHWithVersionRegex,
gitSSHRegex,
})
gs.Scheme = GitSchemeSSH
return gs, version
}
func parseGitHub(p string) (gs *Git, version string) {
gs, version = match(p, []*regexp.Regexp{
githubSlugWithPathAndVersionRegex,
githubSlugWithPathRegex,
githubSlugWithVersionRegex,
githubSlugRegex,
})
gs.Scheme = GitSchemeHTTPS
gs.Host = "github.com"
return gs, version
}
func match(p string, exps []*regexp.Regexp) (gs *Git, version string) {
gs = &Git{} gs = &Git{}
exps := []*regexp.Regexp{
regexp.MustCompile(exp + PathAndVersionRegex),
regexp.MustCompile(exp + PathRegex),
regexp.MustCompile(exp + VersionRegex),
regexp.MustCompile(exp),
}
for _, e := range exps { for _, e := range exps {
if !e.MatchString(p) { if !e.MatchString(p) {
continue continue
@ -190,6 +177,10 @@ func match(p string, exps []*regexp.Regexp) (gs *Git, version string) {
return gs, "" return gs, ""
} }
func reMatch(exp string, str string) bool {
return regexp.MustCompile(exp).MatchString(str)
}
func reSubMatchMap(r *regexp.Regexp, str string) map[string]string { func reSubMatchMap(r *regexp.Regexp, str string) map[string]string {
match := r.FindStringSubmatch(str) match := r.FindStringSubmatch(str)
subMatchMap := make(map[string]string) subMatchMap := make(map[string]string)

View file

@ -17,9 +17,23 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestParseGit(t *testing.T) { func TestParseGit(t *testing.T) {
sshWant := func(host string) *Dependency {
return &Dependency{
Version: "v1",
Source: Source{GitSource: &Git{
Scheme: GitSchemeSSH,
Host: host,
User: "user",
Repo: "repo",
Subdir: "/foobar",
}},
}
}
tests := []struct { tests := []struct {
name string name string
uri string uri string
@ -27,7 +41,7 @@ func TestParseGit(t *testing.T) {
wantRemote string wantRemote string
}{ }{
{ {
name: "GitHub", name: "github-slug",
uri: "github.com/ksonnet/ksonnet-lib/ksonnet.beta.3", uri: "github.com/ksonnet/ksonnet-lib/ksonnet.beta.3",
want: &Dependency{ want: &Dependency{
Version: "master", Version: "master",
@ -42,26 +56,28 @@ func TestParseGit(t *testing.T) {
wantRemote: "https://github.com/ksonnet/ksonnet-lib", wantRemote: "https://github.com/ksonnet/ksonnet-lib",
}, },
{ {
name: "SSH", name: "ssh.ssh",
uri: "git+ssh://git@my.host:user/repo.git/foobar@v1", uri: "ssh://git@example.com/user/repo.git/foobar@v1",
want: &Dependency{ want: sshWant("example.com"),
Version: "v1", wantRemote: "ssh://git@example.com/user/repo.git",
Source: Source{GitSource: &Git{
Scheme: GitSchemeSSH,
Host: "my.host",
User: "user",
Repo: "repo",
Subdir: "/foobar",
}},
}, },
wantRemote: "ssh://git@my.host:user/repo.git", {
name: "ssh.scp",
uri: "git@my.host:user/repo.git/foobar@v1",
want: sshWant("my.host"),
wantRemote: "ssh://git@my.host/user/repo.git", // want ssh format here
}, },
} }
for _, c := range tests { for _, c := range tests {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
got := Parse("", c.uri) got := Parse("", c.uri)
require.NotNilf(t, got, "parsed dependency is nil. Most likely, no regex matched the format.")
assert.Equal(t, c.want, got) assert.Equal(t, c.want, got)
require.NotNil(t, got.Source)
require.NotNil(t, got.Source.GitSource)
assert.Equal(t, c.wantRemote, got.Source.GitSource.Remote()) assert.Equal(t, c.wantRemote, got.Source.GitSource.Remote())
}) })
} }