-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Fix automerge will not work because of some events haven't been triggered #30780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f51af18
d145c26
0dc9531
e4e24d2
db6e278
a5d0708
b208bde
c1eac83
3ec2753
b541467
ed361d2
f5e4176
531e8db
9331ccd
8ecfb7d
7d14dfc
51e4888
797236c
5c0f00a
44b306a
ee1aba3
0d4cce7
438d880
fd8dda3
93c9739
deeea79
8331fa0
d9e8a42
e447ec2
8d98ed5
a0adfd5
1c202fa
56914ed
63b74a2
a5f0397
9680940
894537c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ import ( | |
"code.gitea.io/gitea/modules/log" | ||
"code.gitea.io/gitea/modules/process" | ||
"code.gitea.io/gitea/modules/queue" | ||
notify_service "code.gitea.io/gitea/services/notify" | ||
pull_service "code.gitea.io/gitea/services/pull" | ||
) | ||
|
||
|
@@ -30,6 +31,8 @@ var prAutoMergeQueue *queue.WorkerPoolQueue[string] | |
|
||
// Init runs the task queue to that handles auto merges | ||
func Init() error { | ||
notify_service.RegisterNotifier(NewNotifier()) | ||
|
||
prAutoMergeQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_auto_merge", handler) | ||
if prAutoMergeQueue == nil { | ||
return fmt.Errorf("unable to create pr_auto_merge queue") | ||
|
@@ -47,7 +50,7 @@ func handler(items ...string) []string { | |
log.Error("could not parse data from pr_auto_merge queue (%v): %v", s, err) | ||
continue | ||
} | ||
handlePull(id, sha) | ||
handlePullRequestAutoMerge(id, sha) | ||
} | ||
return nil | ||
} | ||
|
@@ -62,16 +65,6 @@ func addToQueue(pr *issues_model.PullRequest, sha string) { | |
// ScheduleAutoMerge if schedule is false and no error, pull can be merged directly | ||
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, style repo_model.MergeStyle, message string) (scheduled bool, err error) { | ||
err = db.WithTx(ctx, func(ctx context.Context) error { | ||
lastCommitStatus, err := pull_service.GetPullRequestCommitStatusState(ctx, pull) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// we don't need to schedule | ||
if lastCommitStatus.IsSuccess() { | ||
return nil | ||
} | ||
|
||
if err := pull_model.ScheduleAutoMerge(ctx, doer, pull.ID, style, message); err != nil { | ||
return err | ||
} | ||
|
@@ -95,8 +88,8 @@ func RemoveScheduledAutoMerge(ctx context.Context, doer *user_model.User, pull * | |
}) | ||
} | ||
|
||
// MergeScheduledPullRequest merges a previously scheduled pull request when all checks succeeded | ||
func MergeScheduledPullRequest(ctx context.Context, sha string, repo *repo_model.Repository) error { | ||
// StartPRCheckAndAutoMergeBySHA start an automerge check and auto merge task for all pull requests of repository and SHA | ||
func StartPRCheckAndAutoMergeBySHA(ctx context.Context, sha string, repo *repo_model.Repository) error { | ||
pulls, err := getPullRequestsByHeadSHA(ctx, sha, repo, func(pr *issues_model.PullRequest) bool { | ||
return !pr.HasMerged && pr.CanAutoMerge() | ||
}) | ||
|
@@ -111,6 +104,32 @@ func MergeScheduledPullRequest(ctx context.Context, sha string, repo *repo_model | |
return nil | ||
} | ||
|
||
// StartPRCheckAndAutoMerge start an automerge check and auto merge task for a pull request | ||
func StartPRCheckAndAutoMerge(ctx context.Context, pull *issues_model.PullRequest) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are two different functions. I think less code are duplicated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I don't see why It seems that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the SHA will prevent when both an automerge task and a newly push are triggered. So the previous triggered automerge task will be ignored.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually such check doesn't seem right and only makes some problem harder to debug. Because, the head commit ID could change again after that check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's not perfect. It needs more refactor like I said before. But since it's a bugfix, I will not do it in this PR. |
||
if pull == nil || pull.HasMerged || !pull.CanAutoMerge() { | ||
return | ||
} | ||
|
||
if err := pull.LoadBaseRepo(ctx); err != nil { | ||
log.Error("LoadBaseRepo: %v", err) | ||
return | ||
} | ||
|
||
gitRepo, err := gitrepo.OpenRepository(ctx, pull.BaseRepo) | ||
if err != nil { | ||
log.Error("OpenRepository: %v", err) | ||
return | ||
} | ||
defer gitRepo.Close() | ||
commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName()) | ||
if err != nil { | ||
log.Error("GetRefCommitID: %v", err) | ||
return | ||
} | ||
|
||
addToQueue(pull, commitID) | ||
} | ||
|
||
func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.Repository, filter func(*issues_model.PullRequest) bool) (map[int64]*issues_model.PullRequest, error) { | ||
gitRepo, err := gitrepo.OpenRepository(ctx, repo) | ||
if err != nil { | ||
|
@@ -161,7 +180,8 @@ func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model. | |
return pulls, nil | ||
} | ||
|
||
func handlePull(pullID int64, sha string) { | ||
// handlePullRequestAutoMerge merge the pull request if all checks are successful | ||
func handlePullRequestAutoMerge(pullID int64, sha string) { | ||
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), | ||
fmt.Sprintf("Handle AutoMerge of PR[%d] with sha[%s]", pullID, sha)) | ||
defer finished() | ||
|
@@ -182,24 +202,50 @@ func handlePull(pullID int64, sha string) { | |
return | ||
} | ||
|
||
if err = pr.LoadBaseRepo(ctx); err != nil { | ||
log.Error("%-v LoadBaseRepo: %v", pr, err) | ||
return | ||
} | ||
|
||
// check the sha is the same as pull request head commit id | ||
baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) | ||
if err != nil { | ||
log.Error("OpenRepository: %v", err) | ||
return | ||
} | ||
defer baseGitRepo.Close() | ||
|
||
headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) | ||
if err != nil { | ||
log.Error("GetRefCommitID: %v", err) | ||
return | ||
} | ||
if headCommitID != sha { | ||
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) | ||
return | ||
} | ||
|
||
// Get all checks for this pr | ||
// We get the latest sha commit hash again to handle the case where the check of a previous push | ||
// did not succeed or was not finished yet. | ||
|
||
if err = pr.LoadHeadRepo(ctx); err != nil { | ||
log.Error("%-v LoadHeadRepo: %v", pr, err) | ||
return | ||
} | ||
|
||
headGitRepo, err := gitrepo.OpenRepository(ctx, pr.HeadRepo) | ||
if err != nil { | ||
log.Error("OpenRepository %-v: %v", pr.HeadRepo, err) | ||
return | ||
var headGitRepo *git.Repository | ||
if pr.BaseRepoID == pr.HeadRepoID { | ||
headGitRepo = baseGitRepo | ||
} else { | ||
headGitRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo) | ||
if err != nil { | ||
log.Error("OpenRepository %-v: %v", pr.HeadRepo, err) | ||
return | ||
} | ||
defer headGitRepo.Close() | ||
} | ||
defer headGitRepo.Close() | ||
|
||
headBranchExist := headGitRepo.IsBranchExist(pr.HeadBranch) | ||
|
||
if pr.HeadRepo == nil || !headBranchExist { | ||
log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch: %s]", pr, pr.HeadRepoID, pr.HeadBranch) | ||
return | ||
|
@@ -238,25 +284,11 @@ func handlePull(pullID int64, sha string) { | |
return | ||
} | ||
|
||
var baseGitRepo *git.Repository | ||
if pr.BaseRepoID == pr.HeadRepoID { | ||
baseGitRepo = headGitRepo | ||
} else { | ||
if err = pr.LoadBaseRepo(ctx); err != nil { | ||
log.Error("%-v LoadBaseRepo: %v", pr, err) | ||
return | ||
} | ||
|
||
baseGitRepo, err = gitrepo.OpenRepository(ctx, pr.BaseRepo) | ||
if err != nil { | ||
log.Error("OpenRepository %-v: %v", pr.BaseRepo, err) | ||
return | ||
} | ||
defer baseGitRepo.Close() | ||
} | ||
|
||
if err := pull_service.Merge(ctx, pr, doer, baseGitRepo, scheduledPRM.MergeStyle, "", scheduledPRM.Message, true); err != nil { | ||
log.Error("pull_service.Merge: %v", err) | ||
// FIXME: if merge failed, we should display some error message to the pull request page. | ||
// The resolution is add a new column on automerge table named `error_message` to store the error message and displayed | ||
// on the pull request page. But this should not be finished in a bug fix PR which will be backport to release branch. | ||
return | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright 2024 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package automerge | ||
|
||
import ( | ||
"context" | ||
|
||
issues_model "code.gitea.io/gitea/models/issues" | ||
user_model "code.gitea.io/gitea/models/user" | ||
"code.gitea.io/gitea/modules/log" | ||
notify_service "code.gitea.io/gitea/services/notify" | ||
) | ||
|
||
type automergeNotifier struct { | ||
notify_service.NullNotifier | ||
} | ||
|
||
var _ notify_service.Notifier = &automergeNotifier{} | ||
|
||
// NewNotifier create a new automergeNotifier notifier | ||
func NewNotifier() notify_service.Notifier { | ||
return &automergeNotifier{} | ||
} | ||
|
||
func (n *automergeNotifier) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { | ||
// as a missing / blocking reviews could have blocked a pending automerge let's recheck | ||
if review.Type == issues_model.ReviewTypeApprove { | ||
if err := StartPRCheckAndAutoMergeBySHA(ctx, review.CommitID, pr.BaseRepo); err != nil { | ||
log.Error("StartPullRequestAutoMergeCheckBySHA: %v", err) | ||
} | ||
} | ||
} | ||
|
||
func (n *automergeNotifier) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { | ||
if err := review.LoadIssue(ctx); err != nil { | ||
log.Error("LoadIssue: %v", err) | ||
return | ||
} | ||
if err := review.Issue.LoadPullRequest(ctx); err != nil { | ||
log.Error("LoadPullRequest: %v", err) | ||
return | ||
} | ||
// as reviews could have blocked a pending automerge let's recheck | ||
StartPRCheckAndAutoMerge(ctx, review.Issue.PullRequest) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.