This repository has been archived by the owner on Feb 14, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
operation.go
190 lines (158 loc) · 5.22 KB
/
operation.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package main
import (
"context"
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/google/go-github/v35/github"
)
func getOpenPullRequestAll(client *github.Client, owner, name string) []*github.PullRequest {
ctx := context.Background()
list, _, err := client.PullRequests.List(ctx, owner, name, &github.PullRequestListOptions{
State: "open",
})
if err != nil {
log.Printf("%v", err)
return nil
}
return list
}
func isRelatedToPushedBranch(pullReqInfo *github.PullRequest, pushedBranchRef string) (ok bool, hasRelationShip bool) {
// Define to explain followings comments:
// * _origin_ is the repository which registers & runs this action.
// * _forked_ is the repository which is forked from _origin_.
//
// We don't have to imagine the case which the user pushed to the branch in _forked_.
// and its branch is opened as the pull requet for _origin_.
// Because _push_ event happens and this action will run only if someone pushed to a branch on _origin_.
//
// By the observation of the behavior of GitHub REST API v3 Pull Requests,
// object's values would be followings if `repository_name:branch_name` is `octocat:master`.
//
// * `.base.ref`
// * The value is `master`.
// * This value is the branch name which the pull request would be merged into.
// even if the pull request is came from _forked_ or the one's parent is arbitary commit.
// * `.base.label` is `octocat:master`.
// * The value is `octocat:master`.
// * This value is same even if the pull request is came from _forked_ or the one's parent is arbitary commit.
//
// By theese result, I think we can judge that the pull request is related to the pushed branch by checking
// whether `.base.ref` is same with the name of the pushed branch.
base := pullReqInfo.GetBase()
if base == nil {
ok = false
return
}
currentBranchName := base.GetRef()
if currentBranchName == "" {
ok = false
log.Println("currentBranchName is empty")
return
}
pushedBranchName := strings.Replace(pushedBranchRef, "refs/heads/", "", -1)
if pushedBranchName == "" {
ok = false
log.Println("pushedBranchName is empty")
return
}
ok = true
hasRelationShip = currentBranchName == pushedBranchName
return
}
func checkAndMarkIfPullRequestUnmergeable(client *github.Client, owner, repo string, oldPR *github.PullRequest, compareURL, labelStatusNeedRebase string) {
number := oldPR.GetNumber()
if number == 0 {
return
}
if hasNeedRebaseLabel(oldPR.Labels, labelStatusNeedRebase) {
log.Printf("#%v has been labeled as %v\n", number, labelStatusNeedRebase)
return
}
// When we get all opened pull requests, GitHub have not checked whether ths PR is unmergeble yet.
// So I think we should retry them.
hasCompleted, shouldMark, err := shouldMarkPullRequestNeedRebase(client, owner, repo, number, labelStatusNeedRebase)
if err != nil {
log.Printf("#%v fails: %v\n", number, err)
return
}
if !hasCompleted {
// retry
time.Sleep(10 * time.Second)
hasCompleted, shouldMark, err = shouldMarkPullRequestNeedRebase(client, owner, repo, number, labelStatusNeedRebase)
if err != nil {
log.Printf("#%v fails: %v\n", number, err)
return
}
}
log.Printf("#%v is hasCompleted: %v, shouldMark: %v\n", number, hasCompleted, shouldMark)
if !hasCompleted {
// give up
return
}
if !shouldMark {
return
}
ctx := context.Background()
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
labels := []string{labelStatusNeedRebase}
_, _, err := client.Issues.AddLabelsToIssue(ctx, owner, repo, number, labels)
if err != nil {
log.Printf("#%v: Fail to add labels: %v\n", number, err)
}
}()
wg.Add(1)
go func() {
defer wg.Done()
body := fmt.Sprintf(`:umbrella: The latest upstream change (presumably [these](%v)) made this pull request unmergeable. Please resolve the merge conflicts.`, compareURL)
_, _, err := client.Issues.CreateComment(ctx, owner, repo, number, &github.IssueComment{
Body: &body,
})
if err != nil {
log.Printf("#%v: Fail to add comments: %v\n", number, err)
}
}()
wg.Wait()
}
func hasNeedRebaseLabel(labels []*github.Label, labelStatusNeedRebase string) bool {
for _, label := range labels {
name := label.GetName()
if name == labelStatusNeedRebase {
return true
}
}
return false
}
func shouldMarkPullRequestNeedRebase(client *github.Client, owner, repo string, number int, labelStatusNeedRebase string) (hasCompleted bool, shouldMark bool, err error) {
ctx := context.Background()
newPRInfoResponse, _, err := client.PullRequests.Get(ctx, owner, repo, number)
if err != nil {
log.Printf("Getting info about #%v was failed.", number)
return
}
hasCompleted, shouldMark = checkWhetherThisPullRequestNeedRebase(newPRInfoResponse, labelStatusNeedRebase)
return
}
func checkWhetherThisPullRequestNeedRebase(pr *github.PullRequest, labelStatusNeedRebase string) (hasCompleted bool, shouldMark bool) {
mergeable := pr.Mergeable
if mergeable == nil {
return
}
hasCompleted = true
// Check again to confirm the other instance of this action's behavior.
if hasNeedRebaseLabel(pr.Labels, labelStatusNeedRebase) {
shouldMark = false
return
}
if *mergeable {
shouldMark = false
} else {
shouldMark = true
}
return
}