diff --git a/cmd/commands/app.go b/cmd/commands/app.go index 4106c0e7..88c7d91b 100644 --- a/cmd/commands/app.go +++ b/cmd/commands/app.go @@ -170,9 +170,7 @@ func setAppOptsDefaults(ctx context.Context, repofs fs.FS, opts *AppCreateOption // local directory fsys = fs.Create(osfs.New(opts.AppOpts.AppSpecifier)) } else { - host, orgRepo, p, _, _, _, _ := util.ParseGitUrl(opts.AppOpts.AppSpecifier) - url := host + orgRepo - log.G().Infof("cloning repo: '%s', to infer app type from path '%s'", url, p) + log.G().Infof("trying to infer application type from '%s'", opts.AppOpts.AppSpecifier) cloneOpts := &git.CloneOptions{ Repo: opts.AppOpts.AppSpecifier, Auth: opts.CloneOpts.Auth, diff --git a/cmd/commands/repo.go b/cmd/commands/repo.go index 682a6207..d74d5d89 100644 --- a/cmd/commands/repo.go +++ b/cmd/commands/repo.go @@ -165,7 +165,7 @@ func NewRepoBootstrapCommand() *cobra.Command { repo bootstrap --repo https://github.com/example/repo/path/to/installation_root `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() }, RunE: func(cmd *cobra.Command, args []string) error { return RunRepoBootstrap(cmd.Context(), &RepoBootstrapOptions{ AppSpecifier: appSpecifier, @@ -264,6 +264,7 @@ func RunRepoCreate(ctx context.Context, opts *RepoCreateOptions) (*git.CloneOpti Password: opts.Token, }, } + co.Parse() return co, nil } diff --git a/cmd/commands/repo_test.go b/cmd/commands/repo_test.go index 11d97e5a..cce3c224 100644 --- a/cmd/commands/repo_test.go +++ b/cmd/commands/repo_test.go @@ -56,7 +56,7 @@ func TestRunRepoCreate(t *testing.T) { Repo: "https://github.com/owner/name/path?ref=revision", } expected.Parse() - assert.Equal(t, "https://github.com/owner/name", cloneOpts.URL()) + assert.Equal(t, "https://github.com/owner/name.git", cloneOpts.URL()) assert.Equal(t, "revision", cloneOpts.Revision()) assert.Equal(t, "path", cloneOpts.Path()) mp.AssertCalled(t, "CreateRepository", mock.Anything, mock.Anything) @@ -247,7 +247,7 @@ func Test_buildBootstrapManifests(t *testing.T) { argocdApp := &argocdv1alpha1.Application{} assert.NoError(t, yaml.Unmarshal(b.argocdApp, argocdApp)) - assert.Equal(t, "https://github.com/foo/bar", argocdApp.Spec.Source.RepoURL) + assert.Equal(t, "https://github.com/foo/bar.git", argocdApp.Spec.Source.RepoURL) assert.Equal(t, filepath.Join("installation1", store.Default.BootsrtrapDir, store.Default.ArgoCDName), argocdApp.Spec.Source.Path) assert.Equal(t, "main", argocdApp.Spec.Source.TargetRevision) assert.Equal(t, 0, len(argocdApp.ObjectMeta.Finalizers)) @@ -256,7 +256,7 @@ func Test_buildBootstrapManifests(t *testing.T) { bootstrapApp := &argocdv1alpha1.Application{} assert.NoError(t, yaml.Unmarshal(b.bootstrapApp, bootstrapApp)) - assert.Equal(t, "https://github.com/foo/bar", bootstrapApp.Spec.Source.RepoURL) + assert.Equal(t, "https://github.com/foo/bar.git", bootstrapApp.Spec.Source.RepoURL) assert.Equal(t, filepath.Join("installation1", store.Default.BootsrtrapDir), bootstrapApp.Spec.Source.Path) assert.Equal(t, "main", bootstrapApp.Spec.Source.TargetRevision) assert.NotEqual(t, 0, len(bootstrapApp.ObjectMeta.Finalizers)) @@ -265,7 +265,7 @@ func Test_buildBootstrapManifests(t *testing.T) { rootApp := &argocdv1alpha1.Application{} assert.NoError(t, yaml.Unmarshal(b.rootApp, rootApp)) - assert.Equal(t, "https://github.com/foo/bar", rootApp.Spec.Source.RepoURL) + assert.Equal(t, "https://github.com/foo/bar.git", rootApp.Spec.Source.RepoURL) assert.Equal(t, filepath.Join("installation1", store.Default.ProjectsDir), rootApp.Spec.Source.Path) assert.Equal(t, "main", rootApp.Spec.Source.TargetRevision) assert.NotEqual(t, 0, len(rootApp.ObjectMeta.Finalizers)) @@ -296,7 +296,6 @@ func Test_buildBootstrapManifests(t *testing.T) { for tname, tt := range tests { t.Run(tname, func(t *testing.T) { tt.args.cloneOpts.Parse() - b, ret := buildBootstrapManifests( tt.args.namespace, tt.args.appSpecifier, diff --git a/pkg/application/application.go b/pkg/application/application.go index 55de787c..10a35e9a 100644 --- a/pkg/application/application.go +++ b/pkg/application/application.go @@ -10,10 +10,10 @@ import ( "reflect" "github.com/argoproj-labs/argocd-autopilot/pkg/fs" + "github.com/argoproj-labs/argocd-autopilot/pkg/git" "github.com/argoproj-labs/argocd-autopilot/pkg/kube" "github.com/argoproj-labs/argocd-autopilot/pkg/log" "github.com/argoproj-labs/argocd-autopilot/pkg/store" - "github.com/argoproj-labs/argocd-autopilot/pkg/util" "github.com/ghodss/yaml" billyUtils "github.com/go-git/go-billy/v5/util" @@ -388,17 +388,18 @@ func newDirApp(opts *CreateOptions) *dirApp { app := &dirApp{ baseApp: baseApp{opts}, } - - host, orgRepo, path, gitRef, _, _, _ := util.ParseGitUrl(opts.AppSpecifier) - url := host + orgRepo + cloneOpts := &git.CloneOptions{ + Repo: opts.AppSpecifier, + } + cloneOpts.Parse() app.config = &Config{ AppName: opts.AppName, UserGivenName: opts.AppName, DestNamespace: opts.DestNamespace, DestServer: opts.DestServer, - SrcRepoURL: url, - SrcPath: path, - SrcTargetRevision: gitRef, + SrcRepoURL: cloneOpts.URL(), + SrcPath: cloneOpts.Path(), + SrcTargetRevision: cloneOpts.Revision(), } return app diff --git a/pkg/application/application_test.go b/pkg/application/application_test.go index 28ac0dad..d00718e7 100644 --- a/pkg/application/application_test.go +++ b/pkg/application/application_test.go @@ -605,7 +605,7 @@ func Test_newDirApp(t *testing.T) { UserGivenName: "fooapp", DestNamespace: "fizz", DestServer: "buzz", - SrcRepoURL: "https://github.com/foo/bar", + SrcRepoURL: "https://github.com/foo/bar.git", SrcTargetRevision: "v0.1.2", SrcPath: "somepath/in/repo", }, diff --git a/pkg/git/repository.go b/pkg/git/repository.go index 9fe35219..7d3dc6f7 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "net/url" "os" "strings" @@ -57,6 +58,11 @@ type ( } ) +const ( + gitSuffix = ".git" + gitDelimiter = "_git/" +) + // Errors var ( ErrNilOpts = errors.New("options cannot be nil") @@ -113,10 +119,11 @@ func (o *CloneOptions) Parse() { var ( host string orgRepo string + suffix string ) - host, orgRepo, o.path, o.revision, _, _, _ = util.ParseGitUrl(o.Repo) - o.url = host + orgRepo + host, orgRepo, o.path, o.revision, suffix = parseGitUrl(o.Repo) + o.url = host + orgRepo + suffix } func (o *CloneOptions) Clone(ctx context.Context) (Repository, fs.FS, error) { @@ -153,7 +160,7 @@ func (o *CloneOptions) URL() string { } func (o *CloneOptions) Revision() string { - return o.revision + return plumbing.ReferenceName(o.revision).Short() } func (o *CloneOptions) Path() string { @@ -208,7 +215,7 @@ var clone = func(ctx context.Context, opts *CloneOptions) (*repo, error) { } if opts.revision != "" { - cloneOpts.ReferenceName = plumbing.NewBranchReferenceName(opts.revision) + cloneOpts.ReferenceName = plumbing.ReferenceName(opts.revision) } log.G(ctx).WithFields(log.Fields{ @@ -280,3 +287,132 @@ func getAuth(auth Auth) transport.AuthMethod { Password: auth.Password, } } + +// From strings like git@github.com:someOrg/someRepo.git or +// https://github.com/someOrg/someRepo?ref=someHash, extract +// the parts. +func parseGitUrl(n string) (host, orgRepo, path, ref, gitSuff string) { + if strings.Contains(n, gitDelimiter) { + index := strings.Index(n, gitDelimiter) + // Adding _git/ to host + host = normalizeGitHostSpec(n[:index+len(gitDelimiter)]) + orgRepo = strings.Split(strings.Split(n[index+len(gitDelimiter):], "/")[0], "?")[0] + path, ref = peelQuery(n[index+len(gitDelimiter)+len(orgRepo):]) + return + } + + host, n = parseHostSpec(n) + gitSuff = gitSuffix + if strings.Contains(n, gitSuffix) { + index := strings.Index(n, gitSuffix) + orgRepo = n[0:index] + n = n[index+len(gitSuffix):] + path, ref = peelQuery(n) + return + } + + i := strings.Index(n, "/") + if i < 1 { + path, ref = peelQuery(n) + return + } + + j := strings.Index(n[i+1:], "/") + if j >= 0 { + j += i + 1 + orgRepo = n[:j] + path, ref = peelQuery(n[j+1:]) + return + } + + path = "" + orgRepo, ref = peelQuery(n) + return +} + +func peelQuery(arg string) (path, ref string) { + parsed, err := url.Parse(arg) + if err != nil { + return path, "" + } + + path = parsed.Path + values := parsed.Query() + branch := values.Get("ref") + tag := values.Get("tag") + sha := values.Get("sha") + if sha != "" { + ref = sha + return + } + + if tag != "" { + ref = "refs/tags/" + tag + return + } + + if branch != "" { + ref = "refs/heads/" + branch + return + } + + return +} + +func parseHostSpec(n string) (string, string) { + var host string + // Start accumulating the host part. + for _, p := range [...]string{ + // Order matters here. + "git::", "gh:", "ssh://", "https://", "http://", + "git@", "github.com:", "github.com/"} { + if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p { + n = n[len(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. + for _, p := range [...]string{ + "ssh://", "https://", "http://"} { + if strings.HasSuffix(host, p) { + i := strings.Index(n, "/") + if i > -1 { + host = host + n[0:i+1] + n = n[i+1:] + } + break + } + } + + return normalizeGitHostSpec(host), n +} + +func normalizeGitHostSpec(host string) string { + s := strings.ToLower(host) + if strings.Contains(s, "github.com") { + if strings.Contains(s, "git@") || strings.Contains(s, "ssh:") { + host = "git@github.com:" + } else { + host = "https://github.com/" + } + } + if strings.HasPrefix(s, "git::") { + host = strings.TrimPrefix(s, "git::") + } + return host +} diff --git a/pkg/git/repository_test.go b/pkg/git/repository_test.go index 6e0d734d..c7a718c6 100644 --- a/pkg/git/repository_test.go +++ b/pkg/git/repository_test.go @@ -247,7 +247,7 @@ func Test_clone(t *testing.T) { Repo: "https://github.com/owner/name", }, expectedOpts: &gg.CloneOptions{ - URL: "https://github.com/owner/name", + URL: "https://github.com/owner/name.git", Auth: nil, Depth: 1, Progress: os.Stderr, @@ -266,7 +266,7 @@ func Test_clone(t *testing.T) { }, }, expectedOpts: &gg.CloneOptions{ - URL: "https://github.com/owner/name", + URL: "https://github.com/owner/name.git", Auth: &http.BasicAuth{ Username: "asd", Password: "123", @@ -284,7 +284,7 @@ func Test_clone(t *testing.T) { Repo: "https://github.com/owner/name", }, expectedOpts: &gg.CloneOptions{ - URL: "https://github.com/owner/name", + URL: "https://github.com/owner/name.git", Depth: 1, Progress: os.Stderr, Tags: gg.NoTags, @@ -300,7 +300,7 @@ func Test_clone(t *testing.T) { Repo: "https://github.com/owner/name?ref=test", }, expectedOpts: &gg.CloneOptions{ - URL: "https://github.com/owner/name", + URL: "https://github.com/owner/name.git", Depth: 1, Progress: os.Stderr, Tags: gg.NoTags, diff --git a/pkg/git/repospec_test.go b/pkg/git/repospec_test.go new file mode 100644 index 00000000..53f8678c --- /dev/null +++ b/pkg/git/repospec_test.go @@ -0,0 +1,215 @@ +// The following code was copied from https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/git/repospec_test.go +// and modified to test the copied repospec.go + +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package git + +import ( + "fmt" + "path/filepath" + "testing" +) + +const refQuery = "?ref=" + +var orgRepos = []string{"someOrg/someRepo", "kubernetes/website"} + +var pathNames = []string{"README.md", "foo/krusty.txt", ""} + +var hrefArgs = []string{"someBranch", "master", "v0.1.0", ""} + +var hostNamesRawAndNormalized = [][]string{ + {"gh:", "gh:"}, + {"GH:", "gh:"}, + {"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/"}, + {"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 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 { + uri := makeUrl(hostRaw, orgRepo, pathName, hrefArg) + host, org, path, ref, _ := parseGitUrl(uri) + if host != hostSpec { + bad = append(bad, []string{"host", uri, host, hostSpec}) + } + + if org != orgRepo { + bad = append(bad, []string{"orgRepo", uri, orgRepo, orgRepo}) + } + + if path != pathName { + bad = append(bad, []string{"path", uri, path, pathName}) + } + + if hrefArg != "" && ref != "refs/heads/"+hrefArg { + bad = append(bad, []string{"ref", uri, 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() + } +} + +func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) { + testcases := []struct { + input string + cloneSpec string + absPath string + ref string + tag string + sha string + }{ + { + input: "http://github.com/someorg/somerepo/somedir", + cloneSpec: "https://github.com/someorg/somerepo.git", + absPath: "somedir", + ref: "", + }, + { + input: "git@github.com:someorg/somerepo/somedir", + cloneSpec: "git@github.com:someorg/somerepo.git", + absPath: "somedir", + ref: "", + }, + { + input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?ref=branch", + cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git", + absPath: "", + ref: "refs/heads/branch", + }, + { + input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?tag=v0.1.0", + cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git", + absPath: "", + ref: "refs/tags/v0.1.0", + }, + { + input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?sha=some_sha", + cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git", + absPath: "", + ref: "some_sha", + }, + { + input: "https://itfs.mycompany.com/collection/project/_git/somerepos", + cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", + absPath: "", + ref: "", + }, + { + input: "git::https://itfs.mycompany.com/collection/project/_git/somerepos", + cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", + absPath: "", + ref: "", + }, + } + for _, testcase := range testcases { + host, orgRepo, path, ref, suffix := parseGitUrl(testcase.input) + cloneSpec := host + orgRepo + suffix + if cloneSpec != testcase.cloneSpec { + t.Errorf("CloneSpec expected to be %v, but got %v on %s", testcase.cloneSpec, cloneSpec, testcase.input) + } + + if path != testcase.absPath { + t.Errorf("AbsPath expected to be %v, but got %v on %s", testcase.absPath, path, testcase.input) + } + + if ref != testcase.ref { + t.Errorf("ref expected to be %v, but got %v on %s", testcase.ref, ref, testcase.input) + } + } +} + +func TestPeelQuery(t *testing.T) { + testcases := []struct { + input string + + path string + ref string + }{ + { + // All empty. + input: "somerepos", + path: "somerepos", + }, + { + input: "somerepos?ref=branch", + path: "somerepos", + ref: "refs/heads/branch", + }, + { + input: "somerepos?tag=v1.0.0", + path: "somerepos", + ref: "refs/tags/v1.0.0", + }, + { + input: "somerepos?sha=some_sha", + path: "somerepos", + ref: "some_sha", + }, + { + input: "somerepos?ref=branch&tag=v1.0.0", + path: "somerepos", + ref: "refs/tags/v1.0.0", + }, + { + input: "somerepos?ref=branch&sha=some_sha", + path: "somerepos", + ref: "some_sha", + }, + { + input: "somerepos?sha=some_sha&tag=v1.0.0", + path: "somerepos", + ref: "some_sha", + }, + } + + for _, testcase := range testcases { + path, ref := peelQuery(testcase.input) + if path != testcase.path || ref != testcase.ref { + t.Errorf("peelQuery: expected (%s, %s) got (%s, %s) on %s", + testcase.path, testcase.ref, + path, ref, + testcase.input) + } + } +} diff --git a/pkg/util/repospec.go b/pkg/util/repospec.go deleted file mode 100644 index b17f87c5..00000000 --- a/pkg/util/repospec.go +++ /dev/null @@ -1,165 +0,0 @@ -// The following file was copied from https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/git/repospec.go -// and modified to expose the ParseGitUrl function - -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package util - -import ( - "net/url" - "strconv" - "strings" - "time" -) - -const ( - gitSuffix = ".git" - gitDelimiter = "_git/" -) - -// From strings like git@github.com:someOrg/someRepo.git or -// https://github.com/someOrg/someRepo?ref=someHash, extract -// the parts. -func ParseGitUrl(n string) ( - host string, orgRepo string, path string, gitRef string, gitSubmodules bool, gitSuff string, gitTimeout time.Duration) { - - if strings.Contains(n, gitDelimiter) { - index := strings.Index(n, gitDelimiter) - // Adding _git/ to host - host = normalizeGitHostSpec(n[:index+len(gitDelimiter)]) - orgRepo = strings.Split(strings.Split(n[index+len(gitDelimiter):], "/")[0], "?")[0] - path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[index+len(gitDelimiter)+len(orgRepo):]) - return - } - host, n = parseHostSpec(n) - gitSuff = gitSuffix - if strings.Contains(n, gitSuffix) { - index := strings.Index(n, gitSuffix) - orgRepo = n[0:index] - n = n[index+len(gitSuffix):] - path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) - return - } - - i := strings.Index(n, "/") - if i < 1 { - path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) - return - } - j := strings.Index(n[i+1:], "/") - if j >= 0 { - j += i + 1 - orgRepo = n[:j] - path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[j+1:]) - return - } - path = "" - orgRepo, gitRef, gitTimeout, gitSubmodules = peelQuery(n) - return host, orgRepo, path, gitRef, gitSubmodules, gitSuff, gitTimeout -} - -// Clone git submodules by default. -const defaultSubmodules = true - -// Arbitrary, but non-infinite, timeout for running commands. -const defaultTimeout = 27 * time.Second - -func peelQuery(arg string) (string, string, time.Duration, bool) { - // Parse the given arg into a URL. In the event of a parse failure, return - // our defaults. - parsed, err := url.Parse(arg) - if err != nil { - return arg, "", defaultTimeout, defaultSubmodules - } - values := parsed.Query() - - // ref is the desired git ref to target. Can be specified by in a git URL - // with ?ref= or ?version=, although ref takes precedence. - ref := values.Get("version") - if queryValue := values.Get("ref"); queryValue != "" { - ref = queryValue - } - - // depth is the desired git exec timeout. Can be specified by in a git URL - // with ?timeout=. - duration := defaultTimeout - if queryValue := values.Get("timeout"); queryValue != "" { - // Attempt to first parse as a number of integer seconds (like "61"), - // and then attempt to parse as a suffixed duration (like "61s"). - if intValue, err := strconv.Atoi(queryValue); err == nil && intValue > 0 { - duration = time.Duration(intValue) * time.Second - } else if durationValue, err := time.ParseDuration(queryValue); err == nil && durationValue > 0 { - duration = durationValue - } - } - - // submodules indicates if git submodule cloning is desired. Can be - // specified by in a git URL with ?submodules=. - submodules := defaultSubmodules - if queryValue := values.Get("submodules"); queryValue != "" { - if boolValue, err := strconv.ParseBool(queryValue); err == nil { - submodules = boolValue - } - } - - return parsed.Path, ref, duration, submodules -} - -func parseHostSpec(n string) (string, string) { - var host string - // Start accumulating the host part. - for _, p := range [...]string{ - // Order matters here. - "git::", "gh:", "ssh://", "https://", "http://", - "git@", "github.com:", "github.com/"} { - if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p { - n = n[len(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. - for _, p := range [...]string{ - "ssh://", "https://", "http://"} { - if strings.HasSuffix(host, p) { - i := strings.Index(n, "/") - if i > -1 { - host = host + n[0:i+1] - n = n[i+1:] - } - break - } - } - - return normalizeGitHostSpec(host), n -} - -func normalizeGitHostSpec(host string) string { - s := strings.ToLower(host) - if strings.Contains(s, "github.com") { - if strings.Contains(s, "git@") || strings.Contains(s, "ssh:") { - host = "git@github.com:" - } else { - host = "https://github.com/" - } - } - if strings.HasPrefix(s, "git::") { - host = strings.TrimPrefix(s, "git::") - } - return host -} diff --git a/pkg/util/repospec_test.go b/pkg/util/repospec_test.go deleted file mode 100644 index a6735a7f..00000000 --- a/pkg/util/repospec_test.go +++ /dev/null @@ -1,284 +0,0 @@ -// The following code was copied from https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/git/repospec_test.go -// and modified to test the copied repospec.go - -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package util - -import ( - "fmt" - "path/filepath" - "testing" - "time" -) - -const refQuery = "?ref=" - -var orgRepos = []string{"someOrg/someRepo", "kubernetes/website"} - -var pathNames = []string{"README.md", "foo/krusty.txt", ""} - -var hrefArgs = []string{"someBranch", "master", "v0.1.0", ""} - -var hostNamesRawAndNormalized = [][]string{ - {"gh:", "gh:"}, - {"GH:", "gh:"}, - {"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/"}, - {"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 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 { - uri := makeUrl(hostRaw, orgRepo, pathName, hrefArg) - host, org, path, ref, _, _, _ := ParseGitUrl(uri) - if host != hostSpec { - bad = append(bad, []string{"host", uri, host, hostSpec}) - } - if org != orgRepo { - bad = append(bad, []string{"orgRepo", uri, orgRepo, orgRepo}) - } - if path != pathName { - bad = append(bad, []string{"path", uri, path, pathName}) - } - if ref != hrefArg { - bad = append(bad, []string{"ref", uri, 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() - } -} - -func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) { - testcases := []struct { - input string - cloneSpec string - absPath string - ref string - }{ - { - input: "http://github.com/someorg/somerepo/somedir", - cloneSpec: "https://github.com/someorg/somerepo.git", - absPath: "somedir", - ref: "", - }, - { - input: "git@github.com:someorg/somerepo/somedir", - cloneSpec: "git@github.com:someorg/somerepo.git", - absPath: "somedir", - ref: "", - }, - { - input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?ref=v0.1.0", - cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git", - absPath: "", - ref: "v0.1.0", - }, - { - input: "https://itfs.mycompany.com/collection/project/_git/somerepos", - cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", - absPath: "", - ref: "", - }, - { - input: "https://itfs.mycompany.com/collection/project/_git/somerepos?version=v1.0.0", - cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", - absPath: "", - ref: "v1.0.0", - }, - { - input: "git::https://itfs.mycompany.com/collection/project/_git/somerepos", - cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", - absPath: "", - ref: "", - }, - } - for _, testcase := range testcases { - host, orgRepo, path, ref, _, suffix, _ := ParseGitUrl(testcase.input) - cloneSpec := host + orgRepo + suffix - if cloneSpec != testcase.cloneSpec { - t.Errorf("CloneSpec expected to be %v, but got %v on %s", - testcase.cloneSpec, cloneSpec, testcase.input) - } - if path != testcase.absPath { - t.Errorf("AbsPath expected to be %v, but got %v on %s", - testcase.absPath, path, testcase.input) - } - if ref != testcase.ref { - t.Errorf("ref expected to be %v, but got %v on %s", - testcase.ref, ref, testcase.input) - } - } -} - -func TestPeelQuery(t *testing.T) { - testcases := []struct { - input string - - path string - ref string - submodules bool - timeout time.Duration - }{ - { - // All empty. - input: "somerepos", - path: "somerepos", - ref: "", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - input: "somerepos?ref=v1.0.0", - path: "somerepos", - ref: "v1.0.0", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - input: "somerepos?version=master", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - // A ref value takes precedence over a version value. - input: "somerepos?version=master&ref=v1.0.0", - path: "somerepos", - ref: "v1.0.0", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - // Empty submodules value uses default. - input: "somerepos?version=master&submodules=", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - // Malformed submodules value uses default. - input: "somerepos?version=master&submodules=maybe", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - input: "somerepos?version=master&submodules=true", - path: "somerepos", - ref: "master", - submodules: true, - timeout: defaultTimeout, - }, - { - input: "somerepos?version=master&submodules=false", - path: "somerepos", - ref: "master", - submodules: false, - timeout: defaultTimeout, - }, - { - // Empty timeout value uses default. - input: "somerepos?version=master&timeout=", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - // Malformed timeout value uses default. - input: "somerepos?version=master&timeout=jiffy", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - // Zero timeout value uses default. - input: "somerepos?version=master&timeout=0", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - input: "somerepos?version=master&timeout=0s", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: defaultTimeout, - }, - { - input: "somerepos?version=master&timeout=61", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: 61 * time.Second, - }, - { - input: "somerepos?version=master&timeout=1m1s", - path: "somerepos", - ref: "master", - submodules: defaultSubmodules, - timeout: 61 * time.Second, - }, - { - input: "somerepos?version=master&submodules=false&timeout=1m1s", - path: "somerepos", - ref: "master", - submodules: false, - timeout: 61 * time.Second, - }, - } - - for _, testcase := range testcases { - path, ref, timeout, submodules := peelQuery(testcase.input) - if path != testcase.path || ref != testcase.ref || timeout != testcase.timeout || submodules != testcase.submodules { - t.Errorf("peelQuery: expected (%s, %s, %v, %v) got (%s, %s, %v, %v) on %s", - testcase.path, testcase.ref, testcase.timeout, testcase.submodules, - path, ref, timeout, submodules, - testcase.input) - } - } -}