@@ -22,6 +22,7 @@ import (
22
22
"code.gitea.io/gitea/modules/log"
23
23
"code.gitea.io/gitea/modules/process"
24
24
"code.gitea.io/gitea/modules/queue"
25
+ notify_service "code.gitea.io/gitea/services/notify"
25
26
pull_service "code.gitea.io/gitea/services/pull"
26
27
)
27
28
@@ -30,6 +31,8 @@ var prAutoMergeQueue *queue.WorkerPoolQueue[string]
30
31
31
32
// Init runs the task queue to that handles auto merges
32
33
func Init () error {
34
+ notify_service .RegisterNotifier (NewNotifier ())
35
+
33
36
prAutoMergeQueue = queue .CreateUniqueQueue (graceful .GetManager ().ShutdownContext (), "pr_auto_merge" , handler )
34
37
if prAutoMergeQueue == nil {
35
38
return fmt .Errorf ("unable to create pr_auto_merge queue" )
@@ -47,7 +50,7 @@ func handler(items ...string) []string {
47
50
log .Error ("could not parse data from pr_auto_merge queue (%v): %v" , s , err )
48
51
continue
49
52
}
50
- handlePull (id , sha )
53
+ handlePullRequestAutoMerge (id , sha )
51
54
}
52
55
return nil
53
56
}
@@ -62,16 +65,6 @@ func addToQueue(pr *issues_model.PullRequest, sha string) {
62
65
// ScheduleAutoMerge if schedule is false and no error, pull can be merged directly
63
66
func ScheduleAutoMerge (ctx context.Context , doer * user_model.User , pull * issues_model.PullRequest , style repo_model.MergeStyle , message string ) (scheduled bool , err error ) {
64
67
err = db .WithTx (ctx , func (ctx context.Context ) error {
65
- lastCommitStatus , err := pull_service .GetPullRequestCommitStatusState (ctx , pull )
66
- if err != nil {
67
- return err
68
- }
69
-
70
- // we don't need to schedule
71
- if lastCommitStatus .IsSuccess () {
72
- return nil
73
- }
74
-
75
68
if err := pull_model .ScheduleAutoMerge (ctx , doer , pull .ID , style , message ); err != nil {
76
69
return err
77
70
}
@@ -95,8 +88,8 @@ func RemoveScheduledAutoMerge(ctx context.Context, doer *user_model.User, pull *
95
88
})
96
89
}
97
90
98
- // MergeScheduledPullRequest merges a previously scheduled pull request when all checks succeeded
99
- func MergeScheduledPullRequest (ctx context.Context , sha string , repo * repo_model.Repository ) error {
91
+ // StartPRCheckAndAutoMergeBySHA start an automerge check and auto merge task for all pull requests of repository and SHA
92
+ func StartPRCheckAndAutoMergeBySHA (ctx context.Context , sha string , repo * repo_model.Repository ) error {
100
93
pulls , err := getPullRequestsByHeadSHA (ctx , sha , repo , func (pr * issues_model.PullRequest ) bool {
101
94
return ! pr .HasMerged && pr .CanAutoMerge ()
102
95
})
@@ -111,6 +104,32 @@ func MergeScheduledPullRequest(ctx context.Context, sha string, repo *repo_model
111
104
return nil
112
105
}
113
106
107
+ // StartPRCheckAndAutoMerge start an automerge check and auto merge task for a pull request
108
+ func StartPRCheckAndAutoMerge (ctx context.Context , pull * issues_model.PullRequest ) {
109
+ if pull == nil || pull .HasMerged || ! pull .CanAutoMerge () {
110
+ return
111
+ }
112
+
113
+ if err := pull .LoadBaseRepo (ctx ); err != nil {
114
+ log .Error ("LoadBaseRepo: %v" , err )
115
+ return
116
+ }
117
+
118
+ gitRepo , err := gitrepo .OpenRepository (ctx , pull .BaseRepo )
119
+ if err != nil {
120
+ log .Error ("OpenRepository: %v" , err )
121
+ return
122
+ }
123
+ defer gitRepo .Close ()
124
+ commitID , err := gitRepo .GetRefCommitID (pull .GetGitRefName ())
125
+ if err != nil {
126
+ log .Error ("GetRefCommitID: %v" , err )
127
+ return
128
+ }
129
+
130
+ addToQueue (pull , commitID )
131
+ }
132
+
114
133
func getPullRequestsByHeadSHA (ctx context.Context , sha string , repo * repo_model.Repository , filter func (* issues_model.PullRequest ) bool ) (map [int64 ]* issues_model.PullRequest , error ) {
115
134
gitRepo , err := gitrepo .OpenRepository (ctx , repo )
116
135
if err != nil {
@@ -161,7 +180,8 @@ func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.
161
180
return pulls , nil
162
181
}
163
182
164
- func handlePull (pullID int64 , sha string ) {
183
+ // handlePullRequestAutoMerge merge the pull request if all checks are successful
184
+ func handlePullRequestAutoMerge (pullID int64 , sha string ) {
165
185
ctx , _ , finished := process .GetManager ().AddContext (graceful .GetManager ().HammerContext (),
166
186
fmt .Sprintf ("Handle AutoMerge of PR[%d] with sha[%s]" , pullID , sha ))
167
187
defer finished ()
@@ -182,24 +202,50 @@ func handlePull(pullID int64, sha string) {
182
202
return
183
203
}
184
204
205
+ if err = pr .LoadBaseRepo (ctx ); err != nil {
206
+ log .Error ("%-v LoadBaseRepo: %v" , pr , err )
207
+ return
208
+ }
209
+
210
+ // check the sha is the same as pull request head commit id
211
+ baseGitRepo , err := gitrepo .OpenRepository (ctx , pr .BaseRepo )
212
+ if err != nil {
213
+ log .Error ("OpenRepository: %v" , err )
214
+ return
215
+ }
216
+ defer baseGitRepo .Close ()
217
+
218
+ headCommitID , err := baseGitRepo .GetRefCommitID (pr .GetGitRefName ())
219
+ if err != nil {
220
+ log .Error ("GetRefCommitID: %v" , err )
221
+ return
222
+ }
223
+ if headCommitID != sha {
224
+ log .Warn ("Head commit id of auto merge %-v does not match sha [%s], it may means the head branch has been updated. Just ignore this request because a new request expected in the queue" , pr , sha )
225
+ return
226
+ }
227
+
185
228
// Get all checks for this pr
186
229
// We get the latest sha commit hash again to handle the case where the check of a previous push
187
230
// did not succeed or was not finished yet.
188
-
189
231
if err = pr .LoadHeadRepo (ctx ); err != nil {
190
232
log .Error ("%-v LoadHeadRepo: %v" , pr , err )
191
233
return
192
234
}
193
235
194
- headGitRepo , err := gitrepo .OpenRepository (ctx , pr .HeadRepo )
195
- if err != nil {
196
- log .Error ("OpenRepository %-v: %v" , pr .HeadRepo , err )
197
- return
236
+ var headGitRepo * git.Repository
237
+ if pr .BaseRepoID == pr .HeadRepoID {
238
+ headGitRepo = baseGitRepo
239
+ } else {
240
+ headGitRepo , err = gitrepo .OpenRepository (ctx , pr .HeadRepo )
241
+ if err != nil {
242
+ log .Error ("OpenRepository %-v: %v" , pr .HeadRepo , err )
243
+ return
244
+ }
245
+ defer headGitRepo .Close ()
198
246
}
199
- defer headGitRepo .Close ()
200
247
201
248
headBranchExist := headGitRepo .IsBranchExist (pr .HeadBranch )
202
-
203
249
if pr .HeadRepo == nil || ! headBranchExist {
204
250
log .Warn ("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch: %s]" , pr , pr .HeadRepoID , pr .HeadBranch )
205
251
return
@@ -238,25 +284,11 @@ func handlePull(pullID int64, sha string) {
238
284
return
239
285
}
240
286
241
- var baseGitRepo * git.Repository
242
- if pr .BaseRepoID == pr .HeadRepoID {
243
- baseGitRepo = headGitRepo
244
- } else {
245
- if err = pr .LoadBaseRepo (ctx ); err != nil {
246
- log .Error ("%-v LoadBaseRepo: %v" , pr , err )
247
- return
248
- }
249
-
250
- baseGitRepo , err = gitrepo .OpenRepository (ctx , pr .BaseRepo )
251
- if err != nil {
252
- log .Error ("OpenRepository %-v: %v" , pr .BaseRepo , err )
253
- return
254
- }
255
- defer baseGitRepo .Close ()
256
- }
257
-
258
287
if err := pull_service .Merge (ctx , pr , doer , baseGitRepo , scheduledPRM .MergeStyle , "" , scheduledPRM .Message , true ); err != nil {
259
288
log .Error ("pull_service.Merge: %v" , err )
289
+ // FIXME: if merge failed, we should display some error message to the pull request page.
290
+ // The resolution is add a new column on automerge table named `error_message` to store the error message and displayed
291
+ // on the pull request page. But this should not be finished in a bug fix PR which will be backport to release branch.
260
292
return
261
293
}
262
294
}
0 commit comments