mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Prevent merge of outdated PRs on protected branches (#11012)
* Block PR on Outdated Branch * finalize * cleanup * fix typo and sentences thanks @guillep2k Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		@@ -47,6 +47,7 @@ type ProtectedBranch struct {
 | 
				
			|||||||
	ApprovalsWhitelistTeamIDs []int64  `xorm:"JSON TEXT"`
 | 
						ApprovalsWhitelistTeamIDs []int64  `xorm:"JSON TEXT"`
 | 
				
			||||||
	RequiredApprovals         int64    `xorm:"NOT NULL DEFAULT 0"`
 | 
						RequiredApprovals         int64    `xorm:"NOT NULL DEFAULT 0"`
 | 
				
			||||||
	BlockOnRejectedReviews    bool     `xorm:"NOT NULL DEFAULT false"`
 | 
						BlockOnRejectedReviews    bool     `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
 | 
						BlockOnOutdatedBranch     bool     `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
	DismissStaleApprovals     bool     `xorm:"NOT NULL DEFAULT false"`
 | 
						DismissStaleApprovals     bool     `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
	RequireSignedCommits      bool     `xorm:"NOT NULL DEFAULT false"`
 | 
						RequireSignedCommits      bool     `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
	ProtectedFilePatterns     string   `xorm:"TEXT"`
 | 
						ProtectedFilePatterns     string   `xorm:"TEXT"`
 | 
				
			||||||
@@ -194,6 +195,11 @@ func (protectBranch *ProtectedBranch) MergeBlockedByRejectedReview(pr *PullReque
 | 
				
			|||||||
	return rejectExist
 | 
						return rejectExist
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch
 | 
				
			||||||
 | 
					func (protectBranch *ProtectedBranch) MergeBlockedByOutdatedBranch(pr *PullRequest) bool {
 | 
				
			||||||
 | 
						return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetProtectedFilePatterns parses a semicolon separated list of protected file patterns and returns a glob.Glob slice
 | 
					// GetProtectedFilePatterns parses a semicolon separated list of protected file patterns and returns a glob.Glob slice
 | 
				
			||||||
func (protectBranch *ProtectedBranch) GetProtectedFilePatterns() []glob.Glob {
 | 
					func (protectBranch *ProtectedBranch) GetProtectedFilePatterns() []glob.Glob {
 | 
				
			||||||
	extarr := make([]glob.Glob, 0, 10)
 | 
						extarr := make([]glob.Glob, 0, 10)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -202,10 +202,12 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("Add EmailHash Table", addEmailHashTable),
 | 
						NewMigration("Add EmailHash Table", addEmailHashTable),
 | 
				
			||||||
	// v134 -> v135
 | 
						// v134 -> v135
 | 
				
			||||||
	NewMigration("Refix merge base for merged pull requests", refixMergeBase),
 | 
						NewMigration("Refix merge base for merged pull requests", refixMergeBase),
 | 
				
			||||||
	// v135 -> 136
 | 
						// v135 -> v136
 | 
				
			||||||
	NewMigration("Add OrgID column to Labels table", addOrgIDLabelColumn),
 | 
						NewMigration("Add OrgID column to Labels table", addOrgIDLabelColumn),
 | 
				
			||||||
	// v136 -> 137
 | 
						// v136 -> v137
 | 
				
			||||||
	NewMigration("Add CommitsAhead and CommitsBehind Column to PullRequest Table", addCommitDivergenceToPulls),
 | 
						NewMigration("Add CommitsAhead and CommitsBehind Column to PullRequest Table", addCommitDivergenceToPulls),
 | 
				
			||||||
 | 
						// v137 -> v138
 | 
				
			||||||
 | 
						NewMigration("Add Branch Protection Block Outdated Branch", addBlockOnOutdatedBranch),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCurrentDBVersion returns the current db version
 | 
					// GetCurrentDBVersion returns the current db version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								models/migrations/v137.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								models/migrations/v137.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					// Copyright 2020 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 (
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addBlockOnOutdatedBranch(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						type ProtectedBranch struct {
 | 
				
			||||||
 | 
							BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return x.Sync2(new(ProtectedBranch))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -173,6 +173,7 @@ type ProtectBranchForm struct {
 | 
				
			|||||||
	ApprovalsWhitelistUsers  string
 | 
						ApprovalsWhitelistUsers  string
 | 
				
			||||||
	ApprovalsWhitelistTeams  string
 | 
						ApprovalsWhitelistTeams  string
 | 
				
			||||||
	BlockOnRejectedReviews   bool
 | 
						BlockOnRejectedReviews   bool
 | 
				
			||||||
 | 
						BlockOnOutdatedBranch    bool
 | 
				
			||||||
	DismissStaleApprovals    bool
 | 
						DismissStaleApprovals    bool
 | 
				
			||||||
	RequireSignedCommits     bool
 | 
						RequireSignedCommits     bool
 | 
				
			||||||
	ProtectedFilePatterns    string
 | 
						ProtectedFilePatterns    string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -118,6 +118,7 @@ func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
 | 
				
			|||||||
		ApprovalsWhitelistUsernames: approvalsWhitelistUsernames,
 | 
							ApprovalsWhitelistUsernames: approvalsWhitelistUsernames,
 | 
				
			||||||
		ApprovalsWhitelistTeams:     approvalsWhitelistTeams,
 | 
							ApprovalsWhitelistTeams:     approvalsWhitelistTeams,
 | 
				
			||||||
		BlockOnRejectedReviews:      bp.BlockOnRejectedReviews,
 | 
							BlockOnRejectedReviews:      bp.BlockOnRejectedReviews,
 | 
				
			||||||
 | 
							BlockOnOutdatedBranch:       bp.BlockOnOutdatedBranch,
 | 
				
			||||||
		DismissStaleApprovals:       bp.DismissStaleApprovals,
 | 
							DismissStaleApprovals:       bp.DismissStaleApprovals,
 | 
				
			||||||
		RequireSignedCommits:        bp.RequireSignedCommits,
 | 
							RequireSignedCommits:        bp.RequireSignedCommits,
 | 
				
			||||||
		ProtectedFilePatterns:       bp.ProtectedFilePatterns,
 | 
							ProtectedFilePatterns:       bp.ProtectedFilePatterns,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ type BranchProtection struct {
 | 
				
			|||||||
	ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
						ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
				
			||||||
	ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
						ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
				
			||||||
	BlockOnRejectedReviews      bool     `json:"block_on_rejected_reviews"`
 | 
						BlockOnRejectedReviews      bool     `json:"block_on_rejected_reviews"`
 | 
				
			||||||
 | 
						BlockOnOutdatedBranch       bool     `json:"block_on_outdated_branch"`
 | 
				
			||||||
	DismissStaleApprovals       bool     `json:"dismiss_stale_approvals"`
 | 
						DismissStaleApprovals       bool     `json:"dismiss_stale_approvals"`
 | 
				
			||||||
	RequireSignedCommits        bool     `json:"require_signed_commits"`
 | 
						RequireSignedCommits        bool     `json:"require_signed_commits"`
 | 
				
			||||||
	ProtectedFilePatterns       string   `json:"protected_file_patterns"`
 | 
						ProtectedFilePatterns       string   `json:"protected_file_patterns"`
 | 
				
			||||||
@@ -66,6 +67,7 @@ type CreateBranchProtectionOption struct {
 | 
				
			|||||||
	ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
						ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
				
			||||||
	ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
						ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
				
			||||||
	BlockOnRejectedReviews      bool     `json:"block_on_rejected_reviews"`
 | 
						BlockOnRejectedReviews      bool     `json:"block_on_rejected_reviews"`
 | 
				
			||||||
 | 
						BlockOnOutdatedBranch       bool     `json:"block_on_outdated_branch"`
 | 
				
			||||||
	DismissStaleApprovals       bool     `json:"dismiss_stale_approvals"`
 | 
						DismissStaleApprovals       bool     `json:"dismiss_stale_approvals"`
 | 
				
			||||||
	RequireSignedCommits        bool     `json:"require_signed_commits"`
 | 
						RequireSignedCommits        bool     `json:"require_signed_commits"`
 | 
				
			||||||
	ProtectedFilePatterns       string   `json:"protected_file_patterns"`
 | 
						ProtectedFilePatterns       string   `json:"protected_file_patterns"`
 | 
				
			||||||
@@ -88,6 +90,7 @@ type EditBranchProtectionOption struct {
 | 
				
			|||||||
	ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
						ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
				
			||||||
	ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
						ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
				
			||||||
	BlockOnRejectedReviews      *bool    `json:"block_on_rejected_reviews"`
 | 
						BlockOnRejectedReviews      *bool    `json:"block_on_rejected_reviews"`
 | 
				
			||||||
 | 
						BlockOnOutdatedBranch       *bool    `json:"block_on_outdated_branch"`
 | 
				
			||||||
	DismissStaleApprovals       *bool    `json:"dismiss_stale_approvals"`
 | 
						DismissStaleApprovals       *bool    `json:"dismiss_stale_approvals"`
 | 
				
			||||||
	RequireSignedCommits        *bool    `json:"require_signed_commits"`
 | 
						RequireSignedCommits        *bool    `json:"require_signed_commits"`
 | 
				
			||||||
	ProtectedFilePatterns       *string  `json:"protected_file_patterns"`
 | 
						ProtectedFilePatterns       *string  `json:"protected_file_patterns"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1102,6 +1102,7 @@ pulls.required_status_check_missing = Some required checks are missing.
 | 
				
			|||||||
pulls.required_status_check_administrator = As an administrator, you may still merge this pull request.
 | 
					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.blocked_by_approvals = "This Pull Request doesn't have enough approvals yet. %d of %d approvals granted."
 | 
				
			||||||
pulls.blocked_by_rejection = "This Pull Request has changes requested by an official reviewer."
 | 
					pulls.blocked_by_rejection = "This Pull Request has changes requested by an official reviewer."
 | 
				
			||||||
 | 
					pulls.blocked_by_outdated_branch = "This Pull Request is blocked because it's outdated."
 | 
				
			||||||
pulls.can_auto_merge_desc = This pull request can be merged automatically.
 | 
					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.
 | 
					pulls.cannot_auto_merge_desc = This pull request cannot be merged automatically due to conflicts.
 | 
				
			||||||
pulls.cannot_auto_merge_helper = Merge manually to resolve the conflicts.
 | 
					pulls.cannot_auto_merge_helper = Merge manually to resolve the conflicts.
 | 
				
			||||||
@@ -1528,6 +1529,8 @@ settings.protected_branch_deletion = Disable Branch Protection
 | 
				
			|||||||
settings.protected_branch_deletion_desc = Disabling branch protection allows users with write permission to push to the branch. Continue?
 | 
					settings.protected_branch_deletion_desc = Disabling branch protection allows users with write permission to push to the branch. Continue?
 | 
				
			||||||
settings.block_rejected_reviews = Block merge on rejected reviews
 | 
					settings.block_rejected_reviews = Block merge on rejected reviews
 | 
				
			||||||
settings.block_rejected_reviews_desc = Merging will not be possible when changes are requested by official reviewers, even if there are enough approvals.
 | 
					settings.block_rejected_reviews_desc = Merging will not be possible when changes are requested by official reviewers, even if there are enough approvals.
 | 
				
			||||||
 | 
					settings.block_outdated_branch = Block merge if pull request is outdated
 | 
				
			||||||
 | 
					settings.block_outdated_branch_desc = Merging will not be possible when head branch is behind base branch.
 | 
				
			||||||
settings.default_branch_desc = Select a default repository branch for pull requests and code commits:
 | 
					settings.default_branch_desc = Select a default repository branch for pull requests and code commits:
 | 
				
			||||||
settings.choose_branch = Choose a branch…
 | 
					settings.choose_branch = Choose a branch…
 | 
				
			||||||
settings.no_protected_branch = There are no protected branches.
 | 
					settings.no_protected_branch = There are no protected branches.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -340,6 +340,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec
 | 
				
			|||||||
		DismissStaleApprovals:    form.DismissStaleApprovals,
 | 
							DismissStaleApprovals:    form.DismissStaleApprovals,
 | 
				
			||||||
		RequireSignedCommits:     form.RequireSignedCommits,
 | 
							RequireSignedCommits:     form.RequireSignedCommits,
 | 
				
			||||||
		ProtectedFilePatterns:    form.ProtectedFilePatterns,
 | 
							ProtectedFilePatterns:    form.ProtectedFilePatterns,
 | 
				
			||||||
 | 
							BlockOnOutdatedBranch:    form.BlockOnOutdatedBranch,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
 | 
						err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
 | 
				
			||||||
@@ -475,6 +476,10 @@ func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtection
 | 
				
			|||||||
		protectBranch.ProtectedFilePatterns = *form.ProtectedFilePatterns
 | 
							protectBranch.ProtectedFilePatterns = *form.ProtectedFilePatterns
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if form.BlockOnOutdatedBranch != nil {
 | 
				
			||||||
 | 
							protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var whitelistUsers []int64
 | 
						var whitelistUsers []int64
 | 
				
			||||||
	if form.PushWhitelistUsernames != nil {
 | 
						if form.PushWhitelistUsernames != nil {
 | 
				
			||||||
		whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
 | 
							whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -263,6 +263,7 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Detect Protected file pattern
 | 
				
			||||||
			globs := protectBranch.GetProtectedFilePatterns()
 | 
								globs := protectBranch.GetProtectedFilePatterns()
 | 
				
			||||||
			if len(globs) > 0 {
 | 
								if len(globs) > 0 {
 | 
				
			||||||
				err := checkFileProtection(oldCommitID, newCommitID, globs, gitRepo, env)
 | 
									err := checkFileProtection(oldCommitID, newCommitID, globs, gitRepo, env)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1065,6 +1065,7 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			|||||||
			cnt := pull.ProtectedBranch.GetGrantedApprovalsCount(pull)
 | 
								cnt := pull.ProtectedBranch.GetGrantedApprovalsCount(pull)
 | 
				
			||||||
			ctx.Data["IsBlockedByApprovals"] = !pull.ProtectedBranch.HasEnoughApprovals(pull)
 | 
								ctx.Data["IsBlockedByApprovals"] = !pull.ProtectedBranch.HasEnoughApprovals(pull)
 | 
				
			||||||
			ctx.Data["IsBlockedByRejection"] = pull.ProtectedBranch.MergeBlockedByRejectedReview(pull)
 | 
								ctx.Data["IsBlockedByRejection"] = pull.ProtectedBranch.MergeBlockedByRejectedReview(pull)
 | 
				
			||||||
 | 
								ctx.Data["IsBlockedByOutdatedBranch"] = pull.ProtectedBranch.MergeBlockedByOutdatedBranch(pull)
 | 
				
			||||||
			ctx.Data["GrantedApprovals"] = cnt
 | 
								ctx.Data["GrantedApprovals"] = cnt
 | 
				
			||||||
			ctx.Data["RequireSigned"] = pull.ProtectedBranch.RequireSignedCommits
 | 
								ctx.Data["RequireSigned"] = pull.ProtectedBranch.RequireSignedCommits
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -248,6 +248,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
 | 
				
			|||||||
		protectBranch.DismissStaleApprovals = f.DismissStaleApprovals
 | 
							protectBranch.DismissStaleApprovals = f.DismissStaleApprovals
 | 
				
			||||||
		protectBranch.RequireSignedCommits = f.RequireSignedCommits
 | 
							protectBranch.RequireSignedCommits = f.RequireSignedCommits
 | 
				
			||||||
		protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns
 | 
							protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns
 | 
				
			||||||
 | 
							protectBranch.BlockOnOutdatedBranch = f.BlockOnOutdatedBranch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
 | 
							err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
 | 
				
			||||||
			UserIDs:          whitelistUsers,
 | 
								UserIDs:          whitelistUsers,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -574,16 +574,22 @@ func CheckPRReadyToMerge(pr *models.PullRequest) (err error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if enoughApprovals := pr.ProtectedBranch.HasEnoughApprovals(pr); !enoughApprovals {
 | 
						if !pr.ProtectedBranch.HasEnoughApprovals(pr) {
 | 
				
			||||||
		return models.ErrNotAllowedToMerge{
 | 
							return models.ErrNotAllowedToMerge{
 | 
				
			||||||
			Reason: "Does not have enough approvals",
 | 
								Reason: "Does not have enough approvals",
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if rejected := pr.ProtectedBranch.MergeBlockedByRejectedReview(pr); rejected {
 | 
						if pr.ProtectedBranch.MergeBlockedByRejectedReview(pr) {
 | 
				
			||||||
		return models.ErrNotAllowedToMerge{
 | 
							return models.ErrNotAllowedToMerge{
 | 
				
			||||||
			Reason: "There are requested changes",
 | 
								Reason: "There are requested changes",
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pr.ProtectedBranch.MergeBlockedByOutdatedBranch(pr) {
 | 
				
			||||||
 | 
							return models.ErrNotAllowedToMerge{
 | 
				
			||||||
 | 
								Reason: "The head branch is behind the base branch",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,7 @@
 | 
				
			|||||||
	{{- else if .IsPullRequestBroken}}red
 | 
						{{- else if .IsPullRequestBroken}}red
 | 
				
			||||||
	{{- else if .IsBlockedByApprovals}}red
 | 
						{{- else if .IsBlockedByApprovals}}red
 | 
				
			||||||
	{{- else if .IsBlockedByRejection}}red
 | 
						{{- else if .IsBlockedByRejection}}red
 | 
				
			||||||
 | 
						{{- else if .IsBlockedByOutdatedBranch}}red
 | 
				
			||||||
	{{- else if and .EnableStatusCheck (or .RequiredStatusCheckState.IsFailure .RequiredStatusCheckState.IsError)}}red
 | 
						{{- else if and .EnableStatusCheck (or .RequiredStatusCheckState.IsFailure .RequiredStatusCheckState.IsError)}}red
 | 
				
			||||||
	{{- else if and .EnableStatusCheck (or (not $.LatestCommitStatus) .RequiredStatusCheckState.IsPending .RequiredStatusCheckState.IsWarning)}}yellow
 | 
						{{- else if and .EnableStatusCheck (or (not $.LatestCommitStatus) .RequiredStatusCheckState.IsPending .RequiredStatusCheckState.IsWarning)}}yellow
 | 
				
			||||||
	{{- else if and .RequireSigned (not .WillSign)}}}red
 | 
						{{- else if and .RequireSigned (not .WillSign)}}}red
 | 
				
			||||||
@@ -138,6 +139,11 @@
 | 
				
			|||||||
						<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
 | 
											<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
 | 
				
			||||||
					{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
 | 
										{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
									{{else if .IsBlockedByOutdatedBranch}}
 | 
				
			||||||
 | 
										<div class="item text red">
 | 
				
			||||||
 | 
											<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
 | 
				
			||||||
 | 
										{{$.i18n.Tr "repo.pulls.blocked_by_outdated_branch"}}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
				{{else if and .EnableStatusCheck (or .RequiredStatusCheckState.IsError .RequiredStatusCheckState.IsFailure)}}
 | 
									{{else if and .EnableStatusCheck (or .RequiredStatusCheckState.IsError .RequiredStatusCheckState.IsFailure)}}
 | 
				
			||||||
					<div class="item text red">
 | 
										<div class="item text red">
 | 
				
			||||||
						<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
 | 
											<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
 | 
				
			||||||
@@ -158,7 +164,7 @@
 | 
				
			|||||||
						{{$.i18n.Tr (printf "repo.signing.wont_sign.%s" .WontSignReason) }}
 | 
											{{$.i18n.Tr (printf "repo.signing.wont_sign.%s" .WontSignReason) }}
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				{{$notAllOverridableChecksOk := or .IsBlockedByApprovals .IsBlockedByRejection (and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess))}}
 | 
									{{$notAllOverridableChecksOk := or .IsBlockedByApprovals .IsBlockedByRejection .IsBlockedByOutdatedBranch (and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess))}}
 | 
				
			||||||
				{{if and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .RequireSigned) .WillSign)}}
 | 
									{{if and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .RequireSigned) .WillSign)}}
 | 
				
			||||||
					{{if $notAllOverridableChecksOk}}
 | 
										{{if $notAllOverridableChecksOk}}
 | 
				
			||||||
						<div class="item text yellow">
 | 
											<div class="item text yellow">
 | 
				
			||||||
@@ -342,6 +348,11 @@
 | 
				
			|||||||
						{{svg "octicon-x" 16}}
 | 
											{{svg "octicon-x" 16}}
 | 
				
			||||||
					{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
 | 
										{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
									{{else if .IsBlockedByOutdatedBranch}}
 | 
				
			||||||
 | 
										<div class="item text red">
 | 
				
			||||||
 | 
											<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
 | 
				
			||||||
 | 
										{{$.i18n.Tr "repo.pulls.blocked_by_outdated_branch"}}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
				{{else if and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess)}}
 | 
									{{else if and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess)}}
 | 
				
			||||||
					<div class="item text red">
 | 
										<div class="item text red">
 | 
				
			||||||
						{{svg "octicon-x" 16}}
 | 
											{{svg "octicon-x" 16}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -225,6 +225,13 @@
 | 
				
			|||||||
							<p class="help">{{.i18n.Tr "repo.settings.require_signed_commits_desc"}}</p>
 | 
												<p class="help">{{.i18n.Tr "repo.settings.require_signed_commits_desc"}}</p>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="field">
 | 
				
			||||||
 | 
											<div class="ui checkbox">
 | 
				
			||||||
 | 
												<input name="block_on_outdated_branch" type="checkbox" {{if .Branch.BlockOnOutdatedBranch}}checked{{end}}>
 | 
				
			||||||
 | 
												<label for="block_on_outdated_branch">{{.i18n.Tr "repo.settings.block_outdated_branch"}}</label>
 | 
				
			||||||
 | 
												<p class="help">{{.i18n.Tr "repo.settings.block_outdated_branch_desc"}}</p>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
					<div class="field">
 | 
										<div class="field">
 | 
				
			||||||
						<label for="protected_file_patterns">{{.i18n.Tr "repo.settings.protect_protected_file_patterns"}}</label>
 | 
											<label for="protected_file_patterns">{{.i18n.Tr "repo.settings.protect_protected_file_patterns"}}</label>
 | 
				
			||||||
						<input name="protected_file_patterns" id="protected_file_patterns" type="text" value="{{.Branch.ProtectedFilePatterns}}">
 | 
											<input name="protected_file_patterns" id="protected_file_patterns" type="text" value="{{.Branch.ProtectedFilePatterns}}">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10072,6 +10072,10 @@
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
					          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "block_on_outdated_branch": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "BlockOnOutdatedBranch"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "block_on_rejected_reviews": {
 | 
					        "block_on_rejected_reviews": {
 | 
				
			||||||
          "type": "boolean",
 | 
					          "type": "boolean",
 | 
				
			||||||
          "x-go-name": "BlockOnRejectedReviews"
 | 
					          "x-go-name": "BlockOnRejectedReviews"
 | 
				
			||||||
@@ -10392,6 +10396,10 @@
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
					          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "block_on_outdated_branch": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "BlockOnOutdatedBranch"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "block_on_rejected_reviews": {
 | 
					        "block_on_rejected_reviews": {
 | 
				
			||||||
          "type": "boolean",
 | 
					          "type": "boolean",
 | 
				
			||||||
          "x-go-name": "BlockOnRejectedReviews"
 | 
					          "x-go-name": "BlockOnRejectedReviews"
 | 
				
			||||||
@@ -11204,6 +11212,10 @@
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
					          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "block_on_outdated_branch": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "BlockOnOutdatedBranch"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "block_on_rejected_reviews": {
 | 
					        "block_on_rejected_reviews": {
 | 
				
			||||||
          "type": "boolean",
 | 
					          "type": "boolean",
 | 
				
			||||||
          "x-go-name": "BlockOnRejectedReviews"
 | 
					          "x-go-name": "BlockOnRejectedReviews"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user