diff options
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | models/branches.go | 2 | ||||
-rw-r--r-- | models/commit_status.go | 22 | ||||
-rw-r--r-- | models/migrations/migrations.go | 2 | ||||
-rw-r--r-- | models/migrations/v94.go | 24 | ||||
-rw-r--r-- | models/pull.go | 14 | ||||
-rw-r--r-- | modules/auth/repo_form.go | 2 | ||||
-rw-r--r-- | modules/pull/commit_status.go | 70 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 6 | ||||
-rw-r--r-- | routers/api/v1/repo/pull.go | 12 | ||||
-rw-r--r-- | routers/repo/pull.go | 30 | ||||
-rw-r--r-- | routers/repo/setting_protected_branch.go | 28 | ||||
-rw-r--r-- | templates/repo/issue/view_content/pull.tmpl | 249 | ||||
-rw-r--r-- | templates/repo/pulls/status.tmpl | 9 | ||||
-rw-r--r-- | templates/repo/settings/protected_branch.tmpl | 32 |
15 files changed, 387 insertions, 116 deletions
@@ -80,6 +80,7 @@ require ( github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 github.com/oliamb/cutter v0.2.2 github.com/philhofer/fwd v1.0.0 // indirect + github.com/pkg/errors v0.8.1 github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e github.com/prometheus/client_golang v1.1.0 github.com/prometheus/procfs v0.0.4 // indirect diff --git a/models/branches.go b/models/branches.go index 2a99d98955..9daaa487e7 100644 --- a/models/branches.go +++ b/models/branches.go @@ -36,6 +36,8 @@ type ProtectedBranch struct { EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"` MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` + EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` + StatusCheckContexts []string `xorm:"JSON TEXT"` ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"` ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"` diff --git a/models/commit_status.go b/models/commit_status.go index 9f0a32cdfb..6f6cbc387f 100644 --- a/models/commit_status.go +++ b/models/commit_status.go @@ -9,6 +9,7 @@ import ( "crypto/sha1" "fmt" "strings" + "time" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -205,6 +206,27 @@ func GetLatestCommitStatus(repo *Repository, sha string, page int) ([]*CommitSta return statuses, x.In("id", ids).Find(&statuses) } +// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts +func FindRepoRecentCommitStatusContexts(repoID int64, before time.Duration) ([]string, error) { + start := timeutil.TimeStampNow().AddDuration(-before) + ids := make([]int64, 0, 10) + if err := x.Table("commit_status"). + Where("repo_id = ?", repoID). + And("updated_unix >= ?", start). + Select("max( id ) as id"). + GroupBy("context_hash").OrderBy("max( id ) desc"). + Find(&ids); err != nil { + return nil, err + } + + var contexts = make([]string, 0, len(ids)) + if len(ids) == 0 { + return contexts, nil + } + return contexts, x.Select("context").Table("commit_status").In("id", ids).Find(&contexts) + +} + // NewCommitStatusOptions holds options for creating a CommitStatus type NewCommitStatusOptions struct { Repo *Repository diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 15e021c05a..885043dce5 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -242,6 +242,8 @@ var migrations = []Migration{ NewMigration("remove orphaned repository index statuses", removeLingeringIndexStatus), // v93 -> v94 NewMigration("add email notification enabled preference to user", addEmailNotificationEnabledToUser), + // v94 -> v95 + NewMigration("add enable_status_check, status_check_contexts to protected_branch", addStatusCheckColumnsForProtectedBranches), } // Migrate database to current version diff --git a/models/migrations/v94.go b/models/migrations/v94.go new file mode 100644 index 0000000000..5fe8c3fa12 --- /dev/null +++ b/models/migrations/v94.go @@ -0,0 +1,24 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import "github.com/go-xorm/xorm" + +func addStatusCheckColumnsForProtectedBranches(x *xorm.Engine) error { + type ProtectedBranch struct { + EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` + StatusCheckContexts []string `xorm:"JSON TEXT"` + } + + if err := x.Sync2(new(ProtectedBranch)); err != nil { + return err + } + + _, err := x.Cols("enable_status_check", "status_check_contexts").Update(&ProtectedBranch{ + EnableStatusCheck: false, + StatusCheckContexts: []string{}, + }) + return err +} diff --git a/models/pull.go b/models/pull.go index ecb5c1345e..2f7218f415 100644 --- a/models/pull.go +++ b/models/pull.go @@ -99,6 +99,20 @@ func (pr *PullRequest) LoadAttributes() error { return pr.loadAttributes(x) } +// LoadBaseRepo loads pull request base repository from database +func (pr *PullRequest) LoadBaseRepo() error { + if pr.BaseRepo == nil { + var repo Repository + if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil { + return err + } else if !has { + return ErrRepoNotExist{ID: pr.BaseRepoID} + } + pr.BaseRepo = &repo + } + return nil +} + // LoadIssue loads issue information from database func (pr *PullRequest) LoadIssue() (err error) { return pr.loadIssue(x) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 56ae77a7f7..8d10fc1570 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -155,6 +155,8 @@ type ProtectBranchForm struct { EnableMergeWhitelist bool MergeWhitelistUsers string MergeWhitelistTeams string + EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` + StatusCheckContexts []string RequiredApprovals int64 ApprovalsWhitelistUsers string ApprovalsWhitelistTeams string diff --git a/modules/pull/commit_status.go b/modules/pull/commit_status.go new file mode 100644 index 0000000000..bdadc329d6 --- /dev/null +++ b/modules/pull/commit_status.go @@ -0,0 +1,70 @@ +// Copyright 2019 The Gitea Authors. +// All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package pull + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + + "github.com/pkg/errors" +) + +// IsCommitStatusContextSuccess returns true if all required status check contexts succeed. +func IsCommitStatusContextSuccess(commitStatuses []*models.CommitStatus, requiredContexts []string) bool { + for _, ctx := range requiredContexts { + var found bool + for _, commitStatus := range commitStatuses { + if commitStatus.Context == ctx { + if commitStatus.State != models.CommitStatusSuccess { + return false + } + + found = true + break + } + } + if !found { + return false + } + } + return true +} + +// IsPullCommitStatusPass returns if all required status checks PASS +func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { + if err := pr.LoadProtectedBranch(); err != nil { + return false, errors.Wrap(err, "GetLatestCommitStatus") + } + if pr.ProtectedBranch == nil || !pr.ProtectedBranch.EnableStatusCheck { + return true, nil + } + + // check if all required status checks are successful + headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + if err != nil { + return false, errors.Wrap(err, "OpenRepository") + } + + if !headGitRepo.IsBranchExist(pr.HeadBranch) { + return false, errors.New("Head branch does not exist, can not merge") + } + + sha, err := headGitRepo.GetBranchCommitID(pr.HeadBranch) + if err != nil { + return false, errors.Wrap(err, "GetBranchCommitID") + } + + if err := pr.LoadBaseRepo(); err != nil { + return false, errors.Wrap(err, "LoadBaseRepo") + } + + commitStatuses, err := models.GetLatestCommitStatus(pr.BaseRepo, sha, 0) + if err != nil { + return false, errors.Wrap(err, "GetLatestCommitStatus") + } + + return IsCommitStatusContextSuccess(commitStatuses, pr.ProtectedBranch.StatusCheckContexts), nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8a1dc95b9f..843252dc34 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -981,6 +981,8 @@ pulls.cannot_merge_work_in_progress = This pull request is marked as a work in p pulls.data_broken = This pull request is broken due to missing fork information. pulls.files_conflicted = This pull request has changes conflicting with the target branch. pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments." +pulls.required_status_check_failed = Some required checks were not successful. +pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. pulls.blocked_by_approvals = "This Pull Request doesn't have enough approvals yet. %d of %d approvals granted." pulls.can_auto_merge_desc = This pull request can be merged automatically. pulls.cannot_auto_merge_desc = This pull request cannot be merged automatically due to conflicts. @@ -988,6 +990,7 @@ pulls.cannot_auto_merge_helper = Merge manually to resolve the conflicts. pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled. pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually. pulls.no_merge_wip = This pull request can not be merged because it is marked as being a work in progress. +pulls.no_merge_status_check = This pull request cannot be merged because not all required status checkes are successful. pulls.merge_pull_request = Merge Pull Request pulls.rebase_merge_pull_request = Rebase and Merge pulls.rebase_merge_commit_pull_request = Rebase and Merge (--no-ff) @@ -1311,6 +1314,9 @@ settings.protect_merge_whitelist_committers = Enable Merge Whitelist settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users or teams to merge pull requests into this branch. settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: +settings.protect_check_status_contexts = Enable Status Check +settings.protect_check_status_contexts_desc = Require status checks to pass before merging Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. +settings.protect_check_status_contexts_list = Status checks found in the last week for this repository settings.protect_required_approvals = Required approvals: settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews of whitelisted users or teams. settings.protect_approvals_whitelist_users = Whitelisted reviewers: diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 7be34c656e..0f9eab2f50 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/pull" + pull_service "code.gitea.io/gitea/modules/pull" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" milestone_service "code.gitea.io/gitea/services/milestone" @@ -571,6 +572,17 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { return } + isPass, err := pull_service.IsPullCommitStatusPass(pr) + if err != nil { + ctx.Error(500, "IsPullCommitStatusPass", err) + return + } + + if !isPass && !ctx.IsUserRepoAdmin() { + ctx.Status(405) + return + } + if len(form.Do) == 0 { form.Do = string(models.MergeStyleMerge) } diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 14b8670a20..180d592e3d 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/pull" + pull_service "code.gitea.io/gitea/modules/pull" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/gitdiff" @@ -322,6 +323,12 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare setMergeTarget(ctx, pull) + if err = pull.LoadProtectedBranch(); err != nil { + ctx.ServerError("GetLatestCommitStatus", err) + return nil + } + ctx.Data["EnableStatusCheck"] = pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck + var headGitRepo *git.Repository var headBranchExist bool // HeadRepo may be missing @@ -350,6 +357,18 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare ctx.Data["LatestCommitStatuses"] = commitStatuses ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(commitStatuses) } + + if pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck { + ctx.Data["is_context_required"] = func(context string) bool { + for _, c := range pull.ProtectedBranch.StatusCheckContexts { + if c == context { + return true + } + } + return false + } + ctx.Data["IsRequiredStatusCheckSuccess"] = pull_service.IsCommitStatusContextSuccess(commitStatuses, pull.ProtectedBranch.StatusCheckContexts) + } } } @@ -608,6 +627,17 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) { return } + isPass, err := pull_service.IsPullCommitStatusPass(pr) + if err != nil { + ctx.ServerError("IsPullCommitStatusPass", err) + return + } + if !isPass && !ctx.IsUserRepoAdmin() { + ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_status_check")) + ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index)) + return + } + if ctx.HasError() { ctx.Flash.Error(ctx.Data["ErrorMsg"].(string)) ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index)) diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index b5a115b6a4..80f44ead99 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -7,6 +7,7 @@ package repo import ( "fmt" "strings" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" @@ -125,6 +126,29 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.WhitelistUserIDs), ",") c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.MergeWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(protectBranch.ApprovalsWhitelistUserIDs), ",") + contexts, _ := models.FindRepoRecentCommitStatusContexts(c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts + for _, context := range protectBranch.StatusCheckContexts { + var found bool + for _, ctx := range contexts { + if ctx == context { + found = true + break + } + } + if !found { + contexts = append(contexts, context) + } + } + + c.Data["branch_status_check_contexts"] = contexts + c.Data["is_context_required"] = func(context string) bool { + for _, c := range protectBranch.StatusCheckContexts { + if c == context { + return true + } + } + return false + } if c.Repo.Owner.IsOrganization() { teams, err := c.Repo.Owner.TeamsWithAccessToRepo(c.Repo.Repository.ID, models.AccessModeRead) @@ -186,6 +210,10 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) if strings.TrimSpace(f.MergeWhitelistTeams) != "" { mergeWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ",")) } + + protectBranch.EnableStatusCheck = f.EnableStatusCheck + protectBranch.StatusCheckContexts = f.StatusCheckContexts + protectBranch.RequiredApprovals = f.RequiredApprovals if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" { approvalsWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.ApprovalsWhitelistUsers, ",")) diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index f0e39d5e37..f5ce8e0886 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -41,6 +41,7 @@ {{else if .IsFilesConflicted}}grey {{else if .IsPullRequestBroken}}red {{else if .IsBlockedByApprovals}}red + {{else if and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}red {{else if .Issue.PullRequest.IsChecking}}yellow {{else if .Issue.PullRequest.CanAutoMerge}}green {{else}}red{{end}}"><span class="mega-octicon octicon-git-merge"></span></a> @@ -104,130 +105,150 @@ <span class="octicon octicon-sync"></span> {{$.i18n.Tr "repo.pulls.is_checking"}} </div> + {{else if and (not .Issue.PullRequest.CanAutoMerge) .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}} + <div class="item text red"> + <span class="octicon octicon-x"></span> + {{$.i18n.Tr "repo.pulls.required_status_check_failed"}} + </div> {{else if .Issue.PullRequest.CanAutoMerge}} - <div class="item text green"> - <span class="octicon octicon-check"></span> - {{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}} + {{if and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}} + <div class="item text red"> + <span class="octicon octicon-x"></span> + {{$.i18n.Tr "repo.pulls.required_status_check_failed"}} </div> - {{if .AllowMerge}} - {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} - {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}} - <div class="ui divider"></div> - {{if $prUnit.PullRequestsConfig.AllowMerge}} - <div class="ui form merge-fields" style="display: none"> - <form action="{{.Link}}/merge" method="post"> - {{.CsrfTokenHtml}} - <div class="field"> - <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> - </div> - <div class="field"> - <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea> - </div> - <button class="ui green button" type="submit" name="do" value="merge"> - {{$.i18n.Tr "repo.pulls.merge_pull_request"}} - </button> - <button class="ui button merge-cancel"> - {{$.i18n.Tr "cancel"}} - </button> - </form> - </div> - {{end}} - {{if $prUnit.PullRequestsConfig.AllowRebase}} - <div class="ui form rebase-fields" style="display: none"> - <form action="{{.Link}}/merge" method="post"> - {{.CsrfTokenHtml}} - <button class="ui green button" type="submit" name="do" value="rebase"> - {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}} - </button> - <button class="ui button merge-cancel"> - {{$.i18n.Tr "cancel"}} - </button> - </form> - </div> - {{end}} - {{if $prUnit.PullRequestsConfig.AllowRebaseMerge}} - <div class="ui form rebase-merge-fields" style="display: none"> - <form action="{{.Link}}/merge" method="post"> - {{.CsrfTokenHtml}} - <div class="field"> - <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> - </div> - <div class="field"> - <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea> - </div> - <button class="ui green button" type="submit" name="do" value="rebase-merge"> - {{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}} - </button> - <button class="ui button merge-cancel"> - {{$.i18n.Tr "cancel"}} - </button> - </form> + {{end}} + {{if or $.IsRepoAdmin (not .EnableStatusCheck) .IsRequiredStatusCheckSuccess}} + {{if and $.IsRepoAdmin .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}} + <div class="item text yellow"> + <span class="octicon octicon-primitive-dot"></span> + {{$.i18n.Tr "repo.pulls.required_status_check_administrator"}} </div> - {{end}} - {{if $prUnit.PullRequestsConfig.AllowSquash}} - <div class="ui form squash-fields" style="display: none"> - <form action="{{.Link}}/merge" method="post"> - {{.CsrfTokenHtml}} - <div class="field"> - <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}"> - </div> - <div class="field"> - <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea> - </div> - <button class="ui green button" type="submit" name="do" value="squash"> - {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}} - </button> - <button class="ui button merge-cancel"> - {{$.i18n.Tr "cancel"}} - </button> - </form> + {{else}} + <div class="item text green"> + <span class="octicon octicon-check"></span> + {{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}} </div> - {{end}} - <div class="ui green buttons merge-button"> - <button class="ui button" data-do="{{.MergeStyle}}"> - <span class="octicon octicon-git-merge"></span> - <span class="button-text"> - {{if eq .MergeStyle "merge"}} - {{$.i18n.Tr "repo.pulls.merge_pull_request"}} - {{end}} - {{if eq .MergeStyle "rebase"}} - {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}} - {{end}} - {{if eq .MergeStyle "rebase-merge"}} - {{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}} - {{end}} - {{if eq .MergeStyle "squash"}} - {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}} - {{end}} - </span> - </button> - <div class="ui dropdown icon button"> - <i class="dropdown icon"></i> - <div class="menu"> - {{if $prUnit.PullRequestsConfig.AllowMerge}} - <div class="item{{if eq .MergeStyle "merge"}} active selected{{end}}" data-do="merge">{{$.i18n.Tr "repo.pulls.merge_pull_request"}}</div> + {{end}} + {{if .AllowMerge}} + {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} + {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}} + <div class="ui divider"></div> + {{if $prUnit.PullRequestsConfig.AllowMerge}} + <div class="ui form merge-fields" style="display: none"> + <form action="{{.Link}}/merge" method="post"> + {{.CsrfTokenHtml}} + <div class="field"> + <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> + </div> + <div class="field"> + <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea> + </div> + <button class="ui green button" type="submit" name="do" value="merge"> + {{$.i18n.Tr "repo.pulls.merge_pull_request"}} + </button> + <button class="ui button merge-cancel"> + {{$.i18n.Tr "cancel"}} + </button> + </form> + </div> + {{end}} + {{if $prUnit.PullRequestsConfig.AllowRebase}} + <div class="ui form rebase-fields" style="display: none"> + <form action="{{.Link}}/merge" method="post"> + {{.CsrfTokenHtml}} + <button class="ui green button" type="submit" name="do" value="rebase"> + {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}} + </button> + <button class="ui button merge-cancel"> + {{$.i18n.Tr "cancel"}} + </button> + </form> + </div> + {{end}} + {{if $prUnit.PullRequestsConfig.AllowRebaseMerge}} + <div class="ui form rebase-merge-fields" style="display: none"> + <form action="{{.Link}}/merge" method="post"> + {{.CsrfTokenHtml}} + <div class="field"> + <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> + </div> + <div class="field"> + <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea> + </div> + <button class="ui green button" type="submit" name="do" value="rebase-merge"> + {{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}} + </button> + <button class="ui button merge-cancel"> + {{$.i18n.Tr "cancel"}} + </button> + </form> + </div> + {{end}} + {{if $prUnit.PullRequestsConfig.AllowSquash}} + <div class="ui form squash-fields" style="display: none"> + <form action="{{.Link}}/merge" method="post"> + {{.CsrfTokenHtml}} + <div class="field"> + <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}"> + </div> + <div class="field"> + <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea> + </div> + <button class="ui green button" type="submit" name="do" value="squash"> + {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}} + </button> + <button class="ui button merge-cancel"> + {{$.i18n.Tr "cancel"}} + </button> + </form> + </div> + {{end}} + <div class="ui green buttons merge-button"> + <button class="ui button" data-do="{{.MergeStyle}}"> + <span class="octicon octicon-git-merge"></span> + <span class="button-text"> + {{if eq .MergeStyle "merge"}} + {{$.i18n.Tr "repo.pulls.merge_pull_request"}} {{end}} - {{if $prUnit.PullRequestsConfig.AllowRebase}} - <div class="item{{if eq .MergeStyle "rebase"}} active selected{{end}}" data-do="rebase">{{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}}</div> + {{if eq .MergeStyle "rebase"}} + {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}} {{end}} - {{if $prUnit.PullRequestsConfig.AllowRebaseMerge}} - <div class="item{{if eq .MergeStyle "rebase-merge"}} active selected{{end}}" data-do="rebase-merge">{{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div> + {{if eq .MergeStyle "rebase-merge"}} + {{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}} {{end}} - {{if $prUnit.PullRequestsConfig.AllowSquash}} - <div class="item{{if eq .MergeStyle "squash"}} active selected{{end}}" data-do="squash">{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}</div> + {{if eq .MergeStyle "squash"}} + {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}} {{end}} + </span> + </button> + <div class="ui dropdown icon button"> + <i class="dropdown icon"></i> + <div class="menu"> + {{if $prUnit.PullRequestsConfig.AllowMerge}} + <div class="item{{if eq .MergeStyle "merge"}} active selected{{end}}" data-do="merge">{{$.i18n.Tr "repo.pulls.merge_pull_request"}}</div> + {{end}} + {{if $prUnit.PullRequestsConfig.AllowRebase}} + <div class="item{{if eq .MergeStyle "rebase"}} active selected{{end}}" data-do="rebase">{{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}}</div> + {{end}} + {{if $prUnit.PullRequestsConfig.AllowRebaseMerge}} + <div class="item{{if eq .MergeStyle "rebase-merge"}} active selected{{end}}" data-do="rebase-merge">{{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div> + {{end}} + {{if $prUnit.PullRequestsConfig.AllowSquash}} + <div class="item{{if eq .MergeStyle "squash"}} active selected{{end}}" data-do="squash">{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}</div> + {{end}} + </div> </div> </div> - </div> - {{else}} - <div class="item text red"> - <span class="octicon octicon-x"></span> - {{$.i18n.Tr "repo.pulls.no_merge_desc"}} - </div> - <div class="item text grey"> - <span class="octicon octicon-info"></span> - {{$.i18n.Tr "repo.pulls.no_merge_helper"}} - </div> + {{else}} + <div class="item text red"> + <span class="octicon octicon-x"></span> + {{$.i18n.Tr "repo.pulls.no_merge_desc"}} + </div> + <div class="item text grey"> + <span class="octicon octicon-info"></span> + {{$.i18n.Tr "repo.pulls.no_merge_helper"}} + </div> + {{end}} {{end}} {{end}} {{else}} diff --git a/templates/repo/pulls/status.tmpl b/templates/repo/pulls/status.tmpl index e1401aa8bb..76a4eb5423 100644 --- a/templates/repo/pulls/status.tmpl +++ b/templates/repo/pulls/status.tmpl @@ -15,7 +15,12 @@ <div class="ui attached segment"> <span>{{template "repo/commit_status" .}}</span> <span class="ui">{{.Context}} <span class="text grey">{{.Description}}</span></span> - <div class="ui right">{{if .TargetURL}}<a href="{{.TargetURL}}">Details</a>{{end}}</div> + <div class="ui right"> + {{if $.is_context_required}} + {{if (call $.is_context_required .Context)}}<div class="ui label">Required</div>{{end}} + {{end}} + <span class="ui">{{if .TargetURL}}<a href="{{.TargetURL}}">Details</a>{{end}}</span> + </div> </div> {{end}} -{{end}}
\ No newline at end of file +{{end}} diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 066350f97a..067d1d9761 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -105,6 +105,38 @@ </div> <div class="field"> + <div class="ui checkbox"> + <input class="enable-statuscheck" name="enable_status_check" type="checkbox" data-target="#statuscheck_contexts_box" {{if .Branch.EnableStatusCheck}}checked{{end}}> + <label>{{.i18n.Tr "repo.settings.protect_check_status_contexts"}}</label> + <p class="help">{{.i18n.Tr "repo.settings.protect_check_status_contexts_desc"}}</p> + </div> + </div> + + <div id="statuscheck_contexts_box" class="fields {{if not .Branch.EnableStatusCheck}}disabled{{end}}"> + <div class="field"> + <table class="ui celled table six column"> + <thead> + <tr><th> + {{.i18n.Tr "repo.settings.protect_check_status_contexts_list"}} + </th> + </tr> + </thead> + <tbody> + {{range $.branch_status_check_contexts}} + <tr><td> + <span class="ui checkbox"> + <input class="enable-whitelist" name="status_check_contexts" value="{{.}}" type="checkbox" {{if $.is_context_require}}{{if call $.is_context_required .}}checked{{end}}{{end}}> + </span> + {{.}} + {{if $.is_context_required}}{{if call $.is_context_required .}}<div class="ui label right">Required</div>{{end}}{{end}} + </td></tr> + {{end}} + </tbody> + </table> + </div> + </div> + + <div class="field"> <label for="required-approvals">{{.i18n.Tr "repo.settings.protect_required_approvals"}}</label> <input name="required_approvals" id="required-approvals" type="number" value="{{.Branch.RequiredApprovals}}"> <p class="help">{{.i18n.Tr "repo.settings.protect_required_approvals_desc"}}</p> |