mirror of
https://github.com/go-gitea/gitea.git
synced 2025-12-15 17:30:29 +01:00
Honor delete branch on merge repo setting when using merge API (#35488)
Fix #35463. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -46,14 +46,14 @@ func EnableOrDisableWorkflow(ctx *context.APIContext, workflowID string, isEnabl
|
||||
|
||||
func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, workflowID, ref string, processInputs func(model *model.WorkflowDispatch, inputs map[string]any) error) error {
|
||||
if workflowID == "" {
|
||||
return util.ErrorWrapLocale(
|
||||
return util.ErrorWrapTranslatable(
|
||||
util.NewNotExistErrorf("workflowID is empty"),
|
||||
"actions.workflow.not_found", workflowID,
|
||||
)
|
||||
}
|
||||
|
||||
if ref == "" {
|
||||
return util.ErrorWrapLocale(
|
||||
return util.ErrorWrapTranslatable(
|
||||
util.NewNotExistErrorf("ref is empty"),
|
||||
"form.target_ref_not_exist", ref,
|
||||
)
|
||||
@@ -63,7 +63,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
|
||||
cfgUnit := repo.MustGetUnit(ctx, unit.TypeActions)
|
||||
cfg := cfgUnit.ActionsConfig()
|
||||
if cfg.IsWorkflowDisabled(workflowID) {
|
||||
return util.ErrorWrapLocale(
|
||||
return util.ErrorWrapTranslatable(
|
||||
util.NewPermissionDeniedErrorf("workflow is disabled"),
|
||||
"actions.workflow.disabled",
|
||||
)
|
||||
@@ -82,7 +82,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
|
||||
runTargetCommit, err = gitRepo.GetBranchCommit(ref)
|
||||
}
|
||||
if err != nil {
|
||||
return util.ErrorWrapLocale(
|
||||
return util.ErrorWrapTranslatable(
|
||||
util.NewNotExistErrorf("ref %q doesn't exist", ref),
|
||||
"form.target_ref_not_exist", ref,
|
||||
)
|
||||
@@ -122,7 +122,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
|
||||
}
|
||||
|
||||
if entry == nil {
|
||||
return util.ErrorWrapLocale(
|
||||
return util.ErrorWrapTranslatable(
|
||||
util.NewNotExistErrorf("workflow %q doesn't exist", workflowID),
|
||||
"actions.workflow.not_found", workflowID,
|
||||
)
|
||||
|
||||
@@ -205,18 +205,6 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
|
||||
return
|
||||
}
|
||||
|
||||
var headGitRepo *git.Repository
|
||||
if pr.BaseRepoID == pr.HeadRepoID {
|
||||
headGitRepo = baseGitRepo
|
||||
} else {
|
||||
headGitRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository %-v: %v", pr.HeadRepo, err)
|
||||
return
|
||||
}
|
||||
defer headGitRepo.Close()
|
||||
}
|
||||
|
||||
switch pr.Flow {
|
||||
case issues_model.PullRequestFlowGithub:
|
||||
headBranchExist := pr.HeadRepo != nil && gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch)
|
||||
@@ -276,9 +264,12 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
|
||||
return
|
||||
}
|
||||
|
||||
if pr.Flow == issues_model.PullRequestFlowGithub && scheduledPRM.DeleteBranchAfterMerge {
|
||||
if err := repo_service.DeleteBranch(ctx, doer, pr.HeadRepo, headGitRepo, pr.HeadBranch, pr); err != nil {
|
||||
log.Error("DeletePullRequestHeadBranch: %v", err)
|
||||
deleteBranchAfterMerge, err := pull_service.ShouldDeleteBranchAfterMerge(ctx, &scheduledPRM.DeleteBranchAfterMerge, pr.BaseRepo, pr)
|
||||
if err != nil {
|
||||
log.Error("ShouldDeleteBranchAfterMerge: %v", err)
|
||||
} else if deleteBranchAfterMerge {
|
||||
if err = repo_service.DeleteBranchAfterMerge(ctx, doer, pr.ID, nil); err != nil {
|
||||
log.Error("DeleteBranchAfterMerge: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ type MergePullRequestForm struct {
|
||||
HeadCommitID string `json:"head_commit_id,omitempty"`
|
||||
ForceMerge bool `json:"force_merge,omitempty"`
|
||||
MergeWhenChecksSucceed bool `json:"merge_when_checks_succeed,omitempty"`
|
||||
DeleteBranchAfterMerge bool `json:"delete_branch_after_merge,omitempty"`
|
||||
DeleteBranchAfterMerge *bool `json:"delete_branch_after_merge,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
|
||||
@@ -730,3 +730,24 @@ func SetMerged(ctx context.Context, pr *issues_model.PullRequest, mergedCommitID
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func ShouldDeleteBranchAfterMerge(ctx context.Context, userOption *bool, repo *repo_model.Repository, pr *issues_model.PullRequest) (bool, error) {
|
||||
if pr.Flow != issues_model.PullRequestFlowGithub {
|
||||
// only support GitHub workflow (branch-based)
|
||||
// for agit workflow, there is no branch, so nothing to delete
|
||||
// TODO: maybe in the future, it should delete the "agit reference (refs/for/xxxx)"?
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// if user has set an option, respect it
|
||||
if userOption != nil {
|
||||
return *userOption, nil
|
||||
}
|
||||
|
||||
// otherwise, use repository default
|
||||
prUnit, err := repo.GetUnit(ctx, unit.TypePullRequests)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return prUnit.PullRequestsConfig().DefaultDeleteBranchAfterMerge, nil
|
||||
}
|
||||
|
||||
@@ -484,10 +484,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// enmuerates all branch related errors
|
||||
var (
|
||||
ErrBranchIsDefault = errors.New("branch is default")
|
||||
)
|
||||
var ErrBranchIsDefault = util.ErrorWrap(util.ErrPermissionDenied, "branch is default")
|
||||
|
||||
func CanDeleteBranch(ctx context.Context, repo *repo_model.Repository, branchName string, doer *user_model.User) error {
|
||||
if branchName == repo.DefaultBranch {
|
||||
@@ -745,3 +742,89 @@ func GetBranchDivergingInfo(ctx reqctx.RequestContext, baseRepo *repo_model.Repo
|
||||
info.BaseHasNewCommits = info.HeadCommitsBehind > 0
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func DeleteBranchAfterMerge(ctx context.Context, doer *user_model.User, prID int64, outFullBranchName *string) error {
|
||||
pr, err := issues_model.GetPullRequestByID(ctx, prID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = pr.LoadIssue(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = pr.LoadBaseRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pr.LoadHeadRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if pr.HeadRepo == nil {
|
||||
// Forked repository has already been deleted
|
||||
return util.ErrorWrapTranslatable(util.ErrNotExist, "repo.branch.deletion_failed", "(deleted-repo):"+pr.HeadBranch)
|
||||
}
|
||||
if err = pr.HeadRepo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fullBranchName := pr.HeadRepo.FullName() + ":" + pr.HeadBranch
|
||||
if outFullBranchName != nil {
|
||||
*outFullBranchName = fullBranchName
|
||||
}
|
||||
|
||||
errFailedToDelete := func(err error) error {
|
||||
return util.ErrorWrapTranslatable(err, "repo.branch.deletion_failed", fullBranchName)
|
||||
}
|
||||
|
||||
// Don't clean up unmerged and unclosed PRs and agit PRs
|
||||
if !pr.HasMerged && !pr.Issue.IsClosed && pr.Flow != issues_model.PullRequestFlowGithub {
|
||||
return errFailedToDelete(util.ErrUnprocessableContent)
|
||||
}
|
||||
|
||||
// Don't clean up when there are other PR's that use this branch as head branch.
|
||||
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exist {
|
||||
return errFailedToDelete(util.ErrUnprocessableContent)
|
||||
}
|
||||
|
||||
if err := CanDeleteBranch(ctx, pr.HeadRepo, pr.HeadBranch, doer); err != nil {
|
||||
if errors.Is(err, util.ErrPermissionDenied) {
|
||||
return errFailedToDelete(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
gitBaseRepo, gitBaseCloser, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.BaseRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gitBaseCloser.Close()
|
||||
|
||||
gitHeadRepo, gitHeadCloser, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.HeadRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gitHeadCloser.Close()
|
||||
|
||||
// Check if branch has no new commits
|
||||
headCommitID, err := gitBaseRepo.GetRefCommitID(pr.GetGitHeadRefName())
|
||||
if err != nil {
|
||||
log.Error("GetRefCommitID: %v", err)
|
||||
return errFailedToDelete(err)
|
||||
}
|
||||
branchCommitID, err := gitHeadRepo.GetBranchCommitID(pr.HeadBranch)
|
||||
if err != nil {
|
||||
log.Error("GetBranchCommitID: %v", err)
|
||||
return errFailedToDelete(err)
|
||||
}
|
||||
if headCommitID != branchCommitID {
|
||||
return util.ErrorWrapTranslatable(util.ErrUnprocessableContent, "repo.branch.delete_branch_has_new_commits", fullBranchName)
|
||||
}
|
||||
|
||||
err = DeleteBranch(ctx, doer, pr.HeadRepo, gitHeadRepo, pr.HeadBranch, pr)
|
||||
if errors.Is(err, util.ErrPermissionDenied) || errors.Is(err, util.ErrNotExist) {
|
||||
return errFailedToDelete(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user