mirror of
https://github.com/harness/drone.git
synced 2025-05-06 17:00:54 +08:00
211 lines
5.8 KiB
Go
211 lines
5.8 KiB
Go
// Copyright 2022 Harness Inc. All rights reserved.
|
|
// Use of this source code is governed by the Polyform Free Trial License
|
|
// that can be found in the LICENSE.md file for this repository.
|
|
|
|
package codecomments
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/harness/gitness/gitrpc"
|
|
gitrpcenum "github.com/harness/gitness/gitrpc/enum"
|
|
"github.com/harness/gitness/types"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// Migrator is a utility used to migrate code comments after update of the pull request's source branch.
|
|
type Migrator struct {
|
|
gitRPCClient gitrpc.Interface
|
|
}
|
|
|
|
// MigrateNew updates the "+" (the added lines) part of code comments
|
|
// after a new commit on the pull request's source branch.
|
|
// The parameter newSHA should contain the latest commit SHA of the pull request's source branch.
|
|
func (migrator *Migrator) MigrateNew(
|
|
ctx context.Context,
|
|
repoGitUID string,
|
|
newSHA string,
|
|
comments []*types.CodeComment,
|
|
) {
|
|
migrator.migrate(
|
|
ctx,
|
|
repoGitUID,
|
|
newSHA,
|
|
comments,
|
|
func(codeComment *types.CodeComment) string {
|
|
return codeComment.SourceSHA
|
|
},
|
|
func(codeComment *types.CodeComment, sha string) {
|
|
codeComment.SourceSHA = sha
|
|
},
|
|
func(codeComment *types.CodeComment) (int, int) {
|
|
return codeComment.LineNew, codeComment.LineNew + codeComment.SpanNew - 1
|
|
},
|
|
func(codeComment *types.CodeComment, line int) {
|
|
codeComment.LineNew += line
|
|
},
|
|
)
|
|
}
|
|
|
|
// MigrateOld updates the "-" (the removes lines) part of code comments
|
|
// after the pull request's change of the merge base commit.
|
|
func (migrator *Migrator) MigrateOld(
|
|
ctx context.Context,
|
|
repoGitUID string,
|
|
newSHA string,
|
|
comments []*types.CodeComment,
|
|
) {
|
|
migrator.migrate(
|
|
ctx,
|
|
repoGitUID,
|
|
newSHA,
|
|
comments,
|
|
func(codeComment *types.CodeComment) string {
|
|
return codeComment.MergeBaseSHA
|
|
},
|
|
func(codeComment *types.CodeComment, sha string) {
|
|
codeComment.MergeBaseSHA = sha
|
|
},
|
|
func(codeComment *types.CodeComment) (int, int) {
|
|
return codeComment.LineOld, codeComment.LineOld + codeComment.SpanOld - 1
|
|
},
|
|
func(codeComment *types.CodeComment, line int) {
|
|
codeComment.LineOld += line
|
|
},
|
|
)
|
|
}
|
|
|
|
//nolint:gocognit,funlen // refactor if needed
|
|
func (migrator *Migrator) migrate(
|
|
ctx context.Context,
|
|
repoGitUID string,
|
|
newSHA string,
|
|
comments []*types.CodeComment,
|
|
getSHA func(codeComment *types.CodeComment) string,
|
|
setSHA func(codeComment *types.CodeComment, sha string),
|
|
getCommentStartEnd func(codeComment *types.CodeComment) (int, int),
|
|
updateCommentLine func(codeComment *types.CodeComment, line int),
|
|
) {
|
|
if len(comments) == 0 {
|
|
return
|
|
}
|
|
|
|
commitMap := mapCodeComments(comments, getSHA)
|
|
|
|
for commentSHA, fileMap := range commitMap {
|
|
// get all hunk headers for the diff between the SHA that's stored in the comment and the new SHA.
|
|
diffSummary, errDiff := migrator.gitRPCClient.GetDiffHunkHeaders(ctx, gitrpc.GetDiffHunkHeadersParams{
|
|
ReadParams: gitrpc.ReadParams{
|
|
RepoUID: repoGitUID,
|
|
},
|
|
SourceCommitSHA: commentSHA,
|
|
TargetCommitSHA: newSHA,
|
|
})
|
|
if gitrpc.ErrorStatus(errDiff) == gitrpc.StatusNotFound {
|
|
// Handle the commit SHA not found error and mark all code comments as outdated.
|
|
for _, codeComments := range fileMap {
|
|
for _, codeComment := range codeComments {
|
|
codeComment.Outdated = true
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if errDiff != nil {
|
|
log.Ctx(ctx).Err(errDiff).
|
|
Msgf("failed to get git diff between comment's sha %s and the latest %s", commentSHA, newSHA)
|
|
continue
|
|
}
|
|
|
|
// Traverse all the changed files
|
|
for _, file := range diffSummary.Files {
|
|
var codeComments []*types.CodeComment
|
|
|
|
codeComments = fileMap[file.FileHeader.OldName]
|
|
|
|
// Handle file renames
|
|
if file.FileHeader.OldName != file.FileHeader.NewName {
|
|
if len(codeComments) == 0 {
|
|
// If the code comments are not found using the old name of the file, try with the new name.
|
|
codeComments = fileMap[file.FileHeader.NewName]
|
|
} else {
|
|
// Update the code comment's path to the new file name
|
|
for _, cc := range codeComments {
|
|
cc.Path = file.FileHeader.NewName
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle file delete
|
|
if _, isDeleted := file.FileHeader.Extensions[gitrpcenum.DiffExtHeaderDeletedFileMode]; isDeleted {
|
|
for _, codeComment := range codeComments {
|
|
codeComment.Outdated = true
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Handle new files - shouldn't happen because code comments should exist for a non-existing file.
|
|
if _, isAdded := file.FileHeader.Extensions[gitrpcenum.DiffExtHeaderNewFileMode]; isAdded {
|
|
for _, codeComment := range codeComments {
|
|
codeComment.Outdated = true
|
|
}
|
|
continue
|
|
}
|
|
|
|
for _, hunk := range file.HunkHeaders {
|
|
hunkStart := hunk.NewLine
|
|
hunkEnd := hunk.NewLine + hunk.NewSpan - 1
|
|
for _, cc := range codeComments {
|
|
commentStart, commentEnd := getCommentStartEnd(cc)
|
|
if commentEnd < hunkStart {
|
|
continue
|
|
}
|
|
|
|
outdated := commentStart <= hunkEnd
|
|
cc.Outdated = outdated
|
|
|
|
if outdated {
|
|
continue
|
|
}
|
|
|
|
updateCommentLine(cc, hunk.NewSpan-hunk.OldSpan)
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, codeComments := range fileMap {
|
|
for _, codeComment := range codeComments {
|
|
if codeComment.Outdated {
|
|
continue
|
|
}
|
|
setSHA(codeComment, newSHA)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// mapCodeComments groups code comments to maps, first by commit SHA and then by file name.
|
|
// It assumes the incoming list is already sorted.
|
|
func mapCodeComments(
|
|
comments []*types.CodeComment,
|
|
extractSHA func(*types.CodeComment) string,
|
|
) map[string]map[string][]*types.CodeComment {
|
|
commitMap := map[string]map[string][]*types.CodeComment{}
|
|
for _, comment := range comments {
|
|
commitSHA := extractSHA(comment)
|
|
|
|
fileMap := commitMap[commitSHA]
|
|
if fileMap == nil {
|
|
fileMap = map[string][]*types.CodeComment{}
|
|
}
|
|
|
|
fileComments := fileMap[comment.Path]
|
|
fileComments = append(fileComments, comment)
|
|
fileMap[comment.Path] = fileComments
|
|
|
|
commitMap[commitSHA] = fileMap
|
|
}
|
|
|
|
return commitMap
|
|
}
|