1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-22 18:28:37 +00:00

Move some errors to their own sub packages (#32880)

This commit is contained in:
Lunny Xiao
2024-12-20 10:05:29 -08:00
committed by GitHub
parent 4774151e53
commit 751fe8b714
52 changed files with 728 additions and 781 deletions

View File

@@ -11,7 +11,6 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
@@ -36,7 +35,7 @@ var prPatchCheckerQueue *queue.WorkerPoolQueue[string]
var (
ErrIsClosed = errors.New("pull is closed")
ErrUserNotAllowedToMerge = models.ErrDisallowedToMerge{}
ErrUserNotAllowedToMerge = ErrDisallowedToMerge{}
ErrHasMerged = errors.New("has already been merged")
ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged")
ErrIsChecking = errors.New("cannot merge while conflict checking is in progress")
@@ -106,7 +105,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc
}
if err := CheckPullBranchProtections(ctx, pr, false); err != nil {
if !models.IsErrDisallowedToMerge(err) {
if !IsErrDisallowedToMerge(err) {
log.Error("Error whilst checking pull branch protection for %-v: %v", pr, err)
return err
}

View File

@@ -13,7 +13,6 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
@@ -30,6 +29,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify"
)
@@ -159,6 +159,27 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr
return getMergeMessage(ctx, baseGitRepo, pr, mergeStyle, nil)
}
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
type ErrInvalidMergeStyle struct {
ID int64
Style repo_model.MergeStyle
}
// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle.
func IsErrInvalidMergeStyle(err error) bool {
_, ok := err.(ErrInvalidMergeStyle)
return ok
}
func (err ErrInvalidMergeStyle) Error() string {
return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]",
err.ID, err.Style)
}
func (err ErrInvalidMergeStyle) Unwrap() error {
return util.ErrInvalidArgument
}
// Merge merges pull request to base repository.
// Caller should check PR is ready to be merged (review and status checks)
func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error {
@@ -179,7 +200,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
// Check if merge style is correct and allowed
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
return ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
}
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
@@ -283,7 +304,7 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
return "", err
}
default:
return "", models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
return "", ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
}
// OK we should cache our current head and origin/headbranch
@@ -374,13 +395,66 @@ func commitAndSignNoAuthor(ctx *mergeContext, message string) error {
return nil
}
// ErrMergeConflicts represents an error if merging fails with a conflict
type ErrMergeConflicts struct {
Style repo_model.MergeStyle
StdOut string
StdErr string
Err error
}
// IsErrMergeConflicts checks if an error is a ErrMergeConflicts.
func IsErrMergeConflicts(err error) bool {
_, ok := err.(ErrMergeConflicts)
return ok
}
func (err ErrMergeConflicts) Error() string {
return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
}
// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories
type ErrMergeUnrelatedHistories struct {
Style repo_model.MergeStyle
StdOut string
StdErr string
Err error
}
// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories.
func IsErrMergeUnrelatedHistories(err error) bool {
_, ok := err.(ErrMergeUnrelatedHistories)
return ok
}
func (err ErrMergeUnrelatedHistories) Error() string {
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
}
// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
type ErrMergeDivergingFastForwardOnly struct {
StdOut string
StdErr string
Err error
}
// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
func IsErrMergeDivergingFastForwardOnly(err error) bool {
_, ok := err.(ErrMergeDivergingFastForwardOnly)
return ok
}
func (err ErrMergeDivergingFastForwardOnly) Error() string {
return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
}
func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error {
if err := cmd.Run(ctx.RunOpts()); err != nil {
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
// We have a merge conflict error
log.Debug("MergeConflict %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return models.ErrMergeConflicts{
return ErrMergeConflicts{
Style: mergeStyle,
StdOut: ctx.outbuf.String(),
StdErr: ctx.errbuf.String(),
@@ -388,7 +462,7 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g
}
} else if strings.Contains(ctx.errbuf.String(), "refusing to merge unrelated histories") {
log.Debug("MergeUnrelatedHistories %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return models.ErrMergeUnrelatedHistories{
return ErrMergeUnrelatedHistories{
Style: mergeStyle,
StdOut: ctx.outbuf.String(),
StdErr: ctx.errbuf.String(),
@@ -396,7 +470,7 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g
}
} else if mergeStyle == repo_model.MergeStyleFastForwardOnly && strings.Contains(ctx.errbuf.String(), "Not possible to fast-forward, aborting") {
log.Debug("MergeDivergingFastForwardOnly %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return models.ErrMergeDivergingFastForwardOnly{
return ErrMergeDivergingFastForwardOnly{
StdOut: ctx.outbuf.String(),
StdErr: ctx.errbuf.String(),
Err: err,
@@ -431,6 +505,25 @@ func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p a
return false, nil
}
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
type ErrDisallowedToMerge struct {
Reason string
}
// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge.
func IsErrDisallowedToMerge(err error) bool {
_, ok := err.(ErrDisallowedToMerge)
return ok
}
func (err ErrDisallowedToMerge) Error() string {
return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
}
func (err ErrDisallowedToMerge) Unwrap() error {
return util.ErrPermissionDenied
}
// CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks)
func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (err error) {
if err = pr.LoadBaseRepo(ctx); err != nil {
@@ -450,29 +543,29 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
return err
}
if !isPass {
return models.ErrDisallowedToMerge{
return ErrDisallowedToMerge{
Reason: "Not all required status checks successful",
}
}
if !issues_model.HasEnoughApprovals(ctx, pb, pr) {
return models.ErrDisallowedToMerge{
return ErrDisallowedToMerge{
Reason: "Does not have enough approvals",
}
}
if issues_model.MergeBlockedByRejectedReview(ctx, pb, pr) {
return models.ErrDisallowedToMerge{
return ErrDisallowedToMerge{
Reason: "There are requested changes",
}
}
if issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pr) {
return models.ErrDisallowedToMerge{
return ErrDisallowedToMerge{
Reason: "There are official review requests",
}
}
if issues_model.MergeBlockedByOutdatedBranch(pb, pr) {
return models.ErrDisallowedToMerge{
return ErrDisallowedToMerge{
Reason: "The head branch is behind the base branch",
}
}
@@ -482,7 +575,7 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
}
if pb.MergeBlockedByProtectedFiles(pr.ChangedProtectedFiles) {
return models.ErrDisallowedToMerge{
return ErrDisallowedToMerge{
Reason: "Changed protected files",
}
}
@@ -511,7 +604,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
// Check if merge style is correct and allowed
if !prConfig.IsMergeStyleAllowed(repo_model.MergeStyleManuallyMerged) {
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged}
return ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged}
}
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)

View File

@@ -14,7 +14,6 @@ import (
"strings"
"time"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -43,6 +42,23 @@ func (ctx *mergeContext) RunOpts() *git.RunOpts {
}
}
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
type ErrSHADoesNotMatch struct {
Path string
GivenSHA string
CurrentSHA string
}
// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch.
func IsErrSHADoesNotMatch(err error) bool {
_, ok := err.(ErrSHADoesNotMatch)
return ok
}
func (err ErrSHADoesNotMatch) Error() string {
return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA)
}
func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, expectedHeadCommitID string) (mergeCtx *mergeContext, cancel context.CancelFunc, err error) {
// Clone base repo.
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
@@ -65,7 +81,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
}
if strings.TrimSpace(trackingCommitID) != expectedHeadCommitID {
defer cancel()
return nil, nil, models.ErrSHADoesNotMatch{
return nil, nil, ErrSHADoesNotMatch{
GivenSHA: expectedHeadCommitID,
CurrentSHA: trackingCommitID,
}
@@ -233,8 +249,27 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
return err
}
// ErrRebaseConflicts represents an error if rebase fails with a conflict
type ErrRebaseConflicts struct {
Style repo_model.MergeStyle
CommitSHA string
StdOut string
StdErr string
Err error
}
// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts.
func IsErrRebaseConflicts(err error) bool {
_, ok := err.(ErrRebaseConflicts)
return ok
}
func (err ErrRebaseConflicts) Error() string {
return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut)
}
// rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch
// if there is a conflict it will return a models.ErrRebaseConflicts
// if there is a conflict it will return an ErrRebaseConflicts
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
// Checkout head branch
if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
@@ -268,11 +303,11 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle)
}
}
if !ok {
log.Error("Unable to determine failing commit sha for failing rebase in temp repo for %-v. Cannot cast as models.ErrRebaseConflicts.", ctx.pr)
log.Error("Unable to determine failing commit sha for failing rebase in temp repo for %-v. Cannot cast as ErrRebaseConflicts.", ctx.pr)
return fmt.Errorf("unable to git rebase staging on to base in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
log.Debug("Conflict when rebasing staging on to base in %-v at %s: %v\n%s\n%s", ctx.pr, commitSha, err, ctx.outbuf.String(), ctx.errbuf.String())
return models.ErrRebaseConflicts{
return ErrRebaseConflicts{
CommitSHA: commitSha,
Style: mergeStyle,
StdOut: ctx.outbuf.String(),

View File

@@ -13,7 +13,6 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unit"
@@ -502,6 +501,29 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
return false, nil
}
// ErrFilePathProtected represents a "FilePathProtected" kind of error.
type ErrFilePathProtected struct {
Message string
Path string
}
// IsErrFilePathProtected checks if an error is an ErrFilePathProtected.
func IsErrFilePathProtected(err error) bool {
_, ok := err.(ErrFilePathProtected)
return ok
}
func (err ErrFilePathProtected) Error() string {
if err.Message != "" {
return err.Message
}
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
}
func (err ErrFilePathProtected) Unwrap() error {
return util.ErrPermissionDenied
}
// CheckFileProtection check file Protection
func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) {
if len(patterns) == 0 {
@@ -525,7 +547,7 @@ func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommi
}
}
if len(changedProtectedFiles) > 0 {
err = models.ErrFilePathProtected{
err = ErrFilePathProtected{
Path: changedProtectedFiles[0],
}
}
@@ -575,7 +597,7 @@ func checkPullFilesProtection(ctx context.Context, pr *issues_model.PullRequest,
}
pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.HeadBranch, pr.MergeBase, "tracking", pb.GetProtectedFilePatterns(), 10, os.Environ())
if err != nil && !models.IsErrFilePathProtected(err) {
if err != nil && !IsErrFilePathProtected(err) {
return err
}
return nil

View File

@@ -13,7 +13,6 @@ import (
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
@@ -224,6 +223,28 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
return nil
}
// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error
type ErrPullRequestHasMerged struct {
ID int64
IssueID int64
HeadRepoID int64
BaseRepoID int64
HeadBranch string
BaseBranch string
}
// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged.
func IsErrPullRequestHasMerged(err error) bool {
_, ok := err.(ErrPullRequestHasMerged)
return ok
}
// Error does pretty-printing :D
func (err ErrPullRequestHasMerged) Error() string {
return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// ChangeTargetBranch changes the target branch of this pull request, as the given user.
func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) {
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
@@ -247,7 +268,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
}
if pr.HasMerged {
return models.ErrPullRequestHasMerged{
return ErrPullRequestHasMerged{
ID: pr.ID,
IssueID: pr.Index,
HeadRepoID: pr.HeadRepoID,
@@ -654,7 +675,7 @@ func RetargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6
if err = pr.Issue.LoadRepo(ctx); err != nil {
errs = append(errs, err)
} else if err = ChangeTargetBranch(ctx, pr, doer, targetBranch); err != nil &&
!issues_model.IsErrIssueIsClosed(err) && !models.IsErrPullRequestHasMerged(err) &&
!issues_model.IsErrIssueIsClosed(err) && !IsErrPullRequestHasMerged(err) &&
!issues_model.IsErrPullRequestAlreadyExists(err) {
errs = append(errs, err)
}