mirror of https://github.com/harness/drone.git
feat: files in webhook payload (#1013)
parent
646c8fbe75
commit
e0f8248ead
|
@ -23,6 +23,7 @@ import (
|
|||
pullreqevents "github.com/harness/gitness/app/events/pullreq"
|
||||
"github.com/harness/gitness/events"
|
||||
"github.com/harness/gitness/git"
|
||||
"github.com/harness/gitness/git/enum"
|
||||
)
|
||||
|
||||
// handleFileViewedOnBranchUpdate handles pull request Branch Updated events.
|
||||
|
@ -63,15 +64,16 @@ func (s *Service) handleFileViewedOnBranchUpdate(ctx context.Context,
|
|||
// UPDATED: mark as obsolete - in case pr is closed file SHA is handling it
|
||||
// This strategy leads to a behavior very similar to what github is doing
|
||||
switch fileDiff.Status {
|
||||
case git.FileDiffStatusAdded:
|
||||
case enum.FileDiffStatusAdded:
|
||||
obsoletePaths = append(obsoletePaths, fileDiff.Path)
|
||||
case git.FileDiffStatusDeleted:
|
||||
case enum.FileDiffStatusDeleted:
|
||||
obsoletePaths = append(obsoletePaths, fileDiff.OldPath)
|
||||
case git.FileDiffStatusRenamed:
|
||||
case enum.FileDiffStatusRenamed:
|
||||
obsoletePaths = append(obsoletePaths, fileDiff.OldPath, fileDiff.Path)
|
||||
case git.FileDiffStatusModified:
|
||||
case enum.FileDiffStatusModified:
|
||||
obsoletePaths = append(obsoletePaths, fileDiff.Path)
|
||||
case git.FileDiffStatusUndefined:
|
||||
case enum.FileDiffStatusCopied:
|
||||
case enum.FileDiffStatusUndefined:
|
||||
// other cases we don't care
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import (
|
|||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
const MaxWebhookCommitFileStats = 20
|
||||
|
||||
// ReferencePayload describes the payload of Reference related webhook triggers.
|
||||
// Note: Use same payload for all reference operations to make it easier for consumers.
|
||||
type ReferencePayload struct {
|
||||
|
@ -61,8 +63,9 @@ func (s *Service) handleEventBranchCreated(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
ReferenceUpdateSegment: ReferenceUpdateSegment{
|
||||
OldSHA: types.NilSHA,
|
||||
|
@ -79,10 +82,13 @@ func (s *Service) handleEventBranchUpdated(ctx context.Context,
|
|||
return s.triggerForEventWithRepo(ctx, enum.WebhookTriggerBranchUpdated,
|
||||
event.ID, event.Payload.PrincipalID, event.Payload.RepoID,
|
||||
func(principal *types.Principal, repo *types.Repository) (any, error) {
|
||||
commitInfo, err := s.fetchCommitInfoForEvent(ctx, repo.GitUID, event.Payload.NewSHA)
|
||||
commitsInfo, totalCommits, err := s.fetchCommitsInfoForEvent(ctx, repo.GitUID,
|
||||
event.Payload.OldSHA, event.Payload.NewSHA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commitInfo := commitsInfo[0]
|
||||
repoInfo := repositoryInfoFrom(repo, s.urlProvider)
|
||||
|
||||
return &ReferencePayload{
|
||||
|
@ -98,8 +104,11 @@ func (s *Service) handleEventBranchUpdated(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.NewSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.NewSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
Commits: &commitsInfo,
|
||||
TotalCommitsCount: totalCommits,
|
||||
},
|
||||
ReferenceUpdateSegment: ReferenceUpdateSegment{
|
||||
OldSHA: event.Payload.OldSHA,
|
||||
|
@ -152,13 +161,46 @@ func (s *Service) fetchCommitInfoForEvent(ctx context.Context, repoUID string, s
|
|||
|
||||
if errors.AsStatus(err) == errors.StatusNotFound {
|
||||
// this could happen if the commit has been deleted and garbage collected by now
|
||||
// or if the sha doesn't point to an event - either way discard the event.
|
||||
return CommitInfo{}, events.NewDiscardEventErrorf("commit with sha '%s' doesn't exist", sha)
|
||||
// or if the targetSha doesn't point to an event - either way discard the event.
|
||||
return CommitInfo{}, events.NewDiscardEventErrorf("commit with targetSha '%s' doesn't exist", sha)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return CommitInfo{}, fmt.Errorf("failed to get commit with sha '%s': %w", sha, err)
|
||||
return CommitInfo{}, fmt.Errorf("failed to get commit with targetSha '%s': %w", sha, err)
|
||||
}
|
||||
|
||||
return commitInfoFrom(out.Commit), nil
|
||||
}
|
||||
|
||||
func (s *Service) fetchCommitsInfoForEvent(
|
||||
ctx context.Context,
|
||||
repoUID string,
|
||||
oldSHA string,
|
||||
newSHA string,
|
||||
) ([]CommitInfo, int, error) {
|
||||
listCommitsParams := git.ListCommitsParams{
|
||||
ReadParams: git.ReadParams{RepoUID: repoUID},
|
||||
GitREF: newSHA,
|
||||
After: oldSHA,
|
||||
Page: 0,
|
||||
Limit: MaxWebhookCommitFileStats,
|
||||
IncludeFileStats: true,
|
||||
}
|
||||
listCommitsOutput, err := s.git.ListCommits(ctx, &listCommitsParams)
|
||||
|
||||
if errors.AsStatus(err) == errors.StatusNotFound {
|
||||
// this could happen if the commit has been deleted and garbage collected by now
|
||||
// or if the targetSha doesn't point to an event - either way discard the event.
|
||||
return []CommitInfo{}, 0, events.NewDiscardEventErrorf("commit with targetSha '%s' doesn't exist", newSHA)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return []CommitInfo{}, 0, fmt.Errorf("failed to get commit with targetSha '%s': %w", newSHA, err)
|
||||
}
|
||||
|
||||
if len(listCommitsOutput.Commits) == 0 {
|
||||
return nil, 0, fmt.Errorf("no commit found between %s and %s", oldSHA, newSHA)
|
||||
}
|
||||
|
||||
return commitsInfoFrom(listCommitsOutput.Commits), listCommitsOutput.TotalCommits, nil
|
||||
}
|
||||
|
|
|
@ -75,8 +75,9 @@ func (s *Service) handleEventPullReqCreated(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
@ -122,8 +123,9 @@ func (s *Service) handleEventPullReqReopened(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
@ -147,10 +149,13 @@ func (s *Service) handleEventPullReqBranchUpdated(ctx context.Context,
|
|||
return s.triggerForEventWithPullReq(ctx, enum.WebhookTriggerPullReqBranchUpdated,
|
||||
event.ID, event.Payload.PrincipalID, event.Payload.PullReqID,
|
||||
func(principal *types.Principal, pr *types.PullReq, targetRepo, sourceRepo *types.Repository) (any, error) {
|
||||
commitInfo, err := s.fetchCommitInfoForEvent(ctx, sourceRepo.GitUID, event.Payload.NewSHA)
|
||||
commitsInfo, totalCommits, err := s.fetchCommitsInfoForEvent(ctx, sourceRepo.GitUID,
|
||||
event.Payload.OldSHA, event.Payload.NewSHA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commitInfo := commitsInfo[0]
|
||||
targetRepoInfo := repositoryInfoFrom(targetRepo, s.urlProvider)
|
||||
sourceRepoInfo := repositoryInfoFrom(sourceRepo, s.urlProvider)
|
||||
|
||||
|
@ -176,8 +181,11 @@ func (s *Service) handleEventPullReqBranchUpdated(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.NewSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.NewSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
Commits: &commitsInfo,
|
||||
TotalCommitsCount: totalCommits,
|
||||
},
|
||||
ReferenceUpdateSegment: ReferenceUpdateSegment{
|
||||
OldSHA: event.Payload.OldSHA,
|
||||
|
@ -230,8 +238,9 @@ func (s *Service) handleEventPullReqClosed(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
@ -280,8 +289,9 @@ func (s *Service) handleEventPullReqMerged(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
@ -336,8 +346,9 @@ func (s *Service) handleEventPullReqComment(
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SourceSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
PullReqCommentSegment: PullReqCommentSegment{
|
||||
CommentInfo: CommentInfo{
|
||||
|
|
|
@ -49,8 +49,9 @@ func (s *Service) handleEventTagCreated(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.SHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.SHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
},
|
||||
ReferenceUpdateSegment: ReferenceUpdateSegment{
|
||||
OldSHA: types.NilSHA,
|
||||
|
@ -67,10 +68,16 @@ func (s *Service) handleEventTagUpdated(ctx context.Context,
|
|||
return s.triggerForEventWithRepo(ctx, enum.WebhookTriggerTagUpdated,
|
||||
event.ID, event.Payload.PrincipalID, event.Payload.RepoID,
|
||||
func(principal *types.Principal, repo *types.Repository) (any, error) {
|
||||
commitInfo, err := s.fetchCommitInfoForEvent(ctx, repo.GitUID, event.Payload.NewSHA)
|
||||
commitsInfo, totalCommits, err := s.fetchCommitsInfoForEvent(ctx, repo.GitUID,
|
||||
event.Payload.OldSHA, event.Payload.NewSHA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commitInfo := CommitInfo{}
|
||||
if len(commitsInfo) > 0 {
|
||||
commitInfo = commitsInfo[0]
|
||||
}
|
||||
repoInfo := repositoryInfoFrom(repo, s.urlProvider)
|
||||
|
||||
return &ReferencePayload{
|
||||
|
@ -86,8 +93,11 @@ func (s *Service) handleEventTagUpdated(ctx context.Context,
|
|||
},
|
||||
},
|
||||
ReferenceDetailsSegment: ReferenceDetailsSegment{
|
||||
SHA: event.Payload.NewSHA,
|
||||
Commit: &commitInfo,
|
||||
SHA: event.Payload.NewSHA,
|
||||
Commit: &commitInfo,
|
||||
HeadCommit: &commitInfo,
|
||||
Commits: &commitsInfo,
|
||||
TotalCommitsCount: totalCommits,
|
||||
},
|
||||
ReferenceUpdateSegment: ReferenceUpdateSegment{
|
||||
OldSHA: event.Payload.OldSHA,
|
||||
|
|
|
@ -42,9 +42,16 @@ type ReferenceSegment struct {
|
|||
Ref ReferenceInfo `json:"ref"`
|
||||
}
|
||||
|
||||
// ReferenceDetailsSegment contains extra defails for reference related payloads for webhooks.
|
||||
// ReferenceDetailsSegment contains extra details for reference related payloads for webhooks.
|
||||
type ReferenceDetailsSegment struct {
|
||||
SHA string `json:"sha"`
|
||||
SHA string `json:"sha"`
|
||||
|
||||
HeadCommit *CommitInfo `json:"head_commit,omitempty"`
|
||||
|
||||
Commits *[]CommitInfo `json:"commits,omitempty"`
|
||||
TotalCommitsCount int `json:"total_commits_count,omitempty"`
|
||||
|
||||
// Deprecated
|
||||
Commit *CommitInfo `json:"commit,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -168,6 +175,10 @@ type CommitInfo struct {
|
|||
Message string `json:"message"`
|
||||
Author SignatureInfo `json:"author"`
|
||||
Committer SignatureInfo `json:"committer"`
|
||||
|
||||
Added []string `json:"added"`
|
||||
Removed []string `json:"removed"`
|
||||
Modified []string `json:"modified"`
|
||||
}
|
||||
|
||||
// commitInfoFrom gets the CommitInfo from a git.Commit.
|
||||
|
@ -177,9 +188,21 @@ func commitInfoFrom(commit git.Commit) CommitInfo {
|
|||
Message: commit.Message,
|
||||
Author: signatureInfoFrom(commit.Author),
|
||||
Committer: signatureInfoFrom(commit.Committer),
|
||||
Added: commit.FileStats.Added,
|
||||
Removed: commit.FileStats.Removed,
|
||||
Modified: commit.FileStats.Modified,
|
||||
}
|
||||
}
|
||||
|
||||
// commitsInfoFrom gets the ExtendedCommitInfo from a []git.Commit.
|
||||
func commitsInfoFrom(commits []git.Commit) []CommitInfo {
|
||||
commitsInfo := make([]CommitInfo, len(commits))
|
||||
for i, commit := range commits {
|
||||
commitsInfo[i] = commitInfoFrom(commit)
|
||||
}
|
||||
return commitsInfo
|
||||
}
|
||||
|
||||
// SignatureInfo describes the commit signature related info for a webhook payload.
|
||||
// NOTE: don't use types package as we want webhook payload to be independent from API calls.
|
||||
type SignatureInfo struct {
|
||||
|
|
|
@ -55,8 +55,14 @@ type Adapter interface {
|
|||
opts *types.WalkReferencesOptions) error
|
||||
GetCommit(ctx context.Context, repoPath string, ref string) (*types.Commit, error)
|
||||
GetCommits(ctx context.Context, repoPath string, refs []string) ([]types.Commit, error)
|
||||
ListCommits(ctx context.Context, repoPath string,
|
||||
ref string, page int, limit int, filter types.CommitFilter) ([]types.Commit, []types.PathRenameDetails, error)
|
||||
ListCommits(
|
||||
ctx context.Context,
|
||||
repoPath string,
|
||||
ref string,
|
||||
page int,
|
||||
limit int,
|
||||
includeFileStats bool,
|
||||
filter types.CommitFilter) ([]types.Commit, []types.PathRenameDetails, error)
|
||||
ListCommitSHAs(ctx context.Context, repoPath string,
|
||||
ref string, page int, limit int, filter types.CommitFilter) ([]string, error)
|
||||
GetLatestCommit(ctx context.Context, repoPath string, ref string, treePath string) (*types.Commit, error)
|
||||
|
|
|
@ -24,9 +24,11 @@ import (
|
|||
|
||||
"github.com/harness/gitness/errors"
|
||||
"github.com/harness/gitness/git/command"
|
||||
"github.com/harness/gitness/git/enum"
|
||||
"github.com/harness/gitness/git/types"
|
||||
|
||||
gitea "code.gitea.io/gitea/modules/git"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// GetLatestCommit gets the latest commit of a path relative from the provided revision.
|
||||
|
@ -135,11 +137,13 @@ func (a Adapter) ListCommitSHAs(
|
|||
// ListCommits lists the commits reachable from ref.
|
||||
// Note: ref & afterRef can be Branch / Tag / CommitSHA.
|
||||
// Note: commits returned are [ref->...->afterRef).
|
||||
func (a Adapter) ListCommits(ctx context.Context,
|
||||
func (a Adapter) ListCommits(
|
||||
ctx context.Context,
|
||||
repoPath string,
|
||||
ref string,
|
||||
page int,
|
||||
limit int,
|
||||
includeFileStats bool,
|
||||
filter types.CommitFilter,
|
||||
) ([]types.Commit, []types.PathRenameDetails, error) {
|
||||
if repoPath == "" {
|
||||
|
@ -169,10 +173,17 @@ func (a Adapter) ListCommits(ctx context.Context,
|
|||
return nil, nil, err
|
||||
}
|
||||
commits[i] = *commit
|
||||
|
||||
if includeFileStats {
|
||||
err = includeFileStatsInCommits(ctx, giteaRepo, commits)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(filter.Path) != 0 {
|
||||
renameDetailsList, err := getRenameDetails(giteaRepo, commits, filter.Path)
|
||||
renameDetailsList, err := getRenameDetails(ctx, giteaRepo, commits, filter.Path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -183,6 +194,52 @@ func (a Adapter) ListCommits(ctx context.Context,
|
|||
return commits, nil, nil
|
||||
}
|
||||
|
||||
func includeFileStatsInCommits(
|
||||
ctx context.Context,
|
||||
giteaRepo *gitea.Repository,
|
||||
commits []types.Commit,
|
||||
) error {
|
||||
for i, commit := range commits {
|
||||
fileStats, err := getFileStats(ctx, giteaRepo, commit.SHA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get file stat: %w", err)
|
||||
}
|
||||
commits[i].FileStats = fileStats
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileStats(
|
||||
ctx context.Context,
|
||||
giteaRepo *gitea.Repository,
|
||||
sha string,
|
||||
) (types.CommitFileStats, error) {
|
||||
changeInfos, err := getChangeInfos(ctx, giteaRepo, sha)
|
||||
if err != nil {
|
||||
return types.CommitFileStats{}, fmt.Errorf("failed to get change infos: %w", err)
|
||||
}
|
||||
fileStats := types.CommitFileStats{
|
||||
Added: make([]string, 0),
|
||||
Removed: make([]string, 0),
|
||||
Modified: make([]string, 0),
|
||||
}
|
||||
for _, c := range changeInfos {
|
||||
switch {
|
||||
case c.ChangeType == enum.FileDiffStatusModified || c.ChangeType == enum.FileDiffStatusRenamed:
|
||||
fileStats.Modified = append(fileStats.Modified, c.Path)
|
||||
case c.ChangeType == enum.FileDiffStatusDeleted:
|
||||
fileStats.Removed = append(fileStats.Removed, c.Path)
|
||||
case c.ChangeType == enum.FileDiffStatusAdded || c.ChangeType == enum.FileDiffStatusCopied:
|
||||
fileStats.Added = append(fileStats.Added, c.Path)
|
||||
case c.ChangeType == enum.FileDiffStatusUndefined:
|
||||
default:
|
||||
log.Ctx(ctx).Warn().Msgf("unknown change type %q for path %q",
|
||||
c.ChangeType, c.Path)
|
||||
}
|
||||
}
|
||||
return fileStats, nil
|
||||
}
|
||||
|
||||
// In case of rename of a file, same commit will be listed twice - Once in old file and second time in new file.
|
||||
// Hence, we are making it a pattern to only list it as part of new file and not as part of old file.
|
||||
func cleanupCommitsForRename(
|
||||
|
@ -203,6 +260,7 @@ func cleanupCommitsForRename(
|
|||
}
|
||||
|
||||
func getRenameDetails(
|
||||
ctx context.Context,
|
||||
giteaRepo *gitea.Repository,
|
||||
commits []types.Commit,
|
||||
path string,
|
||||
|
@ -213,7 +271,7 @@ func getRenameDetails(
|
|||
|
||||
renameDetailsList := make([]types.PathRenameDetails, 0, 2)
|
||||
|
||||
renameDetails, err := giteaGetRenameDetails(giteaRepo, commits[0].SHA, path)
|
||||
renameDetails, err := giteaGetRenameDetails(ctx, giteaRepo, commits[0].SHA, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -226,7 +284,7 @@ func getRenameDetails(
|
|||
return renameDetailsList, nil
|
||||
}
|
||||
|
||||
renameDetailsLast, err := giteaGetRenameDetails(giteaRepo, commits[len(commits)-1].SHA, path)
|
||||
renameDetailsLast, err := giteaGetRenameDetails(ctx, giteaRepo, commits[len(commits)-1].SHA, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -239,10 +297,33 @@ func getRenameDetails(
|
|||
}
|
||||
|
||||
func giteaGetRenameDetails(
|
||||
ctx context.Context,
|
||||
giteaRepo *gitea.Repository,
|
||||
ref string,
|
||||
path string,
|
||||
) (*types.PathRenameDetails, error) {
|
||||
changeInfos, err := getChangeInfos(ctx, giteaRepo, ref)
|
||||
if err != nil {
|
||||
return &types.PathRenameDetails{}, fmt.Errorf("failed to get change infos %w", err)
|
||||
}
|
||||
|
||||
for _, c := range changeInfos {
|
||||
if c.ChangeType == enum.FileDiffStatusRenamed && (c.Path == path || c.NewPath == path) {
|
||||
return &types.PathRenameDetails{
|
||||
OldPath: c.Path,
|
||||
NewPath: c.NewPath,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &types.PathRenameDetails{}, nil
|
||||
}
|
||||
|
||||
func getChangeInfos(
|
||||
ctx context.Context,
|
||||
giteaRepo *gitea.Repository,
|
||||
ref string,
|
||||
) ([]changeInfo, error) {
|
||||
cmd := command.New("log",
|
||||
command.WithArg(ref),
|
||||
command.WithFlag("--name-status"),
|
||||
|
@ -253,38 +334,60 @@ func giteaGetRenameDetails(
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to trigger log command: %w", err)
|
||||
}
|
||||
|
||||
lines := parseLinesToSlice(output.Bytes())
|
||||
|
||||
changeType, oldPath, newPath, err := getFileChangeTypeFromLog(lines, path)
|
||||
changeInfos, err := getFileChangeTypeFromLog(ctx, lines)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changeInfos, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(*changeType, "R") {
|
||||
return &types.PathRenameDetails{
|
||||
OldPath: *oldPath,
|
||||
NewPath: *newPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &types.PathRenameDetails{}, nil
|
||||
type changeInfo struct {
|
||||
ChangeType enum.FileDiffStatus
|
||||
Path string
|
||||
// populated only in case of renames
|
||||
NewPath string
|
||||
}
|
||||
|
||||
func getFileChangeTypeFromLog(
|
||||
ctx context.Context,
|
||||
changeStrings []string,
|
||||
filePath string,
|
||||
) (*string, *string, *string, error) {
|
||||
for _, changeString := range changeStrings {
|
||||
if strings.Contains(changeString, filePath) {
|
||||
changeInfo := strings.Split(changeString, "\t")
|
||||
if len(changeInfo) != 3 {
|
||||
return &changeInfo[0], nil, nil, nil
|
||||
}
|
||||
return &changeInfo[0], &changeInfo[1], &changeInfo[2], nil
|
||||
) ([]changeInfo, error) {
|
||||
changeInfos := make([]changeInfo, len(changeStrings))
|
||||
for i, changeString := range changeStrings {
|
||||
changeStringSplit := strings.Split(changeString, "\t")
|
||||
if len(changeStringSplit) < 1 {
|
||||
return changeInfos, fmt.Errorf("could not parse changeString %q", changeString)
|
||||
}
|
||||
|
||||
c := changeInfo{}
|
||||
c.ChangeType = convertChangeType(ctx, changeStringSplit[0])
|
||||
c.Path = changeStringSplit[1]
|
||||
if len(changeStringSplit) == 3 {
|
||||
c.NewPath = changeStringSplit[2]
|
||||
}
|
||||
changeInfos[i] = c
|
||||
}
|
||||
return changeInfos, nil
|
||||
}
|
||||
|
||||
func convertChangeType(ctx context.Context, c string) enum.FileDiffStatus {
|
||||
switch {
|
||||
case strings.HasPrefix(c, "A"):
|
||||
return enum.FileDiffStatusAdded
|
||||
case strings.HasPrefix(c, "C"):
|
||||
return enum.FileDiffStatusCopied
|
||||
case strings.HasPrefix(c, "D"):
|
||||
return enum.FileDiffStatusDeleted
|
||||
case strings.HasPrefix(c, "M"):
|
||||
return enum.FileDiffStatusModified
|
||||
case strings.HasPrefix(c, "R"):
|
||||
return enum.FileDiffStatusRenamed
|
||||
default:
|
||||
log.Ctx(ctx).Warn().Msgf("encountered unknown change type %s", c)
|
||||
return enum.FileDiffStatusUndefined
|
||||
}
|
||||
return nil, nil, nil, fmt.Errorf("could not parse change for the file '%s'", filePath)
|
||||
}
|
||||
|
||||
// GetCommit returns the (latest) commit for a specific revision.
|
||||
|
|
|
@ -30,11 +30,12 @@ type GetCommitParams struct {
|
|||
}
|
||||
|
||||
type Commit struct {
|
||||
SHA string `json:"sha"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Author Signature `json:"author"`
|
||||
Committer Signature `json:"committer"`
|
||||
SHA string `json:"sha"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Author Signature `json:"author"`
|
||||
Committer Signature `json:"committer"`
|
||||
FileStats CommitFileStats `json:"file_stats,omitempty"`
|
||||
}
|
||||
|
||||
type GetCommitOutput struct {
|
||||
|
@ -105,6 +106,9 @@ type ListCommitsParams struct {
|
|||
|
||||
// Committer allows to filter for commits based on the committer - Optional, ignored if string is empty.
|
||||
Committer string
|
||||
|
||||
// IncludeFileStats allows you to include information about files changed, added and modified.
|
||||
IncludeFileStats bool
|
||||
}
|
||||
|
||||
type RenameDetails struct {
|
||||
|
@ -120,6 +124,12 @@ type ListCommitsOutput struct {
|
|||
TotalCommits int
|
||||
}
|
||||
|
||||
type CommitFileStats struct {
|
||||
Added []string
|
||||
Modified []string
|
||||
Removed []string
|
||||
}
|
||||
|
||||
func (s *Service) ListCommits(ctx context.Context, params *ListCommitsParams) (*ListCommitsOutput, error) {
|
||||
if params == nil {
|
||||
return nil, ErrNoParamsProvided
|
||||
|
@ -133,6 +143,7 @@ func (s *Service) ListCommits(ctx context.Context, params *ListCommitsParams) (*
|
|||
params.GitREF,
|
||||
int(params.Page),
|
||||
int(params.Limit),
|
||||
params.IncludeFileStats,
|
||||
types.CommitFilter{
|
||||
AfterRef: params.After,
|
||||
Path: params.Path,
|
||||
|
|
48
git/diff.go
48
git/diff.go
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/harness/gitness/errors"
|
||||
"github.com/harness/gitness/git/diff"
|
||||
"github.com/harness/gitness/git/enum"
|
||||
"github.com/harness/gitness/git/types"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
@ -296,44 +297,31 @@ func (s *Service) DiffCut(ctx context.Context, params *DiffCutParams) (DiffCutOu
|
|||
}
|
||||
|
||||
type FileDiff struct {
|
||||
SHA string `json:"sha"`
|
||||
OldSHA string `json:"old_sha,omitempty"`
|
||||
Path string `json:"path"`
|
||||
OldPath string `json:"old_path,omitempty"`
|
||||
Status FileDiffStatus `json:"status"`
|
||||
Additions int64 `json:"additions"`
|
||||
Deletions int64 `json:"deletions"`
|
||||
Changes int64 `json:"changes"`
|
||||
Patch []byte `json:"patch,omitempty"`
|
||||
IsBinary bool `json:"is_binary"`
|
||||
IsSubmodule bool `json:"is_submodule"`
|
||||
SHA string `json:"sha"`
|
||||
OldSHA string `json:"old_sha,omitempty"`
|
||||
Path string `json:"path"`
|
||||
OldPath string `json:"old_path,omitempty"`
|
||||
Status enum.FileDiffStatus `json:"status"`
|
||||
Additions int64 `json:"additions"`
|
||||
Deletions int64 `json:"deletions"`
|
||||
Changes int64 `json:"changes"`
|
||||
Patch []byte `json:"patch,omitempty"`
|
||||
IsBinary bool `json:"is_binary"`
|
||||
IsSubmodule bool `json:"is_submodule"`
|
||||
}
|
||||
|
||||
type FileDiffStatus string
|
||||
|
||||
const (
|
||||
// NOTE: keeping values upper case for now to stay consistent with current API.
|
||||
// TODO: change drone/go-scm (and potentially new dependencies) to case insensitive.
|
||||
|
||||
FileDiffStatusUndefined FileDiffStatus = "UNDEFINED"
|
||||
FileDiffStatusAdded FileDiffStatus = "ADDED"
|
||||
FileDiffStatusModified FileDiffStatus = "MODIFIED"
|
||||
FileDiffStatusDeleted FileDiffStatus = "DELETED"
|
||||
FileDiffStatusRenamed FileDiffStatus = "RENAMED"
|
||||
)
|
||||
|
||||
func parseFileDiffStatus(ftype diff.FileType) FileDiffStatus {
|
||||
func parseFileDiffStatus(ftype diff.FileType) enum.FileDiffStatus {
|
||||
switch ftype {
|
||||
case diff.FileAdd:
|
||||
return FileDiffStatusAdded
|
||||
return enum.FileDiffStatusAdded
|
||||
case diff.FileDelete:
|
||||
return FileDiffStatusDeleted
|
||||
return enum.FileDiffStatusDeleted
|
||||
case diff.FileChange:
|
||||
return FileDiffStatusModified
|
||||
return enum.FileDiffStatusModified
|
||||
case diff.FileRename:
|
||||
return FileDiffStatusRenamed
|
||||
return enum.FileDiffStatusRenamed
|
||||
default:
|
||||
return FileDiffStatusUndefined
|
||||
return enum.FileDiffStatusUndefined
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 enum
|
||||
|
||||
type FileDiffStatus string
|
||||
|
||||
const (
|
||||
// NOTE: keeping values upper case for now to stay consistent with current API.
|
||||
// TODO: change drone/go-scm (and potentially new dependencies) to case insensitive.
|
||||
|
||||
FileDiffStatusUndefined FileDiffStatus = "UNDEFINED"
|
||||
FileDiffStatusAdded FileDiffStatus = "ADDED"
|
||||
FileDiffStatusModified FileDiffStatus = "MODIFIED"
|
||||
FileDiffStatusDeleted FileDiffStatus = "DELETED"
|
||||
FileDiffStatusRenamed FileDiffStatus = "RENAMED"
|
||||
FileDiffStatusCopied FileDiffStatus = "COPIED"
|
||||
)
|
|
@ -55,16 +55,24 @@ func mapCommit(c *types.Commit) (*Commit, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to map rpc committer: %w", err)
|
||||
}
|
||||
|
||||
return &Commit{
|
||||
SHA: c.SHA,
|
||||
Title: c.Title,
|
||||
Message: c.Message,
|
||||
Author: *author,
|
||||
Committer: *comitter,
|
||||
FileStats: *mapFileStats(&c.FileStats),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapFileStats(s *types.CommitFileStats) *CommitFileStats {
|
||||
return &CommitFileStats{
|
||||
Added: s.Added,
|
||||
Modified: s.Modified,
|
||||
Removed: s.Removed,
|
||||
}
|
||||
}
|
||||
|
||||
func mapSignature(s *types.Signature) (*Signature, error) {
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("rpc signature is nil")
|
||||
|
|
|
@ -141,11 +141,18 @@ type WalkReferencesOptions struct {
|
|||
}
|
||||
|
||||
type Commit struct {
|
||||
SHA string `json:"sha"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Author Signature `json:"author"`
|
||||
Committer Signature `json:"committer"`
|
||||
SHA string `json:"sha"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Author Signature `json:"author"`
|
||||
Committer Signature `json:"committer"`
|
||||
FileStats CommitFileStats `json:"file_stats,omitempty"`
|
||||
}
|
||||
|
||||
type CommitFileStats struct {
|
||||
Added []string
|
||||
Modified []string
|
||||
Removed []string
|
||||
}
|
||||
|
||||
type Branch struct {
|
||||
|
|
Loading…
Reference in New Issue