mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.4 KiB
143 lines
4.4 KiB
// Copyright 2019 The Gitea Authors. |
|
// All rights reserved. |
|
// SPDX-License-Identifier: MIT |
|
|
|
package pull |
|
|
|
import ( |
|
"context" |
|
|
|
"code.gitea.io/gitea/models/db" |
|
git_model "code.gitea.io/gitea/models/git" |
|
issues_model "code.gitea.io/gitea/models/issues" |
|
"code.gitea.io/gitea/modules/gitrepo" |
|
"code.gitea.io/gitea/modules/log" |
|
"code.gitea.io/gitea/modules/structs" |
|
|
|
"github.com/gobwas/glob" |
|
"github.com/pkg/errors" |
|
) |
|
|
|
// MergeRequiredContextsCommitStatus returns a commit status state for given required contexts |
|
func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) structs.CommitStatusState { |
|
// matchedCount is the number of `CommitStatus.Context` that match any context of `requiredContexts` |
|
matchedCount := 0 |
|
returnedStatus := structs.CommitStatusSuccess |
|
|
|
if len(requiredContexts) > 0 { |
|
requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts)) |
|
for _, ctx := range requiredContexts { |
|
if gp, err := glob.Compile(ctx); err != nil { |
|
log.Error("glob.Compile %s failed. Error: %v", ctx, err) |
|
} else { |
|
requiredContextsGlob[ctx] = gp |
|
} |
|
} |
|
|
|
for _, gp := range requiredContextsGlob { |
|
var targetStatus structs.CommitStatusState |
|
for _, commitStatus := range commitStatuses { |
|
if gp.Match(commitStatus.Context) { |
|
targetStatus = commitStatus.State |
|
matchedCount++ |
|
break |
|
} |
|
} |
|
|
|
// If required rule not match any action, then it is pending |
|
if targetStatus == "" { |
|
if structs.CommitStatusPending.HasHigherPriorityThan(returnedStatus) { |
|
returnedStatus = structs.CommitStatusPending |
|
} |
|
break |
|
} |
|
|
|
if targetStatus.HasHigherPriorityThan(returnedStatus) { |
|
returnedStatus = targetStatus |
|
} |
|
} |
|
} |
|
|
|
if matchedCount == 0 && returnedStatus == structs.CommitStatusSuccess { |
|
if len(commitStatuses) == 0 { |
|
// "no statuses" should mean "pending" |
|
return structs.CommitStatusPending |
|
} |
|
status := git_model.CalcCommitStatus(commitStatuses) |
|
if status.State == structs.CommitStatusSkipped { |
|
return structs.CommitStatusSuccess // if all statuses are skipped, return success |
|
} |
|
return status.State |
|
} |
|
|
|
return returnedStatus |
|
} |
|
|
|
// IsPullCommitStatusPass returns if all required status checks PASS |
|
func IsPullCommitStatusPass(ctx context.Context, pr *issues_model.PullRequest) (bool, error) { |
|
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) |
|
if err != nil { |
|
return false, errors.Wrap(err, "GetLatestCommitStatus") |
|
} |
|
if pb == nil || !pb.EnableStatusCheck { |
|
return true, nil |
|
} |
|
|
|
state, err := GetPullRequestCommitStatusState(ctx, pr) |
|
if err != nil { |
|
return false, err |
|
} |
|
return state.IsSuccess(), nil |
|
} |
|
|
|
// GetPullRequestCommitStatusState returns pull request merged commit status state |
|
func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullRequest) (structs.CommitStatusState, error) { |
|
// Ensure HeadRepo is loaded |
|
if err := pr.LoadHeadRepo(ctx); err != nil { |
|
return "", errors.Wrap(err, "LoadHeadRepo") |
|
} |
|
|
|
// check if all required status checks are successful |
|
headGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.HeadRepo) |
|
if err != nil { |
|
return "", errors.Wrap(err, "OpenRepository") |
|
} |
|
defer closer.Close() |
|
|
|
if pr.Flow == issues_model.PullRequestFlowGithub && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) { |
|
return "", errors.New("Head branch does not exist, can not merge") |
|
} |
|
if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) { |
|
return "", errors.New("Head branch does not exist, can not merge") |
|
} |
|
|
|
var sha string |
|
if pr.Flow == issues_model.PullRequestFlowGithub { |
|
sha, err = headGitRepo.GetBranchCommitID(pr.HeadBranch) |
|
} else { |
|
sha, err = headGitRepo.GetRefCommitID(pr.GetGitRefName()) |
|
} |
|
if err != nil { |
|
return "", err |
|
} |
|
|
|
if err := pr.LoadBaseRepo(ctx); err != nil { |
|
return "", errors.Wrap(err, "LoadBaseRepo") |
|
} |
|
|
|
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll) |
|
if err != nil { |
|
return "", errors.Wrap(err, "GetLatestCommitStatus") |
|
} |
|
|
|
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) |
|
if err != nil { |
|
return "", errors.Wrap(err, "LoadProtectedBranch") |
|
} |
|
var requiredContexts []string |
|
if pb != nil { |
|
requiredContexts = pb.StatusCheckContexts |
|
} |
|
|
|
return MergeRequiredContextsCommitStatus(commitStatuses, requiredContexts), nil |
|
}
|
|
|