mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-04 14:46:57 -05:00
Auto merge pull requests when all checks succeeded via WebUI (#19648)
Add WebUI part of Auto merge feature close #19621 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
parent
ce3dd04c63
commit
a9cc9c0f7a
@ -1568,14 +1568,7 @@ pulls.squash_merge_pull_request = Create squash commit
|
||||
pulls.merge_manually = Manually merged
|
||||
pulls.merge_commit_id = The merge commit ID
|
||||
pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed
|
||||
pulls.merge_pull_request_now = Merge Pull Request Now
|
||||
pulls.rebase_merge_pull_request_now = Rebase and Merge Now
|
||||
pulls.rebase_merge_commit_pull_request_now = Rebase and Merge Now (--no-ff)
|
||||
pulls.squash_merge_pull_request_now = Squash and Merge Now
|
||||
pulls.merge_pull_request_on_status_success = Merge Pull Request When All Checks Succeed
|
||||
pulls.rebase_merge_pull_request_on_status_success = Rebase and Merge When All Checks Succeed
|
||||
pulls.rebase_merge_commit_pull_request_on_status_success = Rebase and Merge (--no-ff) When All Checks Succeed
|
||||
pulls.squash_merge_pull_request_on_status_success = Squash and Merge When All Checks Succeed
|
||||
|
||||
pulls.invalid_merge_option = You cannot use this merge option for this pull request.
|
||||
pulls.merge_conflict = Merge Failed: There was a conflict whilst merging. Hint: Try a different strategy
|
||||
pulls.merge_conflict_summary = Error Message
|
||||
@ -1606,14 +1599,18 @@ pulls.reopened_at = `reopened this pull request <a id="%[1]s" href="#%[1]s">%[2]
|
||||
pulls.merge_instruction_hint = `You can also view <a class="show-instruction">command line instructions</a>.`
|
||||
pulls.merge_instruction_step1_desc = From your project repository, check out a new branch and test the changes.
|
||||
pulls.merge_instruction_step2_desc = Merge the changes and update on Gitea.
|
||||
pulls.merge_on_status_success = The pull request was scheduled to merge when all checks succeed.
|
||||
pulls.merge_on_status_success_already_scheduled = This pull request is already scheduled to merge when all checks succeed.
|
||||
pulls.pr_has_pending_merge_on_success = %[1]s scheduled this pull request to auto merge when all checks succeed %[2]s.
|
||||
pulls.merge_pull_on_success_cancel = Cancel auto merge
|
||||
pulls.pull_request_not_scheduled = This pull request is not scheduled to auto merge.
|
||||
pulls.pull_request_schedule_canceled = The auto merge was canceled for this pull request.
|
||||
pulls.pull_request_scheduled_auto_merge = `scheduled this pull request to auto merge when all checks succeed %[1]s`
|
||||
pulls.pull_request_canceled_scheduled_auto_merge = `canceled auto merging this pull request when all checks succeed %[1]s`
|
||||
|
||||
pulls.auto_merge_button_when_succeed = (When checks succeed)
|
||||
pulls.auto_merge_when_succeed = Auto merge when all checks succeed
|
||||
pulls.auto_merge_newly_scheduled = The pull request was scheduled to merge when all checks succeed.
|
||||
pulls.auto_merge_has_pending_schedule = %[1]s scheduled this pull request to auto merge when all checks succeed %[2]s.
|
||||
|
||||
pulls.auto_merge_cancel_schedule = Cancel auto merge
|
||||
pulls.auto_merge_not_scheduled = This pull request is not scheduled to auto merge.
|
||||
pulls.auto_merge_canceled_schedule = The auto merge was canceled for this pull request.
|
||||
|
||||
pulls.auto_merge_newly_scheduled_comment = `scheduled this pull request to auto merge when all checks succeed %[1]s`
|
||||
pulls.auto_merge_canceled_schedule_comment = `canceled auto merging this pull request when all checks succeed %[1]s`
|
||||
|
||||
milestones.new = New Milestone
|
||||
milestones.open_tab = %d Open
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
pull_model "code.gitea.io/gitea/models/pull"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
@ -36,6 +37,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/routers/utils"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
"code.gitea.io/gitea/services/automerge"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
@ -966,6 +968,22 @@ func MergePullRequest(ctx *context.Context) {
|
||||
message += "\n\n" + form.MergeMessageField
|
||||
}
|
||||
|
||||
if form.MergeWhenChecksSucceed {
|
||||
// delete all scheduled auto merges
|
||||
_ = pull_model.DeleteScheduledAutoMerge(ctx, pr.ID)
|
||||
// schedule auto merge
|
||||
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message)
|
||||
if err != nil {
|
||||
ctx.ServerError("ScheduleAutoMerge", err)
|
||||
return
|
||||
} else if scheduled {
|
||||
// nothing more to do ...
|
||||
ctx.Flash.Success(ctx.Tr("repo.pulls.auto_merge_newly_scheduled"))
|
||||
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pr.Index))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||
@ -1070,6 +1088,26 @@ func MergePullRequest(ctx *context.Context) {
|
||||
ctx.Redirect(issue.Link())
|
||||
}
|
||||
|
||||
// CancelAutoMergePullRequest cancels a scheduled pr
|
||||
func CancelAutoMergePullRequest(ctx *context.Context) {
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, issue.PullRequest); err != nil {
|
||||
if db.IsErrNotExist(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.auto_merge_not_scheduled"))
|
||||
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, issue.Index))
|
||||
return
|
||||
}
|
||||
ctx.ServerError("RemoveScheduledAutoMerge", err)
|
||||
return
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.pulls.auto_merge_canceled_schedule"))
|
||||
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, issue.Index))
|
||||
}
|
||||
|
||||
func stopTimerIfAvailable(user *user_model.User, issue *models.Issue) error {
|
||||
if models.StopwatchExists(user.ID, issue.ID) {
|
||||
if err := models.CreateOrStopIssueStopwatch(user, issue); err != nil {
|
||||
|
@ -1127,6 +1127,7 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.Get(".patch", repo.DownloadPullPatch)
|
||||
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
|
||||
m.Post("/merge", context.RepoMustNotBeArchived(), bindIgnErr(forms.MergePullRequestForm{}), repo.MergePullRequest)
|
||||
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
|
||||
m.Post("/update", repo.UpdatePullRequest)
|
||||
m.Post("/set_allow_maintainer_edit", bindIgnErr(forms.UpdateAllowEditsForm{}), repo.SetAllowEdits)
|
||||
m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
|
||||
|
@ -843,8 +843,8 @@
|
||||
<span class="badge">{{svg "octicon-git-merge" 16}}</span>
|
||||
<span class="text grey">
|
||||
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
|
||||
{{if eq .Type 34}}{{$.i18n.Tr "repo.pulls.pull_request_scheduled_auto_merge" $createdStr | Safe}}
|
||||
{{else}}{{$.i18n.Tr "repo.pulls.pull_request_canceled_scheduled_auto_merge" $createdStr | Safe}}{{end}}
|
||||
{{if eq .Type 34}}{{$.i18n.Tr "repo.pulls.auto_merge_newly_scheduled_comment" $createdStr | Safe}}
|
||||
{{else}}{{$.i18n.Tr "repo.pulls.auto_merge_canceled_schedule_comment" $createdStr | Safe}}{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -251,8 +251,14 @@
|
||||
{{$.i18n.Tr (printf "repo.signing.wont_sign.%s" .WontSignReason) }}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{$notAllOverridableChecksOk := or .IsBlockedByApprovals .IsBlockedByRejection .IsBlockedByOfficialReviewRequests .IsBlockedByOutdatedBranch .IsBlockedByChangedProtectedFiles (and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess))}}
|
||||
{{if and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}}
|
||||
|
||||
{{/* admin can merge without checks, writer can merge when checkes succeed */}}
|
||||
{{$canMergeNow := and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}}
|
||||
{{/* admin and writer both can make an auto merge schedule */}}
|
||||
|
||||
{{if $canMergeNow}}
|
||||
{{if $notAllOverridableChecksOk}}
|
||||
<div class="item">
|
||||
<i class="icon icon-octicon">{{svg "octicon-dot-fill"}}</i>
|
||||
@ -277,7 +283,6 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{$canAutoMerge = true}}
|
||||
{{if (gt .Issue.PullRequest.CommitsBehind 0)}}
|
||||
<div class="ui divider"></div>
|
||||
<div class="item item-section">
|
||||
@ -317,112 +322,111 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}}
|
||||
{{if .AllowMerge}}
|
||||
{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}}
|
||||
{{$approvers := .Issue.PullRequest.GetApprovers}}
|
||||
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}}
|
||||
{{if .AllowMerge}} {{/* user is allowed to merge */}}
|
||||
{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}}
|
||||
{{$approvers := .Issue.PullRequest.GetApprovers}}
|
||||
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}}
|
||||
{{$hasPendingPullRequestMergeTip := ""}}
|
||||
{{if .HasPendingPullRequestMerge}}
|
||||
{{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix $.i18n.Lang}}
|
||||
{{$hasPendingPullRequestMergeTip = $.i18n.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}}
|
||||
{{end}}
|
||||
<div class="ui divider"></div>
|
||||
<script>
|
||||
<!-- /* eslint-disable */ -->
|
||||
(() => {
|
||||
const defaultMergeTitle = {{.DefaultMergeMessage}};
|
||||
const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}};
|
||||
const defaultMergeMessage = 'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}};
|
||||
const mergeForm = {
|
||||
'baseLink': {{.Link}},
|
||||
'textCancel': {{$.i18n.Tr "cancel"}},
|
||||
'textDeleteBranch': {{$.i18n.Tr "repo.branch.delete" .HeadTarget}},
|
||||
'textAutoMergeButtonWhenSucceed': {{$.i18n.Tr "repo.pulls.auto_merge_button_when_succeed"}},
|
||||
'textAutoMergeWhenSucceed': {{$.i18n.Tr "repo.pulls.auto_merge_when_succeed"}},
|
||||
'textAutoMergeCancelSchedule': {{$.i18n.Tr "repo.pulls.auto_merge_cancel_schedule"}},
|
||||
|
||||
<div class="ui divider"></div>
|
||||
'canMergeNow': {{$canMergeNow}},
|
||||
'allOverridableChecksOk': {{not $notAllOverridableChecksOk}},
|
||||
'pullHeadCommitID': {{.PullHeadCommitID}},
|
||||
'isPullBranchDeletable': {{.IsPullBranchDeletable}},
|
||||
'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}},
|
||||
'mergeMessageFieldPlaceHolder': {{$.i18n.Tr "repo.editor.commit_message_desc"}},
|
||||
|
||||
<script>
|
||||
<!-- /* eslint-disable */ -->
|
||||
(() => {
|
||||
const defaultMergeTitle = {{.DefaultMergeMessage}};
|
||||
const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}};
|
||||
const defaultMergeMessage = 'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}};
|
||||
const mergeForm = {
|
||||
'baseLink': {{.Link}},
|
||||
'textCancel': {{$.i18n.Tr "cancel"}},
|
||||
'textDeleteBranch': {{$.i18n.Tr "repo.branch.delete" .HeadTarget}},
|
||||
'hasPendingPullRequestMerge': {{.HasPendingPullRequestMerge}},
|
||||
'hasPendingPullRequestMergeTip': {{$hasPendingPullRequestMergeTip}},
|
||||
};
|
||||
|
||||
'allOverridableChecksOk': {{not $notAllOverridableChecksOk}},
|
||||
'pullHeadCommitID': {{.PullHeadCommitID}},
|
||||
'isPullBranchDeletable': {{.IsPullBranchDeletable}},
|
||||
'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}},
|
||||
'mergeMessageFieldPlaceHolder': {{$.i18n.Tr "repo.editor.commit_message_desc"}},
|
||||
};
|
||||
mergeForm['mergeStyles'] = [
|
||||
{
|
||||
'name': 'merge',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowMerge}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.merge_pull_request"}},
|
||||
'mergeTitleFieldText': defaultMergeTitle,
|
||||
'mergeMessageFieldText': defaultMergeMessage,
|
||||
},
|
||||
{
|
||||
'name': 'rebase',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowRebase}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}},
|
||||
'hideMergeMessageTexts': true,
|
||||
},
|
||||
{
|
||||
'name': 'rebase-merge',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowRebaseMerge}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}},
|
||||
'mergeTitleFieldText': defaultMergeTitle,
|
||||
'mergeMessageFieldText': defaultMergeMessage,
|
||||
},
|
||||
{
|
||||
'name': 'squash',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowSquash}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}},
|
||||
'mergeTitleFieldText': defaultSquashMergeTitle,
|
||||
'mergeMessageFieldText': defaultMergeMessage,
|
||||
},
|
||||
{
|
||||
'name': 'manually-merged',
|
||||
'allowed': {{and $prUnit.PullRequestsConfig.AllowManualMerge $.IsRepoAdmin}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.merge_manually"}},
|
||||
'hideMergeMessageTexts': true,
|
||||
}
|
||||
];
|
||||
window.config.pageData.pullRequestMergeForm = mergeForm;
|
||||
})();
|
||||
</script>
|
||||
const generalHideAutoMerge = mergeForm.canMergeNow && mergeForm.allOverridableChecksOk; // if this PR can be merged now, then hide the auto merge
|
||||
mergeForm['mergeStyles'] = [
|
||||
{
|
||||
'name': 'merge',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowMerge}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.merge_pull_request"}},
|
||||
'mergeTitleFieldText': defaultMergeTitle,
|
||||
'mergeMessageFieldText': defaultMergeMessage,
|
||||
'hideAutoMerge': generalHideAutoMerge,
|
||||
},
|
||||
{
|
||||
'name': 'rebase',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowRebase}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}},
|
||||
'hideMergeMessageTexts': true,
|
||||
'hideAutoMerge': generalHideAutoMerge,
|
||||
},
|
||||
{
|
||||
'name': 'rebase-merge',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowRebaseMerge}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}},
|
||||
'mergeTitleFieldText': defaultMergeTitle,
|
||||
'mergeMessageFieldText': defaultMergeMessage,
|
||||
'hideAutoMerge': generalHideAutoMerge,
|
||||
},
|
||||
{
|
||||
'name': 'squash',
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowSquash}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}},
|
||||
'mergeTitleFieldText': defaultSquashMergeTitle,
|
||||
'mergeMessageFieldText': defaultMergeMessage,
|
||||
'hideAutoMerge': generalHideAutoMerge,
|
||||
},
|
||||
{
|
||||
'name': 'manually-merged',
|
||||
'allowed': {{and $prUnit.PullRequestsConfig.AllowManualMerge $.IsRepoAdmin}},
|
||||
'textDoMerge': {{$.i18n.Tr "repo.pulls.merge_manually"}},
|
||||
'hideMergeMessageTexts': true,
|
||||
'hideAutoMerge': true,
|
||||
}
|
||||
];
|
||||
window.config.pageData.pullRequestMergeForm = mergeForm;
|
||||
})();
|
||||
</script>
|
||||
|
||||
<div id="pull-request-merge-form"></div>
|
||||
<div id="pull-request-merge-form"></div>
|
||||
|
||||
{{if .ShowMergeInstructions}}
|
||||
<div class="instruct-toggle mt-3"> {{$.i18n.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div>
|
||||
<div class="instruct-content" style="display:none">
|
||||
<div class="ui divider"></div>
|
||||
<div><h3 class="di">{{$.i18n.Tr "step1"}} </h3>{{$.i18n.Tr "repo.pulls.merge_instruction_step1_desc"}}</div>
|
||||
<div class="ui secondary segment">
|
||||
{{if eq .Issue.PullRequest.Flow 0}}
|
||||
<div>git checkout -b {{if ne .Issue.PullRequest.HeadRepo.ID .Issue.PullRequest.BaseRepo.ID}}{{.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{.Issue.PullRequest.HeadBranch}} {{.Issue.PullRequest.BaseBranch}}</div>
|
||||
<div>git pull {{if ne .Issue.PullRequest.HeadRepo.ID .Issue.PullRequest.BaseRepo.ID}}{{.Issue.PullRequest.HeadRepo.HTMLURL}}{{else}}origin{{end}} {{.Issue.PullRequest.HeadBranch}}</div>
|
||||
{{else}}
|
||||
<div>git fetch origin {{.Issue.PullRequest.GetGitRefName}}:{{.Issue.PullRequest.HeadBranch}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div><h3 class="di">{{$.i18n.Tr "step2"}} </h3>{{$.i18n.Tr "repo.pulls.merge_instruction_step2_desc"}}</div>
|
||||
<div class="ui secondary segment">
|
||||
<div>git checkout {{.Issue.PullRequest.BaseBranch}}</div>
|
||||
<div>git merge --no-ff {{if ne .Issue.PullRequest.HeadRepo.ID .Issue.PullRequest.BaseRepo.ID}}{{.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{.Issue.PullRequest.HeadBranch}}</div>
|
||||
<div>git push origin {{.Issue.PullRequest.BaseBranch}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="ui divider"></div>
|
||||
<div class="item text red">
|
||||
{{svg "octicon-x"}}
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_desc"}}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{svg "octicon-info"}}
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_helper"}}
|
||||
</div>
|
||||
{{if .ShowMergeInstructions}}
|
||||
{{template "repo/issue/view_content/pull_merge_instruction" (dict "i18n" .i18n "Issue" .Issue)}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{/* no merge style was set in repo setting: not or ($prUnit.PullRequestsConfig.AllowMerge ...) */}}
|
||||
<div class="ui divider"></div>
|
||||
<div class="item text red">
|
||||
{{svg "octicon-x"}}
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_desc"}}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{svg "octicon-info"}}
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_access"}}
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_helper"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}} {{/* end if the repo was set to use any merge style */}}
|
||||
{{else}}
|
||||
{{/* user is not allowed to merge */}}
|
||||
<div class="ui divider"></div>
|
||||
<div class="item">
|
||||
{{svg "octicon-info"}}
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_access"}}
|
||||
</div>
|
||||
{{end}} {{/* end if user is allowed to merge or not */}}
|
||||
{{else}}
|
||||
{{/* Merge conflict without specific file. Suggest manual merge, only if all reviews and status checks OK. */}}
|
||||
{{if .IsBlockedByApprovals}}
|
||||
|
@ -0,0 +1,19 @@
|
||||
<div class="instruct-toggle mt-3"> {{$.i18n.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div>
|
||||
<div class="instruct-content" style="display:none">
|
||||
<div class="ui divider"></div>
|
||||
<div><h3 class="di">{{$.i18n.Tr "step1"}} </h3>{{$.i18n.Tr "repo.pulls.merge_instruction_step1_desc"}}</div>
|
||||
<div class="ui secondary segment">
|
||||
{{if eq $.Issue.PullRequest.Flow 0}}
|
||||
<div>git checkout -b {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{$.Issue.PullRequest.HeadBranch}} {{$.Issue.PullRequest.BaseBranch}}</div>
|
||||
<div>git pull {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.HTMLURL}}{{else}}origin{{end}} {{$.Issue.PullRequest.HeadBranch}}</div>
|
||||
{{else}}
|
||||
<div>git fetch origin {{$.Issue.PullRequest.GetGitRefName}}:{{$.Issue.PullRequest.HeadBranch}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div><h3 class="di">{{$.i18n.Tr "step2"}} </h3>{{$.i18n.Tr "repo.pulls.merge_instruction_step2_desc"}}</div>
|
||||
<div class="ui secondary segment">
|
||||
<div>git checkout {{$.Issue.PullRequest.BaseBranch}}</div>
|
||||
<div>git merge --no-ff {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{$.Issue.PullRequest.HeadBranch}}</div>
|
||||
<div>git push origin {{$.Issue.PullRequest.BaseBranch}}</div>
|
||||
</div>
|
||||
</div>
|
@ -1,9 +1,23 @@
|
||||
<template>
|
||||
<!--
|
||||
if this component is shown, either the user is admin (can do merge without checks), or they is a writer who has the permission to do merge
|
||||
if the user is a writer and can't do merge now (canMergeNow==false), then only show the Auto Merge for them
|
||||
How to test the UI manually:
|
||||
* Method 1: manually set some variables in pull.tmpl, eg: {{$notAllOverridableChecksOk = true}} {{$canMergeNow = false}}
|
||||
* Method 2: make a protected branch, then set state=pending/success :
|
||||
curl -X POST ${root_url}/api/v1/repos/${owner}/${repo}/statuses/${sha} \
|
||||
-H "accept: application/json" -H "authorization: Basic $base64_auth" -H "Content-Type: application/json" \
|
||||
-d '{"context": "test/context", "description": "description", "state": "${state}", "target_url": "http://localhost"}'
|
||||
-->
|
||||
<div>
|
||||
<!-- eslint-disable -->
|
||||
<div v-if="mergeForm.hasPendingPullRequestMerge" v-html="mergeForm.hasPendingPullRequestMergeTip" class="ui info message"></div>
|
||||
|
||||
<div class="ui form" v-if="showActionForm">
|
||||
<form :action="mergeForm.baseLink+'/merge'" method="post">
|
||||
<input type="hidden" name="_csrf" :value="csrfToken">
|
||||
<input type="hidden" name="head_commit_id" v-model="mergeForm.pullHeadCommitID">
|
||||
<input type="hidden" name="merge_when_checks_succeed" v-model="autoMergeWhenSucceed">
|
||||
|
||||
<template v-if="!mergeStyleDetail.hideMergeMessageTexts">
|
||||
<div class="field">
|
||||
@ -14,39 +28,72 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<button class="ui button" :class="[mergeForm.allOverridableChecksOk?'green':'red']" type="submit" name="do" :value="mergeStyle">
|
||||
<button class="ui button" :class="mergeButtonStyleClass" type="submit" name="do" :value="mergeStyle">
|
||||
{{ mergeStyleDetail.textDoMerge }}
|
||||
<template v-if="autoMergeWhenSucceed">
|
||||
{{ mergeForm.textAutoMergeButtonWhenSucceed }}
|
||||
</template>
|
||||
</button>
|
||||
|
||||
<button class="ui button merge-cancel" @click="toggleActionForm(false)">
|
||||
{{ mergeForm.textCancel }}
|
||||
</button>
|
||||
|
||||
<div class="ui checkbox ml-2" v-if="mergeForm.isPullBranchDeletable">
|
||||
<div class="ui checkbox ml-2" v-if="mergeForm.isPullBranchDeletable && !autoMergeWhenSucceed">
|
||||
<input name="delete_branch_after_merge" type="checkbox" v-model="deleteBranchAfterMerge" id="delete-branch-after-merge">
|
||||
<label for="delete-branch-after-merge">{{ mergeForm.textDeleteBranch }}</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<template v-if="!showActionForm">
|
||||
<div class="ui buttons merge-button" :class="[mergeForm.allOverridableChecksOk?'green':'red']" @click="toggleActionForm(true)">
|
||||
<div v-if="!showActionForm" class="df">
|
||||
<!-- the merge button -->
|
||||
<div class="ui buttons merge-button" :class="mergeButtonStyleClass" @click="toggleActionForm(true)" >
|
||||
<button class="ui button">
|
||||
<svg-icon name="octicon-git-merge"/>
|
||||
<span class="button-text">{{ mergeStyleDetail.textDoMerge }}</span>
|
||||
<span class="button-text">
|
||||
{{ mergeStyleDetail.textDoMerge }}
|
||||
<template v-if="autoMergeWhenSucceed">
|
||||
{{ mergeForm.textAutoMergeButtonWhenSucceed }}
|
||||
</template>
|
||||
</span>
|
||||
</button>
|
||||
<div class="ui dropdown icon button no-text" @click.stop="showMergeStyleMenu = !showMergeStyleMenu" v-if="mergeStyleAllowedCount>1">
|
||||
<svg-icon name="octicon-triangle-down" :size="14"/>
|
||||
<div class="menu" :class="{'show':showMergeStyleMenu}">
|
||||
<template v-for="msd in mergeForm.mergeStyles">
|
||||
<div class="item" v-if="msd.allowed" :key="msd.name" @click.stop="mergeStyle=msd.name">
|
||||
{{ msd.textDoMerge }}
|
||||
<!-- if can merge now, show one action "merge now", and an action "auto merge when succeed" -->
|
||||
<div class="item" v-if="msd.allowed && mergeForm.canMergeNow" :key="msd.name" @click.stop="switchMergeStyle(msd.name)">
|
||||
<div class="action-text">
|
||||
{{ msd.textDoMerge }}
|
||||
</div>
|
||||
<div v-if="!msd.hideAutoMerge" class="auto-merge-small" @click.stop="switchMergeStyle(msd.name, true)">
|
||||
<svg-icon name="octicon-clock" :size="14"/>
|
||||
<div class="auto-merge-tip">
|
||||
{{ mergeForm.textAutoMergeWhenSucceed }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- if can NOT merge now, only show one action "auto merge when succeed" -->
|
||||
<div class="item" v-if="msd.allowed && !mergeForm.canMergeNow && !msd.hideAutoMerge" :key="msd.name" @click.stop="switchMergeStyle(msd.name, true)">
|
||||
<div class="action-text">
|
||||
{{ msd.textDoMerge }} {{ mergeForm.textAutoMergeButtonWhenSucceed }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- the cancel auto merge button -->
|
||||
<form v-if="mergeForm.hasPendingPullRequestMerge" :action="mergeForm.baseLink+'/cancel_auto_merge'" method="post" class="ml-4">
|
||||
<input type="hidden" name="_csrf" :value="csrfToken">
|
||||
<button class="ui button">
|
||||
{{ mergeForm.textAutoMergeCancelSchedule }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -68,6 +115,7 @@ export default {
|
||||
mergeTitleFieldValue: '',
|
||||
mergeMessageFieldValue: '',
|
||||
deleteBranchAfterMerge: false,
|
||||
autoMergeWhenSucceed: false,
|
||||
|
||||
mergeStyle: '',
|
||||
mergeStyleDetail: { // dummy only, these values will come from one of the mergeForm.mergeStyles
|
||||
@ -82,6 +130,13 @@ export default {
|
||||
showActionForm: false,
|
||||
}),
|
||||
|
||||
computed: {
|
||||
mergeButtonStyleClass() {
|
||||
if (this.mergeForm.allOverridableChecksOk) return 'green';
|
||||
return this.autoMergeWhenSucceed ? 'blue' : 'red';
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
mergeStyle(val) {
|
||||
this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val);
|
||||
@ -90,7 +145,7 @@ export default {
|
||||
|
||||
created() {
|
||||
this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0);
|
||||
this.mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name;
|
||||
this.switchMergeStyle(this.mergeForm.mergeStyles.find((e) => e.allowed)?.name, !this.mergeForm.canMergeNow);
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@ -111,7 +166,11 @@ export default {
|
||||
this.deleteBranchAfterMerge = this.mergeForm.defaultDeleteBranchAfterMerge;
|
||||
this.mergeTitleFieldValue = this.mergeStyleDetail.mergeTitleFieldText;
|
||||
this.mergeMessageFieldValue = this.mergeStyleDetail.mergeMessageFieldText;
|
||||
}
|
||||
},
|
||||
switchMergeStyle(name, autoMerge = false) {
|
||||
this.mergeStyle = name;
|
||||
this.autoMergeWhenSucceed = autoMerge;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -124,4 +183,59 @@ export default {
|
||||
.ui.checkbox label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* make the dropdown list left-aligned */
|
||||
.ui.merge-button {
|
||||
position: relative;
|
||||
}
|
||||
.ui.merge-button .ui.dropdown {
|
||||
position: static;
|
||||
}
|
||||
.ui.merge-button > .ui.dropdown:last-child > .menu:not(.left) {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
.ui.merge-button .ui.dropdown .menu > .item {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
padding: 0 !important; /* polluted by semantic.css: .ui.dropdown .menu > .item { !important } */
|
||||
}
|
||||
|
||||
/* merge style list item */
|
||||
.action-text {
|
||||
padding: 0.8rem;
|
||||
flex: 1
|
||||
}
|
||||
|
||||
.auto-merge-small {
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
.auto-merge-small .auto-merge-tip {
|
||||
display: none;
|
||||
left: 38px;
|
||||
top: -1px;
|
||||
bottom: -1px;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
color: var(--color-info-text);
|
||||
background-color: var(--color-info-bg);
|
||||
border: 1px solid var(--color-info-border);
|
||||
border-left: none;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.auto-merge-small:hover {
|
||||
color: var(--color-info-text);
|
||||
background-color: var(--color-info-bg);
|
||||
border: 1px solid var(--color-info-border);
|
||||
}
|
||||
|
||||
.auto-merge-small:hover .auto-merge-tip {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import octiconChevronDown from '../../public/img/svg/octicon-chevron-down.svg';
|
||||
import octiconChevronRight from '../../public/img/svg/octicon-chevron-right.svg';
|
||||
import octiconCopy from '../../public/img/svg/octicon-copy.svg';
|
||||
import octiconClock from '../../public/img/svg/octicon-clock.svg';
|
||||
import octiconGitMerge from '../../public/img/svg/octicon-git-merge.svg';
|
||||
import octiconGitPullRequest from '../../public/img/svg/octicon-git-pull-request.svg';
|
||||
import octiconIssueClosed from '../../public/img/svg/octicon-issue-closed.svg';
|
||||
@ -23,6 +24,7 @@ export const svgs = {
|
||||
'octicon-chevron-down': octiconChevronDown,
|
||||
'octicon-chevron-right': octiconChevronRight,
|
||||
'octicon-copy': octiconCopy,
|
||||
'octicon-clock': octiconClock,
|
||||
'octicon-git-merge': octiconGitMerge,
|
||||
'octicon-git-pull-request': octiconGitPullRequest,
|
||||
'octicon-issue-closed': octiconIssueClosed,
|
||||
|
@ -2003,14 +2003,6 @@ table th[data-sortt-desc] {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
/* limit width of all direct dropdown menu children */
|
||||
/* https://github.com/go-gitea/gitea/pull/10835 */
|
||||
.dropdown:not(.selection) > .menu:not(.review-box) > *:not(.header) {
|
||||
max-width: 300px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ui.dropdown .menu .item {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
@ -1055,10 +1055,6 @@
|
||||
.merge-section {
|
||||
background-color: var(--color-box-body);
|
||||
|
||||
.item {
|
||||
padding: .25rem 0;
|
||||
}
|
||||
|
||||
.item-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
Loading…
Reference in New Issue
Block a user