mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-05-31 20:02:09 +00:00
Move database operations of merging a pull request to post receive hook and add a transaction (#30805)
Merging PR may fail because of various problems. The pull request may
have a dirty state because there is no transaction when merging a pull
request. ref
https://github.com/go-gitea/gitea/pull/25741#issuecomment-2074126393
This PR moves all database update operations to post-receive handler for
merging a pull request and having a database transaction. That means if
database operations fail, then the git merging will fail, the git client
will get a fail result.
There are already many tests for pull request merging, so we don't need
to add a new one.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit ebf0c969403d91ed80745ff5bd7dfbdb08174fc7)
Conflicts:
modules/private/hook.go
routers/private/hook_post_receive.go
trivial conflicts because
263a716cb5 * Performance optimization for git push (#30104)
was not cherry-picked and because of
998a431747
Do not update PRs based on events that happened before they existed
This commit is contained in:
parent
1f56a49f28
commit
eb792d9f8a
8 changed files with 150 additions and 18 deletions
|
@ -4,20 +4,26 @@
|
|||
package private
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
pull_model "code.gitea.io/gitea/models/pull"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
timeutil "code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
|
@ -155,6 +161,14 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
|||
}
|
||||
}
|
||||
|
||||
// handle pull request merging, a pull request action should push at least 1 commit
|
||||
if opts.PushTrigger == repo_module.PushTriggerPRMergeToBase {
|
||||
handlePullRequestMerging(ctx, opts, ownerName, repoName, updates)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Push Options
|
||||
if len(opts.GitPushOptions) > 0 {
|
||||
// load the repository
|
||||
|
@ -302,3 +316,52 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
|||
RepoWasEmpty: wasEmpty,
|
||||
})
|
||||
}
|
||||
|
||||
func loadContextCacheUser(ctx context.Context, id int64) (*user_model.User, error) {
|
||||
return cache.GetWithContextCache(ctx, "hook_post_receive_user", id, func() (*user_model.User, error) {
|
||||
return user_model.GetUserByID(ctx, id)
|
||||
})
|
||||
}
|
||||
|
||||
// handlePullRequestMerging handle pull request merging, a pull request action should push at least 1 commit
|
||||
func handlePullRequestMerging(ctx *gitea_context.PrivateContext, opts *private.HookOptions, ownerName, repoName string, updates []*repo_module.PushUpdateOptions) {
|
||||
if len(updates) == 0 {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Pushing a merged PR (pr:%d) no commits pushed ", opts.PullRequestID),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetPullRequestByID(ctx, opts.PullRequestID)
|
||||
if err != nil {
|
||||
log.Error("GetPullRequestByID[%d]: %v", opts.PullRequestID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "GetPullRequestByID failed"})
|
||||
return
|
||||
}
|
||||
|
||||
pusher, err := loadContextCacheUser(ctx, opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Load pusher user failed"})
|
||||
return
|
||||
}
|
||||
|
||||
pr.MergedCommitID = updates[len(updates)-1].NewCommitID
|
||||
pr.MergedUnix = timeutil.TimeStampNow()
|
||||
pr.Merger = pusher
|
||||
pr.MergerID = pusher.ID
|
||||
err = db.WithTx(ctx, func(ctx context.Context) error {
|
||||
// Removing an auto merge pull and ignore if not exist
|
||||
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
|
||||
return fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", opts.PullRequestID, err)
|
||||
}
|
||||
if _, err := pr.SetMerged(ctx); err != nil {
|
||||
return fmt.Errorf("SetMerged failed: %s/%s Error: %v", ownerName, repoName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Failed to update PR to merged: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to update PR to merged"})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue