mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-30 22:11:07 +00:00 
			
		
		
		
	- There's no need to use `github.com/pkg/errors` when the standard library already has the functionality to wrap and create errors. (cherry picked from commit40f603a538) (cherry picked from commitaa68a2753f) (cherry picked from commit48e252d739) (cherry picked from commitcc6f40ccd2) (cherry picked from commit03c4b97358) (cherry picked from commitf25eeb7695) (cherry picked from commit989d8fa1cb) (cherry picked from commit10e890ed8e)
		
			
				
	
	
		
			162 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2019 The Gitea Authors.
 | |
| // All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package pull
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 
 | |
| 	"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/git"
 | |
| 	"code.gitea.io/gitea/modules/log"
 | |
| 	"code.gitea.io/gitea/modules/structs"
 | |
| 
 | |
| 	"github.com/gobwas/glob"
 | |
| )
 | |
| 
 | |
| // 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 _, commitStatus := range commitStatuses {
 | |
| 			var targetStatus structs.CommitStatusState
 | |
| 			for _, gp := range requiredContextsGlob {
 | |
| 				if gp.Match(commitStatus.Context) {
 | |
| 					targetStatus = commitStatus.State
 | |
| 					matchedCount++
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if targetStatus != "" && targetStatus.NoBetterThan(returnedStatus) {
 | |
| 				returnedStatus = targetStatus
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if matchedCount == 0 {
 | |
| 		status := git_model.CalcCommitStatus(commitStatuses)
 | |
| 		if status != nil {
 | |
| 			return status.State
 | |
| 		}
 | |
| 		return structs.CommitStatusSuccess
 | |
| 	}
 | |
| 
 | |
| 	return returnedStatus
 | |
| }
 | |
| 
 | |
| // IsCommitStatusContextSuccess returns true if all required status check contexts succeed.
 | |
| func IsCommitStatusContextSuccess(commitStatuses []*git_model.CommitStatus, requiredContexts []string) bool {
 | |
| 	// If no specific context is required, require that last commit status is a success
 | |
| 	if len(requiredContexts) == 0 {
 | |
| 		status := git_model.CalcCommitStatus(commitStatuses)
 | |
| 		if status == nil || status.State != structs.CommitStatusSuccess {
 | |
| 			return false
 | |
| 		}
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	for _, ctx := range requiredContexts {
 | |
| 		var found bool
 | |
| 		for _, commitStatus := range commitStatuses {
 | |
| 			if commitStatus.Context == ctx {
 | |
| 				if commitStatus.State != structs.CommitStatusSuccess {
 | |
| 					return false
 | |
| 				}
 | |
| 
 | |
| 				found = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !found {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // 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, fmt.Errorf("GetFirstMatchProtectedBranchRule: %w", err)
 | |
| 	}
 | |
| 	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 "", fmt.Errorf("LoadHeadRepo: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// check if all required status checks are successful
 | |
| 	headGitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath())
 | |
| 	if err != nil {
 | |
| 		return "", fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
 | |
| 	}
 | |
| 	defer closer.Close()
 | |
| 
 | |
| 	if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
 | |
| 		return "", errors.New("head branch does not exist, can not merge")
 | |
| 	}
 | |
| 	if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, 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 "", fmt.Errorf("LoadBaseRepo: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
 | |
| 	if err != nil {
 | |
| 		return "", fmt.Errorf("GetLatestCommitStatus: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
 | |
| 	if err != nil {
 | |
| 		return "", fmt.Errorf("GetFirstMatchProtectedBranchRule: %w", err)
 | |
| 	}
 | |
| 	var requiredContexts []string
 | |
| 	if pb != nil {
 | |
| 		requiredContexts = pb.StatusCheckContexts
 | |
| 	}
 | |
| 
 | |
| 	return MergeRequiredContextsCommitStatus(commitStatuses, requiredContexts), nil
 | |
| }
 |