move gitutil to vcs

Signed-off-by: yxxhero <aiopsclub@163.com>
pull/11258/head
yxxhero 4 years ago
parent 590a6b7c98
commit d5b50a1f45

@ -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

@ -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.

@ -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,
},
}

@ -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
}

@ -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
}

@ -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")
}
}
Loading…
Cancel
Save