refactor: remove github.com/pkg/errors dependency (#1077)
* refactor: split out common/git * refactor: move git options to separate func * refactor: remove github.com/pkg/errors dependency * fix(golangci-lint): forbid github.com/pkg/errors * style: fix typo * style: fix typo Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
432
pkg/common/git/git.go
Normal file
432
pkg/common/git/git.go
Normal file
@@ -0,0 +1,432 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/go-ini/ini"
|
||||
"github.com/mattn/go-isatty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
codeCommitHTTPRegex = regexp.MustCompile(`^https?://git-codecommit\.(.+)\.amazonaws.com/v1/repos/(.+)$`)
|
||||
codeCommitSSHRegex = regexp.MustCompile(`ssh://git-codecommit\.(.+)\.amazonaws.com/v1/repos/(.+)$`)
|
||||
githubHTTPRegex = regexp.MustCompile(`^https?://.*github.com.*/(.+)/(.+?)(?:.git)?$`)
|
||||
githubSSHRegex = regexp.MustCompile(`github.com[:/](.+)/(.+?)(?:.git)?$`)
|
||||
|
||||
cloneLock sync.Mutex
|
||||
|
||||
ErrShortRef = errors.New("short SHA references are not supported")
|
||||
ErrNoRepo = errors.New("unable to find git repo")
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
err error
|
||||
commit string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *Error) Commit() string {
|
||||
return e.commit
|
||||
}
|
||||
|
||||
// FindGitRevision get the current git revision
|
||||
func FindGitRevision(file string) (shortSha string, sha string, err error) {
|
||||
gitDir, err := findGitDirectory(file)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
bts, err := ioutil.ReadFile(filepath.Join(gitDir, "HEAD"))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var ref = strings.TrimSpace(strings.TrimPrefix(string(bts), "ref:"))
|
||||
var refBuf []byte
|
||||
if strings.HasPrefix(ref, "refs/") {
|
||||
// load commitid ref
|
||||
refBuf, err = ioutil.ReadFile(filepath.Join(gitDir, ref))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
} else {
|
||||
refBuf = []byte(ref)
|
||||
}
|
||||
|
||||
log.Debugf("Found revision: %s", refBuf)
|
||||
return string(refBuf[:7]), strings.TrimSpace(string(refBuf)), nil
|
||||
}
|
||||
|
||||
// FindGitRef get the current git ref
|
||||
func FindGitRef(file string) (string, error) {
|
||||
gitDir, err := findGitDirectory(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Debugf("Loading revision from git directory '%s'", gitDir)
|
||||
|
||||
_, ref, err := FindGitRevision(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debugf("HEAD points to '%s'", ref)
|
||||
|
||||
// Prefer the git library to iterate over the references and find a matching tag or branch.
|
||||
var refTag = ""
|
||||
var refBranch = ""
|
||||
r, err := git.PlainOpen(filepath.Join(gitDir, ".."))
|
||||
if err == nil {
|
||||
iter, err := r.References()
|
||||
if err == nil {
|
||||
for {
|
||||
r, err := iter.Next()
|
||||
if r == nil || err != nil {
|
||||
break
|
||||
}
|
||||
// log.Debugf("Reference: name=%s sha=%s", r.Name().String(), r.Hash().String())
|
||||
if r.Hash().String() == ref {
|
||||
if r.Name().IsTag() {
|
||||
refTag = r.Name().String()
|
||||
}
|
||||
if r.Name().IsBranch() {
|
||||
refBranch = r.Name().String()
|
||||
}
|
||||
}
|
||||
}
|
||||
iter.Close()
|
||||
}
|
||||
}
|
||||
if refTag != "" {
|
||||
return refTag, nil
|
||||
}
|
||||
if refBranch != "" {
|
||||
return refBranch, nil
|
||||
}
|
||||
|
||||
// If the above doesn't work, fall back to the old way
|
||||
|
||||
// try tags first
|
||||
tag, err := findGitPrettyRef(ref, gitDir, "refs/tags")
|
||||
if err != nil || tag != "" {
|
||||
return tag, err
|
||||
}
|
||||
// and then branches
|
||||
return findGitPrettyRef(ref, gitDir, "refs/heads")
|
||||
}
|
||||
|
||||
func findGitPrettyRef(head, root, sub string) (string, error) {
|
||||
var name string
|
||||
var err = filepath.Walk(filepath.Join(root, sub), func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name != "" || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
var bts []byte
|
||||
if bts, err = ioutil.ReadFile(path); err != nil {
|
||||
return err
|
||||
}
|
||||
var pointsTo = strings.TrimSpace(string(bts))
|
||||
if head == pointsTo {
|
||||
// On Windows paths are separated with backslash character so they should be replaced to provide proper git refs format
|
||||
name = strings.TrimPrefix(strings.ReplaceAll(strings.Replace(path, root, "", 1), `\`, `/`), "/")
|
||||
log.Debugf("HEAD matches %s", name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return name, err
|
||||
}
|
||||
|
||||
// FindGithubRepo get the repo
|
||||
func FindGithubRepo(file, githubInstance, remoteName string) (string, error) {
|
||||
if remoteName == "" {
|
||||
remoteName = "origin"
|
||||
}
|
||||
|
||||
url, err := findGitRemoteURL(file, remoteName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, slug, err := findGitSlug(url, githubInstance)
|
||||
return slug, err
|
||||
}
|
||||
|
||||
func findGitRemoteURL(file, remoteName string) (string, error) {
|
||||
gitDir, err := findGitDirectory(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Debugf("Loading slug from git directory '%s'", gitDir)
|
||||
|
||||
gitconfig, err := ini.InsensitiveLoad(fmt.Sprintf("%s/config", gitDir))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
remote, err := gitconfig.GetSection(fmt.Sprintf(`remote "%s"`, remoteName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
urlKey, err := remote.GetKey("url")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
url := urlKey.String()
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func findGitSlug(url string, githubInstance string) (string, string, error) {
|
||||
if matches := codeCommitHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "CodeCommit", matches[2], nil
|
||||
} else if matches := codeCommitSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "CodeCommit", matches[2], nil
|
||||
} else if matches := githubHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||
} else if matches := githubSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||
} else if githubInstance != "github.com" {
|
||||
gheHTTPRegex := regexp.MustCompile(fmt.Sprintf(`^https?://%s/(.+)/(.+?)(?:.git)?$`, githubInstance))
|
||||
gheSSHRegex := regexp.MustCompile(fmt.Sprintf(`%s[:/](.+)/(.+?)(?:.git)?$`, githubInstance))
|
||||
if matches := gheHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||
} else if matches := gheSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||
}
|
||||
}
|
||||
return "", url, nil
|
||||
}
|
||||
|
||||
func findGitDirectory(fromFile string) (string, error) {
|
||||
absPath, err := filepath.Abs(fromFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fi, err := os.Stat(absPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var dir string
|
||||
if fi.Mode().IsDir() {
|
||||
dir = absPath
|
||||
} else {
|
||||
dir = filepath.Dir(absPath)
|
||||
}
|
||||
|
||||
gitPath := filepath.Join(dir, ".git")
|
||||
fi, err = os.Stat(gitPath)
|
||||
if err == nil && fi.Mode().IsDir() {
|
||||
return gitPath, nil
|
||||
} else if dir == "/" || dir == "C:\\" || dir == "c:\\" {
|
||||
return "", &Error{err: ErrNoRepo}
|
||||
}
|
||||
|
||||
return findGitDirectory(filepath.Dir(dir))
|
||||
}
|
||||
|
||||
// NewGitCloneExecutorInput the input for the NewGitCloneExecutor
|
||||
type NewGitCloneExecutorInput struct {
|
||||
URL string
|
||||
Ref string
|
||||
Dir string
|
||||
Token string
|
||||
}
|
||||
|
||||
// CloneIfRequired ...
|
||||
func CloneIfRequired(ctx context.Context, refName plumbing.ReferenceName, input NewGitCloneExecutorInput, logger log.FieldLogger) (*git.Repository, error) {
|
||||
r, err := git.PlainOpen(input.Dir)
|
||||
if err != nil {
|
||||
var progressWriter io.Writer
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||
if entry, ok := logger.(*log.Entry); ok {
|
||||
progressWriter = entry.WriterLevel(log.DebugLevel)
|
||||
} else if lgr, ok := logger.(*log.Logger); ok {
|
||||
progressWriter = lgr.WriterLevel(log.DebugLevel)
|
||||
} else {
|
||||
log.Errorf("Unable to get writer from logger (type=%T)", logger)
|
||||
progressWriter = os.Stdout
|
||||
}
|
||||
}
|
||||
|
||||
cloneOptions := git.CloneOptions{
|
||||
URL: input.URL,
|
||||
Progress: progressWriter,
|
||||
}
|
||||
if input.Token != "" {
|
||||
cloneOptions.Auth = &http.BasicAuth{
|
||||
Username: "token",
|
||||
Password: input.Token,
|
||||
}
|
||||
}
|
||||
|
||||
r, err = git.PlainCloneContext(ctx, input.Dir, false, &cloneOptions)
|
||||
if err != nil {
|
||||
logger.Errorf("Unable to clone %v %s: %v", input.URL, refName, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = os.Chmod(input.Dir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func gitOptions(token string) (fetchOptions git.FetchOptions, pullOptions git.PullOptions) {
|
||||
fetchOptions.RefSpecs = []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"}
|
||||
pullOptions.Force = true
|
||||
|
||||
if token != "" {
|
||||
auth := &http.BasicAuth{
|
||||
Username: "token",
|
||||
Password: token,
|
||||
}
|
||||
fetchOptions.Auth = auth
|
||||
pullOptions.Auth = auth
|
||||
}
|
||||
|
||||
return fetchOptions, pullOptions
|
||||
}
|
||||
|
||||
// NewGitCloneExecutor creates an executor to clone git repos
|
||||
// nolint:gocyclo
|
||||
func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := common.Logger(ctx)
|
||||
logger.Infof(" \u2601 git clone '%s' # ref=%s", input.URL, input.Ref)
|
||||
logger.Debugf(" cloning %s to %s", input.URL, input.Dir)
|
||||
|
||||
cloneLock.Lock()
|
||||
defer cloneLock.Unlock()
|
||||
|
||||
refName := plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", input.Ref))
|
||||
r, err := CloneIfRequired(ctx, refName, input, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// fetch latest changes
|
||||
fetchOptions, pullOptions := gitOptions(input.Token)
|
||||
|
||||
err = r.Fetch(&fetchOptions)
|
||||
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||
return err
|
||||
}
|
||||
|
||||
var hash *plumbing.Hash
|
||||
rev := plumbing.Revision(input.Ref)
|
||||
if hash, err = r.ResolveRevision(rev); err != nil {
|
||||
logger.Errorf("Unable to resolve %s: %v", input.Ref, err)
|
||||
}
|
||||
|
||||
if hash.String() != input.Ref && strings.HasPrefix(hash.String(), input.Ref) {
|
||||
return &Error{
|
||||
err: ErrShortRef,
|
||||
commit: hash.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we need to know if it's a tag or a branch
|
||||
// And the easiest way to do it is duck typing
|
||||
//
|
||||
// If err is nil, it's a tag so let's proceed with that hash like we would if
|
||||
// it was a sha
|
||||
refType := "tag"
|
||||
rev = plumbing.Revision(path.Join("refs", "tags", input.Ref))
|
||||
if _, err := r.Tag(input.Ref); errors.Is(err, git.ErrTagNotFound) {
|
||||
rName := plumbing.ReferenceName(path.Join("refs", "remotes", "origin", input.Ref))
|
||||
if _, err := r.Reference(rName, false); errors.Is(err, plumbing.ErrReferenceNotFound) {
|
||||
refType = "sha"
|
||||
rev = plumbing.Revision(input.Ref)
|
||||
} else {
|
||||
refType = "branch"
|
||||
rev = plumbing.Revision(rName)
|
||||
}
|
||||
}
|
||||
|
||||
if hash, err = r.ResolveRevision(rev); err != nil {
|
||||
logger.Errorf("Unable to resolve %s: %v", input.Ref, err)
|
||||
return err
|
||||
}
|
||||
|
||||
var w *git.Worktree
|
||||
if w, err = r.Worktree(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the hash resolved doesn't match the ref provided in a workflow then we're
|
||||
// using a branch or tag ref, not a sha
|
||||
//
|
||||
// Repos on disk point to commit hashes, and need to checkout input.Ref before
|
||||
// we try and pull down any changes
|
||||
if hash.String() != input.Ref && refType == "branch" {
|
||||
logger.Debugf("Provided ref is not a sha. Checking out branch before pulling changes")
|
||||
sourceRef := plumbing.ReferenceName(path.Join("refs", "remotes", "origin", input.Ref))
|
||||
if err = w.Checkout(&git.CheckoutOptions{
|
||||
Branch: sourceRef,
|
||||
Force: true,
|
||||
}); err != nil {
|
||||
logger.Errorf("Unable to checkout %s: %v", sourceRef, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = w.Pull(&pullOptions); err != nil && err != git.NoErrAlreadyUpToDate {
|
||||
logger.Debugf("Unable to pull %s: %v", refName, err)
|
||||
}
|
||||
logger.Debugf("Cloned %s to %s", input.URL, input.Dir)
|
||||
|
||||
if hash.String() != input.Ref && refType == "branch" {
|
||||
logger.Debugf("Provided ref is not a sha. Updating branch ref after pull")
|
||||
if hash, err = r.ResolveRevision(rev); err != nil {
|
||||
logger.Errorf("Unable to resolve %s: %v", input.Ref, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = w.Checkout(&git.CheckoutOptions{
|
||||
Hash: *hash,
|
||||
Force: true,
|
||||
}); err != nil {
|
||||
logger.Errorf("Unable to checkout %s: %v", *hash, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = w.Reset(&git.ResetOptions{
|
||||
Mode: git.HardReset,
|
||||
Commit: *hash,
|
||||
}); err != nil {
|
||||
logger.Errorf("Unable to reset to %s: %v", hash.String(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debugf("Checked out %s", input.Ref)
|
||||
return nil
|
||||
}
|
||||
}
|
243
pkg/common/git/git_test.go
Normal file
243
pkg/common/git/git_test.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFindGitSlug(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var slugTests = []struct {
|
||||
url string // input
|
||||
provider string // expected result
|
||||
slug string // expected result
|
||||
}{
|
||||
{"https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo-name", "CodeCommit", "my-repo-name"},
|
||||
{"ssh://git-codecommit.us-west-2.amazonaws.com/v1/repos/my-repo", "CodeCommit", "my-repo"},
|
||||
{"git@github.com:nektos/act.git", "GitHub", "nektos/act"},
|
||||
{"git@github.com:nektos/act", "GitHub", "nektos/act"},
|
||||
{"https://github.com/nektos/act.git", "GitHub", "nektos/act"},
|
||||
{"http://github.com/nektos/act.git", "GitHub", "nektos/act"},
|
||||
{"https://github.com/nektos/act", "GitHub", "nektos/act"},
|
||||
{"http://github.com/nektos/act", "GitHub", "nektos/act"},
|
||||
{"git+ssh://git@github.com/owner/repo.git", "GitHub", "owner/repo"},
|
||||
{"http://myotherrepo.com/act.git", "", "http://myotherrepo.com/act.git"},
|
||||
}
|
||||
|
||||
for _, tt := range slugTests {
|
||||
provider, slug, err := findGitSlug(tt.url, "github.com")
|
||||
|
||||
assert.NoError(err)
|
||||
assert.Equal(tt.provider, provider)
|
||||
assert.Equal(tt.slug, slug)
|
||||
}
|
||||
}
|
||||
|
||||
func testDir(t *testing.T) string {
|
||||
basedir, err := ioutil.TempDir("", "act-test")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = os.RemoveAll(basedir) })
|
||||
return basedir
|
||||
}
|
||||
|
||||
func cleanGitHooks(dir string) error {
|
||||
hooksDir := filepath.Join(dir, ".git", "hooks")
|
||||
files, err := ioutil.ReadDir(hooksDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.IsDir() {
|
||||
continue
|
||||
}
|
||||
relName := filepath.Join(hooksDir, f.Name())
|
||||
if err := os.Remove(relName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestFindGitRemoteURL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
basedir := testDir(t)
|
||||
gitConfig()
|
||||
err := gitCmd("init", basedir)
|
||||
assert.NoError(err)
|
||||
err = cleanGitHooks(basedir)
|
||||
assert.NoError(err)
|
||||
|
||||
remoteURL := "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo-name"
|
||||
err = gitCmd("config", "-f", fmt.Sprintf("%s/.git/config", basedir), "--add", "remote.origin.url", remoteURL)
|
||||
assert.NoError(err)
|
||||
|
||||
u, err := findGitRemoteURL(basedir, "origin")
|
||||
assert.NoError(err)
|
||||
assert.Equal(remoteURL, u)
|
||||
}
|
||||
|
||||
func TestGitFindRef(t *testing.T) {
|
||||
basedir := testDir(t)
|
||||
gitConfig()
|
||||
|
||||
for name, tt := range map[string]struct {
|
||||
Prepare func(t *testing.T, dir string)
|
||||
Assert func(t *testing.T, ref string, err error)
|
||||
}{
|
||||
"new_repo": {
|
||||
Prepare: func(t *testing.T, dir string) {},
|
||||
Assert: func(t *testing.T, ref string, err error) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
"new_repo_with_commit": {
|
||||
Prepare: func(t *testing.T, dir string) {
|
||||
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg"))
|
||||
},
|
||||
Assert: func(t *testing.T, ref string, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "refs/heads/master", ref)
|
||||
},
|
||||
},
|
||||
"current_head_is_tag": {
|
||||
Prepare: func(t *testing.T, dir string) {
|
||||
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "commit msg"))
|
||||
require.NoError(t, gitCmd("-C", dir, "tag", "v1.2.3"))
|
||||
require.NoError(t, gitCmd("-C", dir, "checkout", "v1.2.3"))
|
||||
},
|
||||
Assert: func(t *testing.T, ref string, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "refs/tags/v1.2.3", ref)
|
||||
},
|
||||
},
|
||||
"current_head_is_same_as_tag": {
|
||||
Prepare: func(t *testing.T, dir string) {
|
||||
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "1.4.2 release"))
|
||||
require.NoError(t, gitCmd("-C", dir, "tag", "v1.4.2"))
|
||||
},
|
||||
Assert: func(t *testing.T, ref string, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "refs/tags/v1.4.2", ref)
|
||||
},
|
||||
},
|
||||
"current_head_is_not_tag": {
|
||||
Prepare: func(t *testing.T, dir string) {
|
||||
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg"))
|
||||
require.NoError(t, gitCmd("-C", dir, "tag", "v1.4.2"))
|
||||
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg2"))
|
||||
},
|
||||
Assert: func(t *testing.T, ref string, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "refs/heads/master", ref)
|
||||
},
|
||||
},
|
||||
"current_head_is_another_branch": {
|
||||
Prepare: func(t *testing.T, dir string) {
|
||||
require.NoError(t, gitCmd("-C", dir, "checkout", "-b", "mybranch"))
|
||||
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg"))
|
||||
},
|
||||
Assert: func(t *testing.T, ref string, err error) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "refs/heads/mybranch", ref)
|
||||
},
|
||||
},
|
||||
} {
|
||||
tt := tt
|
||||
name := name
|
||||
t.Run(name, func(t *testing.T) {
|
||||
dir := filepath.Join(basedir, name)
|
||||
require.NoError(t, os.MkdirAll(dir, 0755))
|
||||
require.NoError(t, gitCmd("-C", dir, "init", "--initial-branch=master"))
|
||||
require.NoError(t, cleanGitHooks(dir))
|
||||
tt.Prepare(t, dir)
|
||||
ref, err := FindGitRef(dir)
|
||||
tt.Assert(t, ref, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitCloneExecutor(t *testing.T) {
|
||||
for name, tt := range map[string]struct {
|
||||
Err error
|
||||
URL, Ref string
|
||||
}{
|
||||
"tag": {
|
||||
Err: nil,
|
||||
URL: "https://github.com/actions/checkout",
|
||||
Ref: "v2",
|
||||
},
|
||||
"branch": {
|
||||
Err: nil,
|
||||
URL: "https://github.com/anchore/scan-action",
|
||||
Ref: "act-fails",
|
||||
},
|
||||
"sha": {
|
||||
Err: nil,
|
||||
URL: "https://github.com/actions/checkout",
|
||||
Ref: "5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f", // v2
|
||||
},
|
||||
"short-sha": {
|
||||
Err: &Error{ErrShortRef, "5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f"},
|
||||
URL: "https://github.com/actions/checkout",
|
||||
Ref: "5a4ac90", // v2
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
clone := NewGitCloneExecutor(NewGitCloneExecutorInput{
|
||||
URL: tt.URL,
|
||||
Ref: tt.Ref,
|
||||
Dir: testDir(t),
|
||||
})
|
||||
|
||||
err := clone(context.Background())
|
||||
if tt.Err != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.Err, err)
|
||||
} else {
|
||||
assert.Empty(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func gitConfig() {
|
||||
if os.Getenv("GITHUB_ACTIONS") == "true" {
|
||||
var err error
|
||||
if err = gitCmd("config", "--global", "user.email", "test@test.com"); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
if err = gitCmd("config", "--global", "user.name", "Unit Test"); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func gitCmd(args ...string) error {
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Run()
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||
return fmt.Errorf("Exit error %d", waitStatus.ExitStatus())
|
||||
}
|
||||
return exitError
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user