forked from apache/incubator-devlake
/
mr_enricher.go
112 lines (103 loc) · 3.12 KB
/
mr_enricher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package tasks
import (
"time"
"github.com/merico-dev/lake/plugins/core"
gitlabModels "github.com/merico-dev/lake/plugins/gitlab/models"
"gorm.io/gorm/clause"
)
var EnrichMergeRequestsMeta = core.SubTaskMeta{
Name: "enrichMrs",
EntryPoint: EnrichMergeRequests,
EnabledByDefault: true,
Description: "Enrich merge requests data from GitlabCommit, GitlabMergeRequestNote and GitlabMergeRequest",
}
func EnrichMergeRequests(taskCtx core.SubTaskContext) error {
data := taskCtx.GetData().(*GitlabTaskData)
db := taskCtx.GetDb()
// get mrs from theDB
cursor, err := db.Model(&gitlabModels.GitlabMergeRequest{}).Where("project_id = ?", data.Options.ProjectId).Rows()
if err != nil {
return err
}
defer cursor.Close()
gitlabMr := &gitlabModels.GitlabMergeRequest{}
for cursor.Next() {
err = db.ScanRows(cursor, gitlabMr)
if err != nil {
return err
}
// enrich first_comment_time field
notes := make([]gitlabModels.GitlabMergeRequestNote, 0)
// `system` = 0 is needed since we only care about human comments
db.Where("merge_request_id = ? AND `system` = 0", gitlabMr.GitlabId).
Order("gitlab_created_at asc").Find(¬es)
commits := make([]gitlabModels.GitlabCommit, 0)
db.Joins("join _tool_gitlab_merge_request_commits gmrc on gmrc.commit_sha = _tool_gitlab_commits.sha").
Where("merge_request_id = ?", gitlabMr.GitlabId).Order("authored_date asc").Find(&commits)
// calculate reviewRounds from commits and notes
reviewRounds := getReviewRounds(commits, notes)
gitlabMr.ReviewRounds = reviewRounds
if len(notes) > 0 {
earliestNote, err := findEarliestNote(notes)
if err != nil {
return err
}
if earliestNote != nil {
gitlabMr.FirstCommentTime = &earliestNote.GitlabCreatedAt
}
}
err = db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(gitlabMr).Error
if err != nil {
return err
}
}
return nil
}
func findEarliestNote(notes []gitlabModels.GitlabMergeRequestNote) (*gitlabModels.GitlabMergeRequestNote, error) {
var earliestNote *gitlabModels.GitlabMergeRequestNote
earliestTime := time.Now()
for i := range notes {
if !notes[i].Resolvable {
continue
}
noteTime := notes[i].GitlabCreatedAt
if noteTime.Before(earliestTime) {
earliestTime = noteTime
earliestNote = ¬es[i]
}
}
return earliestNote, nil
}
func getReviewRounds(commits []gitlabModels.GitlabCommit, notes []gitlabModels.GitlabMergeRequestNote) int {
i := 0
j := 0
reviewRounds := 0
if len(commits) == 0 && len(notes) == 0 {
return 1
}
// state is used to keep track of previous activity
// 0: init, 1: commit, 2: comment
// whenever state is switched to comment, we increment reviewRounds by 1
state := 0 // 0, 1, 2
for i < len(commits) && j < len(notes) {
if commits[i].AuthoredDate.Before(notes[j].GitlabCreatedAt) {
i++
state = 1
} else {
j++
if state != 2 {
reviewRounds++
}
state = 2
}
}
// There's another implicit round of review in 2 scenarios
// One: the last state is commit (state == 1)
// Two: the last state is comment but there're still commits left
if state == 1 || i < len(commits) {
reviewRounds++
}
return reviewRounds
}