mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
use go-gitlog as an alternative to git2go (#1393)
* use go-gitlog as an alternative to git2go Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com> * set RELEASE to something to avoid failing binary-build step Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com> --------- Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
This commit is contained in:
committed by
GitHub
parent
a5d1fa3f66
commit
3efa40e808
@@ -3,20 +3,87 @@
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/kubescape/go-git-url/apis"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/matthyx/go-gitlog"
|
||||
)
|
||||
|
||||
var ErrFatalNotSupportedByBuild = errors.New(`git scan not supported by this build. Build with tag "gitenabled" to enable the git scan feature`)
|
||||
|
||||
type gitRepository struct {
|
||||
gitLogDisabled bool
|
||||
gitRepo gitlog.GitLog
|
||||
fileToLastCommit map[string]*gitlog.Commit
|
||||
}
|
||||
|
||||
func newGitRepository(root string) (*gitRepository, error) {
|
||||
return &gitRepository{}, ErrWarnNotSupportedByBuild
|
||||
gitRepo := gitlog.New(&gitlog.Config{Path: root})
|
||||
|
||||
return &gitRepository{
|
||||
gitRepo: gitRepo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *gitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error) {
|
||||
return nil, ErrFatalNotSupportedByBuild
|
||||
if len(g.fileToLastCommit) == 0 && !g.gitLogDisabled {
|
||||
g.buildCommitMap()
|
||||
}
|
||||
|
||||
if relevantCommit, exists := g.fileToLastCommit[filepath.ToSlash(filePath)]; exists {
|
||||
return g.getCommit(relevantCommit), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to get commit information for file: %s", filePath)
|
||||
}
|
||||
|
||||
func (g *gitRepository) buildCommitMap() {
|
||||
filePathToCommitTime := map[string]time.Time{}
|
||||
filePathToCommit := map[string]*gitlog.Commit{}
|
||||
allCommits, err := g.gitRepo.Log(nil, &gitlog.Params{IgnoreMerges: true})
|
||||
if err != nil {
|
||||
logger.L().Warning("git not found in PATH: git metadata (author and hash) will not be available", helpers.Error(err))
|
||||
g.gitLogDisabled = true
|
||||
return
|
||||
}
|
||||
|
||||
// builds a map of all files to their last commit
|
||||
for _, commit := range allCommits {
|
||||
for _, file := range commit.Files {
|
||||
commitTime := commit.Author.Date
|
||||
|
||||
// In case we have the commit information for the file which is not the latest - we override it
|
||||
if currentCommitTime, exists := filePathToCommitTime[file]; exists {
|
||||
if currentCommitTime.Before(commitTime) {
|
||||
filePathToCommitTime[file] = commitTime
|
||||
filePathToCommit[file] = commit
|
||||
}
|
||||
} else {
|
||||
filePathToCommitTime[file] = commitTime
|
||||
filePathToCommit[file] = commit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.fileToLastCommit = filePathToCommit
|
||||
}
|
||||
|
||||
func (g *gitRepository) getCommit(commit *gitlog.Commit) *apis.Commit {
|
||||
return &apis.Commit{
|
||||
SHA: commit.Hash.Long,
|
||||
Author: apis.Committer{
|
||||
Name: commit.Author.Name,
|
||||
Email: commit.Author.Email,
|
||||
Date: commit.Author.Date,
|
||||
},
|
||||
Message: commit.Subject + "\n" + commit.Body,
|
||||
Committer: apis.Committer{
|
||||
Name: commit.Committer.Name,
|
||||
Email: commit.Committer.Email,
|
||||
Date: commit.Committer.Date,
|
||||
},
|
||||
Files: []apis.Files{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build !gitenabled
|
||||
|
||||
package cautils
|
||||
|
||||
func (s *LocalGitRepositoryTestSuite) TestGetLastCommit() {
|
||||
s.T().Log("warn: skipped testing native git functionality [GetLastCommit]")
|
||||
}
|
||||
|
||||
func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
|
||||
s.T().Log("warn: skipped testing native git functionality [GetFileLastCommit]")
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//go:build gitenabled
|
||||
|
||||
package cautils
|
||||
|
||||
import (
|
||||
@@ -27,64 +28,7 @@ func newGitRepository(root string) (*gitRepository, error) {
|
||||
|
||||
func (g *gitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error) {
|
||||
if len(g.fileToLastCommit) == 0 {
|
||||
filePathToCommitTime := map[string]time.Time{}
|
||||
filePathToCommit := map[string]*git2go.Commit{}
|
||||
allCommits, _ := g.getAllCommits()
|
||||
|
||||
// builds a map of all files to their last commit
|
||||
for _, commit := range allCommits {
|
||||
// Ignore merge commits (2+ parents)
|
||||
if commit.ParentCount() <= 1 {
|
||||
tree, err := commit.Tree()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// ParentCount can be either 1 or 0 (initial commit)
|
||||
// In case it's the initial commit, prevTree is nil
|
||||
var prevTree *git2go.Tree
|
||||
if commit.ParentCount() == 1 {
|
||||
prevCommit := commit.Parent(0)
|
||||
prevTree, err = prevCommit.Tree()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
diff, err := g.git2GoRepo.DiffTreeToTree(prevTree, tree, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
numDeltas, err := diff.NumDeltas()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i < numDeltas; i++ {
|
||||
delta, err := diff.Delta(i)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
deltaFilePath := delta.NewFile.Path
|
||||
commitTime := commit.Author().When
|
||||
|
||||
// In case we have the commit information for the file which is not the latest - we override it
|
||||
if currentCommitTime, exists := filePathToCommitTime[deltaFilePath]; exists {
|
||||
if currentCommitTime.Before(commitTime) {
|
||||
filePathToCommitTime[deltaFilePath] = commitTime
|
||||
filePathToCommit[deltaFilePath] = commit
|
||||
}
|
||||
} else {
|
||||
filePathToCommitTime[deltaFilePath] = commitTime
|
||||
filePathToCommit[deltaFilePath] = commit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.fileToLastCommit = filePathToCommit
|
||||
g.buildCommitMap()
|
||||
}
|
||||
|
||||
if relevantCommit, exists := g.fileToLastCommit[filePath]; exists {
|
||||
@@ -94,6 +38,67 @@ func (g *gitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error)
|
||||
return nil, fmt.Errorf("failed to get commit information for file: %s", filePath)
|
||||
}
|
||||
|
||||
func (g *gitRepository) buildCommitMap() {
|
||||
filePathToCommitTime := map[string]time.Time{}
|
||||
filePathToCommit := map[string]*git2go.Commit{}
|
||||
allCommits, _ := g.getAllCommits()
|
||||
|
||||
// builds a map of all files to their last commit
|
||||
for _, commit := range allCommits {
|
||||
// Ignore merge commits (2+ parents)
|
||||
if commit.ParentCount() <= 1 {
|
||||
tree, err := commit.Tree()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// ParentCount can be either 1 or 0 (initial commit)
|
||||
// In case it's the initial commit, prevTree is nil
|
||||
var prevTree *git2go.Tree
|
||||
if commit.ParentCount() == 1 {
|
||||
prevCommit := commit.Parent(0)
|
||||
prevTree, err = prevCommit.Tree()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
diff, err := g.git2GoRepo.DiffTreeToTree(prevTree, tree, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
numDeltas, err := diff.NumDeltas()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i < numDeltas; i++ {
|
||||
delta, err := diff.Delta(i)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
deltaFilePath := delta.NewFile.Path
|
||||
commitTime := commit.Author().When
|
||||
|
||||
// In case we have the commit information for the file which is not the latest - we override it
|
||||
if currentCommitTime, exists := filePathToCommitTime[deltaFilePath]; exists {
|
||||
if currentCommitTime.Before(commitTime) {
|
||||
filePathToCommitTime[deltaFilePath] = commitTime
|
||||
filePathToCommit[deltaFilePath] = commit
|
||||
}
|
||||
} else {
|
||||
filePathToCommitTime[deltaFilePath] = commitTime
|
||||
filePathToCommit[deltaFilePath] = commit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.fileToLastCommit = filePathToCommit
|
||||
}
|
||||
|
||||
func (g *gitRepository) getAllCommits() ([]*git2go.Commit, error) {
|
||||
logItr, itrErr := g.git2GoRepo.Walk()
|
||||
if itrErr != nil {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
//go:build gitenabled
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func (s *LocalGitRepositoryTestSuite) TestGetLastCommit() {
|
||||
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
|
||||
if commit, err := localRepo.GetLastCommit(); s.NoError(err) {
|
||||
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
|
||||
s.Equal("Amir Malka", commit.Author.Name)
|
||||
s.Equal("amirm@armosec.io", commit.Author.Email)
|
||||
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
|
||||
s.Equal(int64(1653235917), commit.Author.Date.Unix())
|
||||
s.Equal("added file B\n", commit.Message)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +26,7 @@ func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
|
||||
s.Equal("9fae4be19624297947d2b605cefbff516628612d", commit.SHA)
|
||||
s.Equal("Amir Malka", commit.Author.Name)
|
||||
s.Equal("amirm@armosec.io", commit.Author.Email)
|
||||
s.Equal("2022-05-22 18:55:48 +0300 +0300", commit.Author.Date.String())
|
||||
s.Equal(int64(1653234948), commit.Author.Date.Unix())
|
||||
s.Equal("added file A\n", commit.Message)
|
||||
}
|
||||
|
||||
@@ -35,10 +40,19 @@ func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
|
||||
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
|
||||
s.Equal("Amir Malka", commit.Author.Name)
|
||||
s.Equal("amirm@armosec.io", commit.Author.Email)
|
||||
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
|
||||
s.Equal(int64(1653235917), commit.Author.Date.Unix())
|
||||
s.Equal("added file B\n", commit.Message)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkBuildCommitMap(b *testing.B) {
|
||||
localRepo, err := NewLocalGitRepository("testdata/temp/localrepo")
|
||||
assert.NoError(b, err)
|
||||
for i := 0; i < b.N; i++ {
|
||||
localRepo.buildCommitMap()
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
if gitRepo != nil {
|
||||
commitInfo, err := gitRepo.GetFileLastCommit(source)
|
||||
if err != nil && !warnIssued {
|
||||
logger.L().Ctx(ctx).Warning("Git scan skipped", helpers.Error(err))
|
||||
logger.L().Debug("Git scan skipped", helpers.Error(err))
|
||||
warnIssued = true // croak only once
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user