From e195cdadf3cbb021cfd81b1c6bf6a846bf9cafc1 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Sat, 24 Jul 2021 16:44:20 +0800 Subject: [PATCH] move gitutil to vcs Signed-off-by: yxxhero --- internal/fileutil/fileutil.go | 10 ---- internal/resolver/resolver.go | 13 +++-- internal/resolver/resolver_test.go | 18 ++---- pkg/getter/gitgetter.go | 20 ++++--- pkg/gitutil/gitutil.go | 76 ------------------------ pkg/gitutil/gitutil_test.go | 93 ------------------------------ 6 files changed, 26 insertions(+), 204 deletions(-) delete mode 100644 pkg/gitutil/gitutil.go delete mode 100644 pkg/gitutil/gitutil_test.go diff --git a/internal/fileutil/fileutil.go b/internal/fileutil/fileutil.go index b737877cf..9292ead8a 100644 --- a/internal/fileutil/fileutil.go +++ b/internal/fileutil/fileutil.go @@ -102,16 +102,6 @@ func CompressDirToTgz(chartTmpDir, tmpdir string) (*bytes.Buffer, error) { header.Format = tar.FormatPAX header.PAXRecords = nil - if fi.IsDir() { - header.Typeflag = tar.TypeDir - header.Mode |= 0755 - header.Size = 0 - } else { - header.Typeflag = tar.TypeReg - header.Mode |= 0644 - header.Size = fi.Size() - } - // write header if err := tw.WriteHeader(header); err != nil { return err diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index f4ebe2a91..7ea87166c 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -27,10 +27,10 @@ import ( "github.com/Masterminds/semver/v3" "github.com/pkg/errors" + "github.com/Masterminds/vcs" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/gates" - "helm.sh/helm/v3/pkg/gitutil" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/provenance" "helm.sh/helm/v3/pkg/registry" @@ -39,8 +39,6 @@ import ( const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") -var gitGetRefs = gitutil.GetRefs - // Resolver resolves dependencies from semantic version ranges to a particular version. type Resolver struct { chartpath string @@ -114,13 +112,18 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string } if strings.HasPrefix(d.Repository, "git://") { - refs, err := gitGetRefs(strings.TrimPrefix(d.Repository, "git://")) + + local, err := os.MkdirTemp("", d.Name) + if err != nil { + return nil, err + } + repo, err := vcs.NewRepo(strings.TrimPrefix(d.Repository, "git://"), local) if err != nil { return nil, err } - _, found := refs[d.Version] + found := repo.IsReference(d.Version) if !found { return nil, fmt.Errorf(`dependency %q is missing git branch or tag: %s. diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go index e8fe63d73..6c9c81c77 100644 --- a/internal/resolver/resolver_test.go +++ b/internal/resolver/resolver_test.go @@ -23,15 +23,7 @@ import ( "helm.sh/helm/v3/pkg/registry" ) -func fakeGetRefs(gitRepoURL string) (map[string]string, error) { - refs := map[string]string{ - "1.0.0": "9668ad4d90c5e95bd520e58e7387607be6b63bb6", - } - return refs, nil -} - func TestResolve(t *testing.T) { - gitGetRefs = fakeGetRefs tests := []struct { name string req []*chart.Dependency @@ -148,24 +140,26 @@ func TestResolve(t *testing.T) { { name: "repo from git ssh url", req: []*chart.Dependency{ - {Name: "gitdependency", Repository: "git:git@github.com:helm/gitdependency.git", Version: "1.0.0"}, + {Name: "gitdependency", Repository: "git://git@github.com:helm/gitdependency.git", Version: "1.0.0"}, }, expect: &chart.Lock{ Dependencies: []*chart.Dependency{ - {Name: "gitdependency", Repository: "git:git@github.com:helm/gitdependency.git", Version: "1.0.0"}, + {Name: "gitdependency", Repository: "git://git@github.com:helm/gitdependency.git", Version: "1.0.0"}, }, }, + err: true, }, { name: "repo from git https url", req: []*chart.Dependency{ - {Name: "gitdependency", Repository: "https://github.com/helm/gitdependency.git", Version: "1.0.0"}, + {Name: "gitdependency", Repository: "git://https://github.com/helm/gitdependency.git", Version: "1.0.0"}, }, expect: &chart.Lock{ Dependencies: []*chart.Dependency{ - {Name: "gitdependency", Repository: "https://github.com/helm/gitdependency.git", Version: "1.0.0"}, + {Name: "gitdependency", Repository: "git://https://github.com/helm/gitdependency.git", Version: "1.0.0"}, }, }, + err: true, }, } diff --git a/pkg/getter/gitgetter.go b/pkg/getter/gitgetter.go index cc901839b..40ec4833f 100644 --- a/pkg/getter/gitgetter.go +++ b/pkg/getter/gitgetter.go @@ -23,13 +23,10 @@ import ( "os" "path/filepath" + "github.com/Masterminds/vcs" "helm.sh/helm/v3/internal/fileutil" - "helm.sh/helm/v3/pkg/gitutil" ) -// Assigned here so it can be overridden for testing. -var gitCloneTo = gitutil.CloneTo - // GitGetter is the default HTTP(/S) backend handler type GitGetter struct { opts options @@ -67,7 +64,7 @@ func (g *GitGetter) get(href string) (*bytes.Buffer, error) { version := g.opts.version chartName := g.opts.chartName if version == "" { - return nil, fmt.Errorf("The version must be a valid tag or branch name for the git repo, not nil") + return nil, fmt.Errorf("the version must be a valid tag or branch name for the git repo, not nil") } tmpDir, err := os.MkdirTemp("", "helm") if err != nil { @@ -80,8 +77,15 @@ func (g *GitGetter) get(href string) (*bytes.Buffer, error) { } defer os.RemoveAll(tmpDir) - if err = gitCloneTo(gitURL, version, chartTmpDir); err != nil { - return nil, fmt.Errorf("Unable to retrieve git repo. %s", err) + repo, err := vcs.NewRepo(gitURL, chartTmpDir) + if err != nil { + return nil, err + } + if err := repo.Get(); err != nil { + return nil, err + } + if err := repo.UpdateVersion(version); err != nil { + return nil, err } // A .helmignore that includes an ignore for .git/ should be included in the git repo itself, @@ -91,7 +95,7 @@ func (g *GitGetter) get(href string) (*bytes.Buffer, error) { buf, err := fileutil.CompressDirToTgz(chartTmpDir, tmpDir) if err != nil { - return nil, fmt.Errorf("Unable to tar and compress dir %s to tgz file. %s", tmpDir, err) + return nil, fmt.Errorf("unable to tar and compress dir %s to tgz file. %s", tmpDir, err) } return buf, nil } diff --git a/pkg/gitutil/gitutil.go b/pkg/gitutil/gitutil.go deleted file mode 100644 index 85b2a2ea1..000000000 --- a/pkg/gitutil/gitutil.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gitutil - -import ( - "bufio" - "fmt" - "os/exec" - "regexp" - "strings" -) - -var execCommand = exec.Command - -// This regex is designed to match output from git of the style: -// ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/v0.21.0 -// and extract the hash and ref name as capture groups -var gitRefLineRegexp = regexp.MustCompile(`^([a-fA-F0-9]+)\s+(refs\/(?:tags|heads|pull|remotes)\/)(.*)$`) - -// Run a git command as a child process. -// If git is not on the path, an error will be returned. -// Returns the command output or an error. -func gitExec(args ...string) ([]byte, error) { - cmd := execCommand("git", args...) - output, err := cmd.CombinedOutput() - if err != nil { - err = fmt.Errorf("Error executing git command:\ngit %s\n\n%s\n%s", strings.Join(args, " "), string(output), err) - } - return output, err -} - -// GetRefs loads the tags, refs, branches (commit-ish) from a git repo. -// Returns a map of tags and branch names to commit shas -func GetRefs(gitRepoURL string) (map[string]string, error) { - output, err := gitExec("ls-remote", "--tags", "--heads", gitRepoURL) - if err != nil { - return nil, err - } - - tagsToCommitShas := map[string]string{} - scanner := bufio.NewScanner(strings.NewReader(string(output))) - for scanner.Scan() { - line := scanner.Bytes() - match := gitRefLineRegexp.FindSubmatch(line) - if len(match) == 4 { - // As documented in gitrevisions: https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html#_specifying_revisions - // "A suffix ^ followed by an empty brace pair means the object could be a tag, and dereference the tag recursively until a non-tag object is found." - // In other words, the hash without ^{} is the hash of the tag, and the hash with ^{} is the hash of the commit at which the tag was made. - // For our purposes, either will work. - var name = strings.TrimSuffix(string(match[3]), "^{}") - tagsToCommitShas[name] = string(match[1]) - } - } - - return tagsToCommitShas, nil -} - -// CloneTo fetches a git repo at a specific ref from a git url -func CloneTo(gitRepoURL string, ref string, destinationPath string) error { - _, err := gitExec("clone", "--depth", "1", gitRepoURL, "--branch", ref, "--single-branch", destinationPath) - return err -} diff --git a/pkg/gitutil/gitutil_test.go b/pkg/gitutil/gitutil_test.go deleted file mode 100644 index fdecaa27c..000000000 --- a/pkg/gitutil/gitutil_test.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gitutil - -import ( - "fmt" - "os" - "os/exec" - "reflect" - "testing" -) - -func fakeExecCommand(command string, args ...string) *exec.Cmd { - cs := []string{"-test.run=TestHelperProcess", "--", command} - cs = append(cs, args...) - cmd := exec.Command(os.Args[0], cs...) - cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} - return cmd -} - -// TestHelperProcess is not a test. It is used to create a mock process to spawn as a child process for testing exec.Command(). -// Borrowed from the way Go tests exec.Command() internally: https://github.com/golang/go/blob/master/src/os/exec/exec_test.go#L727 -func TestHelperProcess(t *testing.T) { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { - return - } - args := os.Args - cmd := args[len(args)-1] // the last arg is the github url. using this to determine what to return. - result := "Unknown Command" - exitCode := 1 - - switch cmd { - case "success": - exitCode = 0 - result = `From git@github.com:helm/helm.git -9b42702a4bced339ff424a78ad68dd6be6e1a80a refs/heads/dev -9668ad4d90c5e95bd520e58e7387607be6b63bb6 refs/heads/master -44fb06eb69fecd4b6a5b2443a4768ba12bd70c09 refs/tags/v2.10.0 -9ad53aac42165a5fadc6c87be0dea6b115f93090 refs/tags/v2.10.0^{} -4fdd07f21418abb43925998cf690857adc16451b refs/tags/v2.10.0-rc.1 -aa98e7e3dd2356bce72e8e367e8c87e8085c692b refs/tags/v2.10.0-rc.1^{}` - - case "error": - exitCode = 1 - result = `ssh: Could not resolve hostname git: nodename nor servname provided, or not known -fatal: Could not read from remote repository. - -Please make sure you have the correct access rights -and the repository exists.` - } - - fmt.Fprint(os.Stdout, result) - os.Exit(exitCode) -} - -func TestGetRefsWhenGitReturnsRefs(t *testing.T) { - expected := map[string]string{ - "dev": "9b42702a4bced339ff424a78ad68dd6be6e1a80a", - "master": "9668ad4d90c5e95bd520e58e7387607be6b63bb6", - "v2.10.0": "9ad53aac42165a5fadc6c87be0dea6b115f93090", - "v2.10.0-rc.1": "aa98e7e3dd2356bce72e8e367e8c87e8085c692b", - } - execCommand = fakeExecCommand - defer func() { execCommand = exec.Command }() - result, err := GetRefs("success") - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Expected %q, got %q", expected, result) - } -} -func TestGetRefsWhenGitCommandReturnsError(t *testing.T) { - execCommand = fakeExecCommand - defer func() { execCommand = exec.Command }() - _, err := GetRefs("error") - if err == nil { - t.Errorf("Error should have been returned, but was not") - } -}