mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Merge pull request #737 from monopole/moreGitUrlParsingCoverage
Add better error reporting to NewRepoSpecFromUrl
This commit is contained in:
@@ -46,7 +46,7 @@ func ClonerUsingGitExec(spec string) (*RepoSpec, error) {
|
|||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
gitProgram,
|
gitProgram,
|
||||||
"clone",
|
"clone",
|
||||||
repoSpec.repo,
|
repoSpec.CloneSpec(),
|
||||||
repoSpec.cloneDir.String())
|
repoSpec.cloneDir.String())
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
|
|||||||
@@ -17,27 +17,30 @@ limitations under the License.
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sigs.k8s.io/kustomize/pkg/fs"
|
"sigs.k8s.io/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RepoSpec specifies a git repo and a branch and path therein.
|
// RepoSpec specifies a git repository and a branch and path therein.
|
||||||
type RepoSpec struct {
|
type RepoSpec struct {
|
||||||
// Raw, original spec, used to look for cycles.
|
// Raw, original spec, used to look for cycles.
|
||||||
// TODO(monopole): Drop raw, use processed fields instead.
|
// TODO(monopole): Drop raw, use processed fields instead.
|
||||||
raw string
|
raw string
|
||||||
|
|
||||||
// Repo, e.g. github.com/kubernetes-sigs/kustomize
|
// Host, e.g. github.com
|
||||||
repo string
|
host string
|
||||||
|
|
||||||
// ConfirmedDir where the repo is cloned to.
|
// orgRepo name (organization/repoName),
|
||||||
|
// e.g. kubernetes-sigs/kustomize
|
||||||
|
orgRepo string
|
||||||
|
|
||||||
|
// ConfirmedDir where the orgRepo is cloned to.
|
||||||
cloneDir fs.ConfirmedDir
|
cloneDir fs.ConfirmedDir
|
||||||
|
|
||||||
// Relative path in the repo, and in the cloneDir,
|
// Relative path in the repository, and in the cloneDir,
|
||||||
// to a Kustomization.
|
// to a Kustomization.
|
||||||
path string
|
path string
|
||||||
|
|
||||||
@@ -45,6 +48,14 @@ type RepoSpec struct {
|
|||||||
ref string
|
ref string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloneSpec returns a string suitable for "git clone {spec}".
|
||||||
|
func (x *RepoSpec) CloneSpec() string {
|
||||||
|
if isAzureHost(x.host) || isAWSHost(x.host) {
|
||||||
|
return x.host + x.orgRepo
|
||||||
|
}
|
||||||
|
return x.host + x.orgRepo + gitSuffix
|
||||||
|
}
|
||||||
|
|
||||||
func (x *RepoSpec) CloneDir() fs.ConfirmedDir {
|
func (x *RepoSpec) CloneDir() fs.ConfirmedDir {
|
||||||
return x.cloneDir
|
return x.cloneDir
|
||||||
}
|
}
|
||||||
@@ -74,17 +85,23 @@ func IsRepoUrl(arg string) bool {
|
|||||||
isAzureHost(arg) || isAWSHost(arg))
|
isAzureHost(arg) || isAWSHost(arg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From strings like git@github.com:someOrg/someRepo.git or
|
||||||
|
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
||||||
|
// the parts.
|
||||||
func NewRepoSpecFromUrl(n string) (*RepoSpec, error) {
|
func NewRepoSpecFromUrl(n string) (*RepoSpec, error) {
|
||||||
host, repo, path, gitRef, err := parseGithubUrl(n)
|
if filepath.IsAbs(n) {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("uri looks like abs path: %s", n)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if isAzureHost(host) || isAWSHost(host) {
|
host, orgRepo, path, gitRef := parseGithubUrl(n)
|
||||||
repo = host + repo
|
if orgRepo == "" {
|
||||||
} else {
|
return nil, fmt.Errorf("url lacks orgRepo: %s", n)
|
||||||
repo = host + repo + ".git"
|
|
||||||
}
|
}
|
||||||
return &RepoSpec{raw: n, repo: repo, path: path, ref: gitRef}, nil
|
if host == "" {
|
||||||
|
return nil, fmt.Errorf("url lacks host: %s", n)
|
||||||
|
}
|
||||||
|
return &RepoSpec{
|
||||||
|
raw: n, host: host, orgRepo: orgRepo,
|
||||||
|
path: path, ref: gitRef}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRepoSpec(
|
func NewRepoSpec(
|
||||||
@@ -101,33 +118,29 @@ const (
|
|||||||
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
||||||
// the parts.
|
// the parts.
|
||||||
func parseGithubUrl(n string) (
|
func parseGithubUrl(n string) (
|
||||||
host string, repo string, path string, gitRef string, err error) {
|
host string, orgRepo string, path string, gitRef string) {
|
||||||
host, n = parseHostSpec(n)
|
host, n = parseHostSpec(n)
|
||||||
host = normalizeGitHostSpec(host)
|
|
||||||
|
|
||||||
if strings.HasSuffix(n, gitSuffix) {
|
|
||||||
repo = n[0 : len(n)-len(gitSuffix)]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.Contains(n, gitSuffix) {
|
if strings.Contains(n, gitSuffix) {
|
||||||
index := strings.Index(n, gitSuffix)
|
index := strings.Index(n, gitSuffix)
|
||||||
repo = n[0:index]
|
orgRepo = n[0:index]
|
||||||
n = n[index+len(gitSuffix):]
|
n = n[index+len(gitSuffix):]
|
||||||
path, gitRef = peelQuery(n)
|
path, gitRef = peelQuery(n)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
i := strings.Index(n, "/")
|
i := strings.Index(n, "/")
|
||||||
if i < 1 {
|
if i < 1 {
|
||||||
return "", "", "", "", errors.New("no separator")
|
return "", "", "", ""
|
||||||
}
|
}
|
||||||
j := strings.Index(n[i+1:], "/")
|
j := strings.Index(n[i+1:], "/")
|
||||||
if j >= 0 {
|
if j >= 0 {
|
||||||
j += i + 1
|
j += i + 1
|
||||||
repo = n[:j]
|
orgRepo = n[:j]
|
||||||
path, gitRef = peelQuery(n[j+1:])
|
path, gitRef = peelQuery(n[j+1:])
|
||||||
} else {
|
} else {
|
||||||
path = ""
|
path = ""
|
||||||
repo, gitRef = peelQuery(n)
|
orgRepo, gitRef = peelQuery(n)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -142,28 +155,45 @@ func peelQuery(arg string) (string, string) {
|
|||||||
|
|
||||||
func parseHostSpec(n string) (string, string) {
|
func parseHostSpec(n string) (string, string) {
|
||||||
var host string
|
var host string
|
||||||
|
// Start accumulating the host part.
|
||||||
for _, p := range []string{
|
for _, p := range []string{
|
||||||
// Order matters here.
|
// Order matters here.
|
||||||
"git::", "gh:", "ssh://", "https://", "http://",
|
"git::", "gh:", "ssh://", "https://", "http://",
|
||||||
"git@", "github.com:", "github.com/"} {
|
"git@", "github.com:", "github.com/"} {
|
||||||
if strings.ToLower(n[:len(p)]) == p {
|
if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p {
|
||||||
n = n[len(p):]
|
n = n[len(p):]
|
||||||
host = host + p
|
host += p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if host == "git@" {
|
||||||
|
i := strings.Index(n, "/")
|
||||||
|
if i > -1 {
|
||||||
|
host += n[:i+1]
|
||||||
|
n = n[i+1:]
|
||||||
|
} else {
|
||||||
|
i = strings.Index(n, ":")
|
||||||
|
if i > -1 {
|
||||||
|
host += n[:i+1]
|
||||||
|
n = n[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return host, n
|
||||||
|
}
|
||||||
|
|
||||||
// If host is a http(s) or ssh URL, grab the domain part.
|
// If host is a http(s) or ssh URL, grab the domain part.
|
||||||
for _, p := range []string{
|
for _, p := range []string{
|
||||||
"ssh://", "https://", "http://"} {
|
"ssh://", "https://", "http://"} {
|
||||||
if strings.HasSuffix(strings.ToLower(host), p) {
|
if strings.HasSuffix(host, p) {
|
||||||
index := regexp.MustCompile("^(.*?)/").FindStringIndex(n)
|
i := strings.Index(n, "/")
|
||||||
if len(index) > 0 {
|
if i > -1 {
|
||||||
host = host + n[0:index[len(index)-1]]
|
host = host + n[0:i+1]
|
||||||
n = n[index[len(index)-1]:]
|
n = n[i+1:]
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return host, n
|
|
||||||
|
return normalizeGitHostSpec(host), n
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeGitHostSpec(host string) string {
|
func normalizeGitHostSpec(host string) string {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package git
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,15 +102,16 @@ func TestIsRepoURL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var repoNames = []string{"someOrg/someRepo", "kubernetes/website"}
|
var orgRepos = []string{"someOrg/someRepo", "kubernetes/website"}
|
||||||
|
|
||||||
var paths = []string{"README.md", "foo/krusty.txt", ""}
|
var pathNames = []string{"README.md", "foo/krusty.txt", ""}
|
||||||
|
|
||||||
var hrefArgs = []string{"someBranch", ""}
|
var hrefArgs = []string{"someBranch", "master", "v0.1.0", ""}
|
||||||
|
|
||||||
var extractFmts = map[string]string{
|
var hostNamesRawAndNormalizedOld = map[string]string{
|
||||||
"gh:%s": "gh:",
|
"gh:%s": "gh:",
|
||||||
"GH:%s": "gh:",
|
"GH:%s": "gh:",
|
||||||
|
"git@github.com:%s": "git@github.com:",
|
||||||
"gitHub.com/%s": "https://github.com/",
|
"gitHub.com/%s": "https://github.com/",
|
||||||
"https://github.com/%s": "https://github.com/",
|
"https://github.com/%s": "https://github.com/",
|
||||||
"hTTps://github.com/%s": "https://github.com/",
|
"hTTps://github.com/%s": "https://github.com/",
|
||||||
@@ -118,40 +120,132 @@ var extractFmts = map[string]string{
|
|||||||
"git::http://git.example.com/%s": "http://git.example.com/",
|
"git::http://git.example.com/%s": "http://git.example.com/",
|
||||||
"git::https://git.example.com/%s": "https://git.example.com/",
|
"git::https://git.example.com/%s": "https://git.example.com/",
|
||||||
"ssh://git.example.com:7999/%s": "ssh://git.example.com:7999/",
|
"ssh://git.example.com:7999/%s": "ssh://git.example.com:7999/",
|
||||||
|
"https://git-codecommit.us-east-2.amazonaws.com/%s": "https://git-codecommit.us-east-2.amazonaws.com/",
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseGithubUrl(t *testing.T) {
|
var hostNamesRawAndNormalized = [][]string{
|
||||||
for _, repoName := range repoNames {
|
{"gh:", "gh:"},
|
||||||
for _, pathName := range paths {
|
{"GH:", "gh:"},
|
||||||
for extractFmt, hostSpec := range extractFmts {
|
{"gitHub.com/", "https://github.com/"},
|
||||||
|
{"github.com:", "https://github.com/"},
|
||||||
|
{"http://github.com/", "https://github.com/"},
|
||||||
|
{"https://github.com/", "https://github.com/"},
|
||||||
|
{"hTTps://github.com/", "https://github.com/"},
|
||||||
|
{"https://git-codecommit.us-east-2.amazonaws.com/", "https://git-codecommit.us-east-2.amazonaws.com/"},
|
||||||
|
{"https://fabrikops2.visualstudio.com/", "https://fabrikops2.visualstudio.com/"},
|
||||||
|
{"ssh://git.example.com:7999/", "ssh://git.example.com:7999/"},
|
||||||
|
{"git::https://gitlab.com/", "https://gitlab.com/"},
|
||||||
|
{"git::http://git.example.com/", "http://git.example.com/"},
|
||||||
|
{"git::https://git.example.com/", "https://git.example.com/"},
|
||||||
|
{"git@github.com:", "git@github.com:"},
|
||||||
|
{"git@github.com/", "git@github.com:"},
|
||||||
|
{"git@gitlab2.sqtools.ru:10022/", "git@gitlab2.sqtools.ru:10022/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUrlOld(hostFmt, orgRepo, path, href string) string {
|
||||||
|
if len(path) > 0 {
|
||||||
|
orgRepo = filepath.Join(orgRepo, path)
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf(hostFmt, orgRepo)
|
||||||
|
if href != "" {
|
||||||
|
url += refQuery + href
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUrl(hostFmt, orgRepo, path, href string) string {
|
||||||
|
if len(path) > 0 {
|
||||||
|
orgRepo = filepath.Join(orgRepo, path)
|
||||||
|
}
|
||||||
|
url := hostFmt + orgRepo
|
||||||
|
if href != "" {
|
||||||
|
url += refQuery + href
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRepoSpecFromUrl(t *testing.T) {
|
||||||
|
var bad [][]string
|
||||||
|
for _, tuple := range hostNamesRawAndNormalized {
|
||||||
|
hostRaw := tuple[0]
|
||||||
|
hostSpec := tuple[1]
|
||||||
|
for _, orgRepo := range orgRepos {
|
||||||
|
for _, pathName := range pathNames {
|
||||||
for _, hrefArg := range hrefArgs {
|
for _, hrefArg := range hrefArgs {
|
||||||
spec := repoName
|
uri := makeUrl(hostRaw, orgRepo, pathName, hrefArg)
|
||||||
if len(pathName) > 0 {
|
rs, err := NewRepoSpecFromUrl(uri)
|
||||||
spec = filepath.Join(spec, pathName)
|
|
||||||
}
|
|
||||||
input := fmt.Sprintf(extractFmt, spec)
|
|
||||||
if hrefArg != "" {
|
|
||||||
input = input + refQuery + hrefArg
|
|
||||||
}
|
|
||||||
if !IsRepoUrl(input) {
|
|
||||||
t.Errorf("Should smell like github arg: %s\n", input)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
host, repo, path, gitRef, err := parseGithubUrl(input)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("problem %v", err)
|
t.Errorf("problem %v", err)
|
||||||
}
|
}
|
||||||
|
if rs.host != hostSpec {
|
||||||
|
bad = append(bad, []string{"host", uri, rs.host, hostSpec})
|
||||||
|
}
|
||||||
|
if rs.orgRepo != orgRepo {
|
||||||
|
bad = append(bad, []string{"orgRepo", uri, rs.orgRepo, orgRepo})
|
||||||
|
}
|
||||||
|
if rs.path != pathName {
|
||||||
|
bad = append(bad, []string{"path", uri, rs.path, pathName})
|
||||||
|
}
|
||||||
|
if rs.ref != hrefArg {
|
||||||
|
bad = append(bad, []string{"ref", uri, rs.ref, hrefArg})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bad) > 0 {
|
||||||
|
for _, tuple := range bad {
|
||||||
|
fmt.Printf("\n"+
|
||||||
|
" from uri: %s\n"+
|
||||||
|
" actual %4s: %s\n"+
|
||||||
|
"expected %4s: %s\n",
|
||||||
|
tuple[1], tuple[0], tuple[2], tuple[0], tuple[3])
|
||||||
|
}
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var badData = [][]string{
|
||||||
|
{"/tmp", "uri looks like abs path"},
|
||||||
|
{"iauhsdiuashduas", "url lacks orgRepo"},
|
||||||
|
{"htxxxtp://github.com/", "url lacks host"},
|
||||||
|
{"ssh://git.example.com", "url lacks orgRepo"},
|
||||||
|
{"git::___", "url lacks orgRepo"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRepoSpecFromUrlErrors(t *testing.T) {
|
||||||
|
for _, tuple := range badData {
|
||||||
|
_, err := NewRepoSpecFromUrl(tuple[0])
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), tuple[1]) {
|
||||||
|
t.Errorf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseGithubUrl(t *testing.T) {
|
||||||
|
for hostRawFmt, hostSpec := range hostNamesRawAndNormalizedOld {
|
||||||
|
for _, orgRepo := range orgRepos {
|
||||||
|
for _, pathName := range pathNames {
|
||||||
|
for _, hrefArg := range hrefArgs {
|
||||||
|
input := makeUrlOld(hostRawFmt, orgRepo, pathName, hrefArg)
|
||||||
|
if !IsRepoUrl(input) {
|
||||||
|
t.Errorf("Should smell like github arg: %s\n", input)
|
||||||
|
}
|
||||||
|
host, repo, path, gitRef := parseGithubUrl(input)
|
||||||
if host != hostSpec {
|
if host != hostSpec {
|
||||||
t.Errorf("\n"+
|
t.Errorf("\n"+
|
||||||
" from %s\n"+
|
" from %s\n"+
|
||||||
" actual host %s\n"+
|
" actual host %s\n"+
|
||||||
"expected host %s\n", input, host, hostSpec)
|
"expected host %s\n", input, host, hostSpec)
|
||||||
}
|
}
|
||||||
if repo != repoName {
|
if repo != orgRepo {
|
||||||
t.Errorf("\n"+
|
t.Errorf("\n"+
|
||||||
" from %s\n"+
|
" from %s\n"+
|
||||||
" actual Repo %s\n"+
|
" actual Repo %s\n"+
|
||||||
"expected Repo %s\n", input, repo, repoName)
|
"expected Repo %s\n", input, repo, orgRepo)
|
||||||
}
|
}
|
||||||
if path != pathName {
|
if path != pathName {
|
||||||
t.Errorf("\n"+
|
t.Errorf("\n"+
|
||||||
@@ -171,7 +265,7 @@ func TestParseGithubUrl(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRepoSpecFromUrl(t *testing.T) {
|
func TestNewRepoSpecFromUrlOld(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
input string
|
input string
|
||||||
repo string
|
repo string
|
||||||
@@ -220,9 +314,9 @@ func TestNewRepoSpecFromUrl(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if rs.repo != testcase.repo {
|
if rs.CloneSpec() != testcase.repo {
|
||||||
t.Errorf("repo expected to be %v, but got %v on %s",
|
t.Errorf("CloneSpec expected to be %v, but got %v on %s",
|
||||||
testcase.repo, rs.repo, testcase.input)
|
testcase.repo, rs.CloneSpec(), testcase.input)
|
||||||
}
|
}
|
||||||
if rs.path != testcase.path {
|
if rs.path != testcase.path {
|
||||||
t.Errorf("path expected to be %v, but got %v on %s",
|
t.Errorf("path expected to be %v, but got %v on %s",
|
||||||
|
|||||||
Reference in New Issue
Block a user