/
detect_unmergeable.go
140 lines (114 loc) · 3.76 KB
/
detect_unmergeable.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package epic
import (
"context"
"log"
"sync"
"github.com/google/go-github/v24/github"
"github.com/student-kyushu/frau/operation"
)
const masterBranchName = "master"
func DetectUnmergeablePR(ctx context.Context, client *github.Client, ev *github.PushEvent) {
// At this moment, we only care a pull request which are looking master branch.
if *ev.Ref != "refs/heads/"+masterBranchName {
log.Printf("info: pushed branch is not related to me: %v\n", *ev.Ref)
return
}
repoOwner := *ev.Repo.Owner.Name
log.Printf("debug: repository owner is %v\n", repoOwner)
repo := *ev.Repo.Name
log.Printf("debug: repository name is %v\n", repo)
prSvc := client.PullRequests
prList, _, err := prSvc.List(ctx, repoOwner, repo, &github.PullRequestListOptions{
State: "open",
})
if err != nil {
log.Println("warn: could not fetch opened pull requests")
return
}
compare := *ev.Compare
comment := ":umbrella: The latest upstream change (presumably [these](" + compare + ")) made this pull request unmergeable. Please resolve the merge conflicts."
// Restrict the number of Goroutine which checks unmergeables
// to avoid the API limits at a moment.
const maxConcurrency int = 8
semaphore := make(chan int, maxConcurrency)
wg := &sync.WaitGroup{}
for _, item := range prList {
wg.Add(1)
go markUnmergeable(ctx, wg, &markUnmergeableInfo{
client.Issues,
prSvc,
repoOwner,
repo,
*item.Number,
comment,
semaphore,
})
}
wg.Wait()
}
type markUnmergeableInfo struct {
issueSvc *github.IssuesService
prSvc *github.PullRequestsService
RepoOwner string
Repo string
Number int
Comment string
semaphore chan int
}
func markUnmergeable(ctx context.Context, wg *sync.WaitGroup, info *markUnmergeableInfo) {
info.semaphore <- 0 // wait until the internal buffer takes a space.
var err error
defer wg.Done()
defer func() {
<-info.semaphore // release the space of the internal buffer
if err != nil {
log.Printf("error: %v\n", err)
}
}()
issueSvc := info.issueSvc
repoOwner := info.RepoOwner
log.Printf("debug: repository owner is %v\n", repoOwner)
repo := info.Repo
log.Printf("debug: repository name is %v\n", repo)
number := info.Number
log.Printf("debug: pull request number is %v\n", number)
pr, _, err := info.prSvc.Get(ctx, repoOwner, repo, number)
if err != nil || pr == nil {
log.Println("info: could not get the info for pull request")
log.Printf("debug: %v\n", err)
return
}
if !operation.IsRelatedToMaster(pr, repoOwner, masterBranchName) {
log.Printf("info: #%v is not related to `%v` branch", number, masterBranchName)
return
}
currentLabels := operation.GetLabelsByIssue(ctx, issueSvc, repoOwner, repo, number)
if currentLabels == nil {
return
}
// We don't have to warn to a pull request which have been marked as unmergeable.
if operation.HasLabelInList(currentLabels, operation.LABEL_NEEDS_REBASE) {
log.Printf("info: #%v has marked as 'should rebase on the latest master'.\n", number)
return
}
ok, mergeable := operation.IsMergeable(ctx, info.prSvc, repoOwner, repo, number, pr)
if !ok {
log.Printf("info: We treat #%v as 'mergeable' to avoid miss detection because we could not fetch the pr info,\n", number)
return
}
if mergeable {
log.Printf("info: do not have to mark %v as 'unmergeable'\n", number)
return
}
if ok := operation.AddComment(ctx, issueSvc, repoOwner, repo, number, info.Comment); !ok {
log.Printf("info: could not create the comment about unmergeables to #%v\n", number)
return
}
labels := operation.AddNeedRebaseLabel(currentLabels)
log.Printf("debug: the changed labels: %v of #%v\n", labels, number)
_, _, err = issueSvc.ReplaceLabelsForIssue(ctx, repoOwner, repo, number, labels)
if err != nil {
log.Printf("could not change labels of #%v\n", number)
return
}
}