This commit is contained in:
@@ -8,20 +8,22 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
git_model "gitea.dev/models/git"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
access_model "gitea.dev/models/perm/access"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/gitrepo"
|
||||
"gitea.dev/modules/globallock"
|
||||
"gitea.dev/modules/graceful"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/repository"
|
||||
)
|
||||
|
||||
// Update updates pull request with base branch.
|
||||
func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string, rebase bool) error {
|
||||
func Update(pr *issues_model.PullRequest, doer *user_model.User, message string, rebase bool) error {
|
||||
ctx := graceful.GetManager().HammerContext() // don't abort the git operation even if the user's request is canceled
|
||||
if pr.Flow == issues_model.PullRequestFlowAGit {
|
||||
// TODO: update of agit flow pull request's head branch is unsupported
|
||||
return errors.New("update of agit flow pull request's head branch is unsupported")
|
||||
@@ -62,20 +64,10 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
|
||||
return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// The code is from https://github.com/go-gitea/gitea/pull/9784,
|
||||
// it seems a simple copy-paste from https://github.com/go-gitea/gitea/pull/7082 without a real reason.
|
||||
// TODO: DUPLICATE-PR-TASK: search and see another TODO comment for more details
|
||||
go AddTestPullRequestTask(TestPullRequestOptions{
|
||||
RepoID: pr.BaseRepo.ID,
|
||||
Doer: doer,
|
||||
Branch: pr.BaseBranch,
|
||||
IsSync: false,
|
||||
IsForcePush: false,
|
||||
OldCommitID: "",
|
||||
NewCommitID: "",
|
||||
})
|
||||
}()
|
||||
// TODO: The code is from https://github.com/go-gitea/gitea/pull/9784,
|
||||
// it seems a simple copy-paste from https://github.com/go-gitea/gitea/pull/7082 without a real reason.
|
||||
// TODO: DUPLICATE-PR-TASK: search and see another TODO comment for more details
|
||||
defer addTestPullRequestTaskAfterWebOperation(pr, doer)
|
||||
|
||||
if rebase {
|
||||
return updateHeadByRebaseOnToBase(ctx, pr, doer)
|
||||
@@ -97,6 +89,13 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
|
||||
}
|
||||
|
||||
_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message, repository.PushTriggerPRUpdateWithBase)
|
||||
// TODO: the "update" (merge target branch to PR head branch) operation has finished, there could still be some edge cases:
|
||||
// * the database was already out of sync: the target branch was already in head branch:
|
||||
// * so no post-receive hook is really executed, no PR status update
|
||||
// * then the PR status is stuck in "behind the target branch" (a new push can be used as a workaround)
|
||||
// * "merge" operation does finish, but the post-receive hook isn't correctly executed due to other reasons:
|
||||
// * although the target branch is merged into head branch by this "update" (head branch receives new commits)
|
||||
// * but database isn't updated, so the PR status is still "behind the target branch"
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -131,74 +130,82 @@ func isUserAllowedToPushOrForcePushInRepoBranch(ctx context.Context, user *user_
|
||||
return pushAllowed, forcePushAllowed, nil
|
||||
}
|
||||
|
||||
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
|
||||
// CheckUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
|
||||
// update PR means send new commits to PR head branch from base branch
|
||||
func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (pushAllowed, rebaseAllowed bool, err error) {
|
||||
func CheckUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (ret struct {
|
||||
MergeAllowed, RebaseAllowed bool
|
||||
DefaultUpdateStyle repo_model.UpdateStyle
|
||||
}, err error,
|
||||
) {
|
||||
if user == nil {
|
||||
return false, false, nil
|
||||
return ret, nil
|
||||
}
|
||||
if err := pull.LoadBaseRepo(ctx); err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
if err := pull.LoadHeadRepo(ctx); err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// 1. check whether pull request enabled.
|
||||
prBaseUnit, err := pull.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
|
||||
if repo_model.IsErrUnitTypeNotExist(err) {
|
||||
return false, false, nil // the PR unit is disabled in base repo means no update allowed
|
||||
return ret, nil // the PR unit is disabled in base repo means no update allowed
|
||||
} else if err != nil {
|
||||
return false, false, fmt.Errorf("get base repo unit: %v", err)
|
||||
return ret, fmt.Errorf("get base repo unit: %v", err)
|
||||
}
|
||||
|
||||
// 2. only support Github style pull request
|
||||
if pull.Flow == issues_model.PullRequestFlowAGit {
|
||||
return false, false, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 3. check user push permission on head repository
|
||||
pushAllowed, rebaseAllowed, err = isUserAllowedToPushOrForcePushInRepoBranch(ctx, user, pull.HeadRepo, pull.HeadBranch)
|
||||
ret.MergeAllowed, ret.RebaseAllowed, err = isUserAllowedToPushOrForcePushInRepoBranch(ctx, user, pull.HeadRepo, pull.HeadBranch)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// 4. if the pull creator allows maintainer to edit, we need to check whether
|
||||
// user is a maintainer (has permission to merge into base branch) and inherit pull request poster's permission
|
||||
if pull.AllowMaintainerEdit && (!pushAllowed || !rebaseAllowed) {
|
||||
if pull.AllowMaintainerEdit && (!ret.MergeAllowed || !ret.RebaseAllowed) {
|
||||
baseRepoPerm, err := access_model.GetDoerRepoPermission(ctx, pull.BaseRepo, user)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
userAllowedToMergePR, err := isUserAllowedToMergeInRepoBranch(ctx, pull.BaseRepoID, pull.BaseBranch, baseRepoPerm, user)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
if userAllowedToMergePR {
|
||||
// the user is maintainer (can merge PR), and this PR is allowed to be edited by maintainers,
|
||||
// then the user should inherit the PR poster's push/rebase permission for the head branch
|
||||
if err := pull.LoadIssue(ctx); err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
if err := pull.Issue.LoadPoster(ctx); err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
posterPushAllowed, posterRebaseAllowed, err := isUserAllowedToPushOrForcePushInRepoBranch(ctx, pull.Issue.Poster, pull.HeadRepo, pull.HeadBranch)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
return ret, err
|
||||
}
|
||||
if !pushAllowed {
|
||||
pushAllowed = posterPushAllowed
|
||||
if !ret.MergeAllowed {
|
||||
ret.MergeAllowed = posterPushAllowed
|
||||
}
|
||||
if !rebaseAllowed {
|
||||
rebaseAllowed = posterRebaseAllowed
|
||||
if !ret.RebaseAllowed {
|
||||
ret.RebaseAllowed = posterRebaseAllowed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. check base repository's AllowRebaseUpdate configuration
|
||||
// it is a config in base repo but controls the head (fork) repo's "Update" behavior
|
||||
return pushAllowed, rebaseAllowed && prBaseUnit.PullRequestsConfig().AllowRebaseUpdate, nil
|
||||
// 5. apply base repository's update configuration; it is a config on the base repo,
|
||||
// but it controls the head (fork) repo's "Update" behavior.
|
||||
prConfig := prBaseUnit.PullRequestsConfig()
|
||||
ret.MergeAllowed = ret.MergeAllowed && prConfig.AllowMergeUpdate
|
||||
ret.RebaseAllowed = ret.RebaseAllowed && prConfig.AllowRebaseUpdate
|
||||
ret.DefaultUpdateStyle = prConfig.DefaultUpdateStyle
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func syncCommitDivergence(ctx context.Context, pr *issues_model.PullRequest) error {
|
||||
|
||||
Reference in New Issue
Block a user