mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add API branch protection endpoint (#9311)
* add API branch protection endpoint * lint * Change to use team names instead of ids. * Status codes. * fix * Fix * Add new branch protection options (BlockOnRejectedReviews, DismissStaleApprovals, RequireSignedCommits) * Do xorm query directly * fix xorm GetUserNamesByIDs * Add some tests * Improved GetTeamNamesByID * http status created for CreateBranchProtection * Correct status code in integration test Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		@@ -30,6 +30,54 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) {
 | 
			
		||||
	assert.EqualValues(t, branchName, branch.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) {
 | 
			
		||||
	session := loginUser(t, "user2")
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token)
 | 
			
		||||
	resp := session.MakeRequest(t, req, expectedHTTPStatus)
 | 
			
		||||
 | 
			
		||||
	if resp.Code == 200 {
 | 
			
		||||
		var branchProtection api.BranchProtection
 | 
			
		||||
		DecodeJSON(t, resp, &branchProtection)
 | 
			
		||||
		assert.EqualValues(t, branchName, branchProtection.BranchName)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) {
 | 
			
		||||
	session := loginUser(t, "user2")
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{
 | 
			
		||||
		BranchName: branchName,
 | 
			
		||||
	})
 | 
			
		||||
	resp := session.MakeRequest(t, req, expectedHTTPStatus)
 | 
			
		||||
 | 
			
		||||
	if resp.Code == 201 {
 | 
			
		||||
		var branchProtection api.BranchProtection
 | 
			
		||||
		DecodeJSON(t, resp, &branchProtection)
 | 
			
		||||
		assert.EqualValues(t, branchName, branchProtection.BranchName)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) {
 | 
			
		||||
	session := loginUser(t, "user2")
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
	req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body)
 | 
			
		||||
	resp := session.MakeRequest(t, req, expectedHTTPStatus)
 | 
			
		||||
 | 
			
		||||
	if resp.Code == 200 {
 | 
			
		||||
		var branchProtection api.BranchProtection
 | 
			
		||||
		DecodeJSON(t, resp, &branchProtection)
 | 
			
		||||
		assert.EqualValues(t, branchName, branchProtection.BranchName)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) {
 | 
			
		||||
	session := loginUser(t, "user2")
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
	req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token)
 | 
			
		||||
	session.MakeRequest(t, req, expectedHTTPStatus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAPIGetBranch(t *testing.T) {
 | 
			
		||||
	for _, test := range []struct {
 | 
			
		||||
		BranchName string
 | 
			
		||||
@@ -43,3 +91,23 @@ func TestAPIGetBranch(t *testing.T) {
 | 
			
		||||
		testAPIGetBranch(t, test.BranchName, test.Exists)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAPIBranchProtection(t *testing.T) {
 | 
			
		||||
	defer prepareTestEnv(t)()
 | 
			
		||||
 | 
			
		||||
	// Branch protection only on branch that exist
 | 
			
		||||
	testAPICreateBranchProtection(t, "master/doesnotexist", http.StatusNotFound)
 | 
			
		||||
	// Get branch protection on branch that exist but not branch protection
 | 
			
		||||
	testAPIGetBranchProtection(t, "master", http.StatusNotFound)
 | 
			
		||||
 | 
			
		||||
	testAPICreateBranchProtection(t, "master", http.StatusCreated)
 | 
			
		||||
	// Can only create once
 | 
			
		||||
	testAPICreateBranchProtection(t, "master", http.StatusForbidden)
 | 
			
		||||
 | 
			
		||||
	testAPIGetBranchProtection(t, "master", http.StatusOK)
 | 
			
		||||
	testAPIEditBranchProtection(t, "master", &api.BranchProtection{
 | 
			
		||||
		EnablePush: true,
 | 
			
		||||
	}, http.StatusOK)
 | 
			
		||||
 | 
			
		||||
	testAPIDeleteBranchProtection(t, "master", http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -553,6 +553,23 @@ func GetTeam(orgID int64, name string) (*Team, error) {
 | 
			
		||||
	return getTeam(x, orgID, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTeamIDsByNames returns a slice of team ids corresponds to names.
 | 
			
		||||
func GetTeamIDsByNames(orgID int64, names []string, ignoreNonExistent bool) ([]int64, error) {
 | 
			
		||||
	ids := make([]int64, 0, len(names))
 | 
			
		||||
	for _, name := range names {
 | 
			
		||||
		u, err := GetTeam(orgID, name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if ignoreNonExistent {
 | 
			
		||||
				continue
 | 
			
		||||
			} else {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ids = append(ids, u.ID)
 | 
			
		||||
	}
 | 
			
		||||
	return ids, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getOwnerTeam returns team by given team name and organization.
 | 
			
		||||
func getOwnerTeam(e Engine, orgID int64) (*Team, error) {
 | 
			
		||||
	return getTeam(e, orgID, ownerTeamName)
 | 
			
		||||
@@ -574,6 +591,22 @@ func GetTeamByID(teamID int64) (*Team, error) {
 | 
			
		||||
	return getTeamByID(x, teamID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTeamNamesByID returns team's lower name from a list of team ids.
 | 
			
		||||
func GetTeamNamesByID(teamIDs []int64) ([]string, error) {
 | 
			
		||||
	if len(teamIDs) == 0 {
 | 
			
		||||
		return []string{}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var teamNames []string
 | 
			
		||||
	err := x.Table("team").
 | 
			
		||||
		Select("lower_name").
 | 
			
		||||
		In("id", teamIDs).
 | 
			
		||||
		Asc("name").
 | 
			
		||||
		Find(&teamNames)
 | 
			
		||||
 | 
			
		||||
	return teamNames, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateTeam updates information of team.
 | 
			
		||||
func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) {
 | 
			
		||||
	if len(t.Name) == 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -1386,6 +1386,17 @@ func GetMaileableUsersByIDs(ids []int64) ([]*User, error) {
 | 
			
		||||
		Find(&ous)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids.
 | 
			
		||||
func GetUserNamesByIDs(ids []int64) ([]string, error) {
 | 
			
		||||
	unames := make([]string, 0, len(ids))
 | 
			
		||||
	err := x.In("id", ids).
 | 
			
		||||
		Table("user").
 | 
			
		||||
		Asc("name").
 | 
			
		||||
		Cols("name").
 | 
			
		||||
		Find(&unames)
 | 
			
		||||
	return unames, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUsersByIDs returns all resolved users from a list of Ids.
 | 
			
		||||
func GetUsersByIDs(ids []int64) ([]*User, error) {
 | 
			
		||||
	ous := make([]*User, 0, len(ids))
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ func ToEmail(email *models.EmailAddress) *api.Email {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
 | 
			
		||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User) *api.Branch {
 | 
			
		||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User, isRepoAdmin bool) *api.Branch {
 | 
			
		||||
	if bp == nil {
 | 
			
		||||
		return &api.Branch{
 | 
			
		||||
			Name:                          b.Name,
 | 
			
		||||
@@ -41,8 +41,14 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
 | 
			
		||||
			StatusCheckContexts:           []string{},
 | 
			
		||||
			UserCanPush:                   true,
 | 
			
		||||
			UserCanMerge:                  true,
 | 
			
		||||
			EffectiveBranchProtectionName: "",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	branchProtectionName := ""
 | 
			
		||||
	if isRepoAdmin {
 | 
			
		||||
		branchProtectionName = bp.BranchName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &api.Branch{
 | 
			
		||||
		Name:                          b.Name,
 | 
			
		||||
		Commit:                        ToCommit(repo, c),
 | 
			
		||||
@@ -52,6 +58,58 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
 | 
			
		||||
		StatusCheckContexts:           bp.StatusCheckContexts,
 | 
			
		||||
		UserCanPush:                   bp.CanUserPush(user.ID),
 | 
			
		||||
		UserCanMerge:                  bp.IsUserMergeWhitelisted(user.ID),
 | 
			
		||||
		EffectiveBranchProtectionName: branchProtectionName,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToBranchProtection convert a ProtectedBranch to api.BranchProtection
 | 
			
		||||
func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
 | 
			
		||||
	pushWhitelistUsernames, err := models.GetUserNamesByIDs(bp.WhitelistUserIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	mergeWhitelistUsernames, err := models.GetUserNamesByIDs(bp.MergeWhitelistUserIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	approvalsWhitelistUsernames, err := models.GetUserNamesByIDs(bp.ApprovalsWhitelistUserIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	pushWhitelistTeams, err := models.GetTeamNamesByID(bp.WhitelistTeamIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	mergeWhitelistTeams, err := models.GetTeamNamesByID(bp.MergeWhitelistTeamIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	approvalsWhitelistTeams, err := models.GetTeamNamesByID(bp.ApprovalsWhitelistTeamIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &api.BranchProtection{
 | 
			
		||||
		BranchName:                  bp.BranchName,
 | 
			
		||||
		EnablePush:                  bp.CanPush,
 | 
			
		||||
		EnablePushWhitelist:         bp.EnableWhitelist,
 | 
			
		||||
		PushWhitelistUsernames:      pushWhitelistUsernames,
 | 
			
		||||
		PushWhitelistTeams:          pushWhitelistTeams,
 | 
			
		||||
		PushWhitelistDeployKeys:     bp.WhitelistDeployKeys,
 | 
			
		||||
		EnableMergeWhitelist:        bp.EnableMergeWhitelist,
 | 
			
		||||
		MergeWhitelistUsernames:     mergeWhitelistUsernames,
 | 
			
		||||
		MergeWhitelistTeams:         mergeWhitelistTeams,
 | 
			
		||||
		EnableStatusCheck:           bp.EnableStatusCheck,
 | 
			
		||||
		StatusCheckContexts:         bp.StatusCheckContexts,
 | 
			
		||||
		RequiredApprovals:           bp.RequiredApprovals,
 | 
			
		||||
		EnableApprovalsWhitelist:    bp.EnableApprovalsWhitelist,
 | 
			
		||||
		ApprovalsWhitelistUsernames: approvalsWhitelistUsernames,
 | 
			
		||||
		ApprovalsWhitelistTeams:     approvalsWhitelistTeams,
 | 
			
		||||
		BlockOnRejectedReviews:      bp.BlockOnRejectedReviews,
 | 
			
		||||
		DismissStaleApprovals:       bp.DismissStaleApprovals,
 | 
			
		||||
		RequireSignedCommits:        bp.RequireSignedCommits,
 | 
			
		||||
		Created:                     bp.CreatedUnix.AsTime(),
 | 
			
		||||
		Updated:                     bp.UpdatedUnix.AsTime(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,10 @@
 | 
			
		||||
 | 
			
		||||
package structs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Branch represents a repository branch
 | 
			
		||||
type Branch struct {
 | 
			
		||||
	Name                          string         `json:"name"`
 | 
			
		||||
@@ -14,4 +18,74 @@ type Branch struct {
 | 
			
		||||
	StatusCheckContexts           []string       `json:"status_check_contexts"`
 | 
			
		||||
	UserCanPush                   bool           `json:"user_can_push"`
 | 
			
		||||
	UserCanMerge                  bool           `json:"user_can_merge"`
 | 
			
		||||
	EffectiveBranchProtectionName string         `json:"effective_branch_protection_name"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BranchProtection represents a branch protection for a repository
 | 
			
		||||
type BranchProtection struct {
 | 
			
		||||
	BranchName                  string   `json:"branch_name"`
 | 
			
		||||
	EnablePush                  bool     `json:"enable_push"`
 | 
			
		||||
	EnablePushWhitelist         bool     `json:"enable_push_whitelist"`
 | 
			
		||||
	PushWhitelistUsernames      []string `json:"push_whitelist_usernames"`
 | 
			
		||||
	PushWhitelistTeams          []string `json:"push_whitelist_teams"`
 | 
			
		||||
	PushWhitelistDeployKeys     bool     `json:"push_whitelist_deploy_keys"`
 | 
			
		||||
	EnableMergeWhitelist        bool     `json:"enable_merge_whitelist"`
 | 
			
		||||
	MergeWhitelistUsernames     []string `json:"merge_whitelist_usernames"`
 | 
			
		||||
	MergeWhitelistTeams         []string `json:"merge_whitelist_teams"`
 | 
			
		||||
	EnableStatusCheck           bool     `json:"enable_status_check"`
 | 
			
		||||
	StatusCheckContexts         []string `json:"status_check_contexts"`
 | 
			
		||||
	RequiredApprovals           int64    `json:"required_approvals"`
 | 
			
		||||
	EnableApprovalsWhitelist    bool     `json:"enable_approvals_whitelist"`
 | 
			
		||||
	ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
			
		||||
	ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
			
		||||
	BlockOnRejectedReviews      bool     `json:"block_on_rejected_reviews"`
 | 
			
		||||
	DismissStaleApprovals       bool     `json:"dismiss_stale_approvals"`
 | 
			
		||||
	RequireSignedCommits        bool     `json:"require_signed_commits"`
 | 
			
		||||
	// swagger:strfmt date-time
 | 
			
		||||
	Created time.Time `json:"created_at"`
 | 
			
		||||
	// swagger:strfmt date-time
 | 
			
		||||
	Updated time.Time `json:"updated_at"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateBranchProtectionOption options for creating a branch protection
 | 
			
		||||
type CreateBranchProtectionOption struct {
 | 
			
		||||
	BranchName                  string   `json:"branch_name"`
 | 
			
		||||
	EnablePush                  bool     `json:"enable_push"`
 | 
			
		||||
	EnablePushWhitelist         bool     `json:"enable_push_whitelist"`
 | 
			
		||||
	PushWhitelistUsernames      []string `json:"push_whitelist_usernames"`
 | 
			
		||||
	PushWhitelistTeams          []string `json:"push_whitelist_teams"`
 | 
			
		||||
	PushWhitelistDeployKeys     bool     `json:"push_whitelist_deploy_keys"`
 | 
			
		||||
	EnableMergeWhitelist        bool     `json:"enable_merge_whitelist"`
 | 
			
		||||
	MergeWhitelistUsernames     []string `json:"merge_whitelist_usernames"`
 | 
			
		||||
	MergeWhitelistTeams         []string `json:"merge_whitelist_teams"`
 | 
			
		||||
	EnableStatusCheck           bool     `json:"enable_status_check"`
 | 
			
		||||
	StatusCheckContexts         []string `json:"status_check_contexts"`
 | 
			
		||||
	RequiredApprovals           int64    `json:"required_approvals"`
 | 
			
		||||
	EnableApprovalsWhitelist    bool     `json:"enable_approvals_whitelist"`
 | 
			
		||||
	ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
			
		||||
	ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
			
		||||
	BlockOnRejectedReviews      bool     `json:"block_on_rejected_reviews"`
 | 
			
		||||
	DismissStaleApprovals       bool     `json:"dismiss_stale_approvals"`
 | 
			
		||||
	RequireSignedCommits        bool     `json:"require_signed_commits"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EditBranchProtectionOption options for editing a branch protection
 | 
			
		||||
type EditBranchProtectionOption struct {
 | 
			
		||||
	EnablePush                  *bool    `json:"enable_push"`
 | 
			
		||||
	EnablePushWhitelist         *bool    `json:"enable_push_whitelist"`
 | 
			
		||||
	PushWhitelistUsernames      []string `json:"push_whitelist_usernames"`
 | 
			
		||||
	PushWhitelistTeams          []string `json:"push_whitelist_teams"`
 | 
			
		||||
	PushWhitelistDeployKeys     *bool    `json:"push_whitelist_deploy_keys"`
 | 
			
		||||
	EnableMergeWhitelist        *bool    `json:"enable_merge_whitelist"`
 | 
			
		||||
	MergeWhitelistUsernames     []string `json:"merge_whitelist_usernames"`
 | 
			
		||||
	MergeWhitelistTeams         []string `json:"merge_whitelist_teams"`
 | 
			
		||||
	EnableStatusCheck           *bool    `json:"enable_status_check"`
 | 
			
		||||
	StatusCheckContexts         []string `json:"status_check_contexts"`
 | 
			
		||||
	RequiredApprovals           *int64   `json:"required_approvals"`
 | 
			
		||||
	EnableApprovalsWhitelist    *bool    `json:"enable_approvals_whitelist"`
 | 
			
		||||
	ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
 | 
			
		||||
	ApprovalsWhitelistTeams     []string `json:"approvals_whitelist_teams"`
 | 
			
		||||
	BlockOnRejectedReviews      *bool    `json:"block_on_rejected_reviews"`
 | 
			
		||||
	DismissStaleApprovals       *bool    `json:"dismiss_stale_approvals"`
 | 
			
		||||
	RequireSignedCommits        *bool    `json:"require_signed_commits"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -656,6 +656,15 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
					m.Get("", repo.ListBranches)
 | 
			
		||||
					m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch)
 | 
			
		||||
				}, reqRepoReader(models.UnitTypeCode))
 | 
			
		||||
				m.Group("/branch_protections", func() {
 | 
			
		||||
					m.Get("", repo.ListBranchProtections)
 | 
			
		||||
					m.Post("", bind(api.CreateBranchProtectionOption{}), repo.CreateBranchProtection)
 | 
			
		||||
					m.Group("/:name", func() {
 | 
			
		||||
						m.Get("", repo.GetBranchProtection)
 | 
			
		||||
						m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection)
 | 
			
		||||
						m.Delete("", repo.DeleteBranchProtection)
 | 
			
		||||
					})
 | 
			
		||||
				}, reqToken(), reqAdmin())
 | 
			
		||||
				m.Group("/tags", func() {
 | 
			
		||||
					m.Get("", repo.ListTags)
 | 
			
		||||
				}, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true))
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ package repo
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
	"code.gitea.io/gitea/modules/convert"
 | 
			
		||||
	"code.gitea.io/gitea/modules/git"
 | 
			
		||||
@@ -71,7 +72,7 @@ func GetBranch(ctx *context.APIContext) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User))
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User, ctx.Repo.IsAdmin()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListBranches list all the branches of a repository
 | 
			
		||||
@@ -114,8 +115,509 @@ func ListBranches(ctx *context.APIContext) {
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		apiBranches[i] = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User)
 | 
			
		||||
		apiBranches[i] = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, &apiBranches)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetBranchProtection gets a branch protection
 | 
			
		||||
func GetBranchProtection(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Get a specific branch protection for the repository
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: owner
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: owner of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: repo
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: name
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of protected branch
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/BranchProtection"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	bpName := ctx.Params(":name")
 | 
			
		||||
	bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if bp == nil || bp.RepoID != repo.ID {
 | 
			
		||||
		ctx.NotFound()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToBranchProtection(bp))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListBranchProtections list branch protections for a repo
 | 
			
		||||
func ListBranchProtections(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation GET /repos/{owner}/{repo}/branch_protections repository repoListBranchProtection
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: List branch protections for a repository
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: owner
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: owner of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: repo
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/BranchProtectionList"
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	bps, err := repo.GetProtectedBranches()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectedBranches", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	apiBps := make([]*api.BranchProtection, len(bps))
 | 
			
		||||
	for i := range bps {
 | 
			
		||||
		apiBps[i] = convert.ToBranchProtection(bps[i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, apiBps)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateBranchProtection creates a branch protection for a repo
 | 
			
		||||
func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) {
 | 
			
		||||
	// swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Create a branch protections for a repository
 | 
			
		||||
	// consumes:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: owner
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: owner of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: repo
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: body
 | 
			
		||||
	//   in: body
 | 
			
		||||
	//   schema:
 | 
			
		||||
	//     "$ref": "#/definitions/CreateBranchProtectionOption"
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "201":
 | 
			
		||||
	//     "$ref": "#/responses/BranchProtection"
 | 
			
		||||
	//   "403":
 | 
			
		||||
	//     "$ref": "#/responses/forbidden"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
	//   "422":
 | 
			
		||||
	//     "$ref": "#/responses/validationError"
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
 | 
			
		||||
	// Currently protection must match an actual branch
 | 
			
		||||
	if !git.IsBranchExist(ctx.Repo.Repository.RepoPath(), form.BranchName) {
 | 
			
		||||
		ctx.NotFound()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protectBranch, err := models.GetProtectedBranchBy(repo.ID, form.BranchName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectBranchOfRepoByName", err)
 | 
			
		||||
		return
 | 
			
		||||
	} else if protectBranch != nil {
 | 
			
		||||
		ctx.Error(http.StatusForbidden, "Create branch protection", "Branch protection already exist")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var requiredApprovals int64
 | 
			
		||||
	if form.RequiredApprovals > 0 {
 | 
			
		||||
		requiredApprovals = form.RequiredApprovals
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	whitelistUsers, err := models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if models.IsErrUserNotExist(err) {
 | 
			
		||||
			ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	mergeWhitelistUsers, err := models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if models.IsErrUserNotExist(err) {
 | 
			
		||||
			ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	approvalsWhitelistUsers, err := models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if models.IsErrUserNotExist(err) {
 | 
			
		||||
			ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
 | 
			
		||||
	if repo.Owner.IsOrganization() {
 | 
			
		||||
		whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrTeamNotExist(err) {
 | 
			
		||||
				ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrTeamNotExist(err) {
 | 
			
		||||
				ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrTeamNotExist(err) {
 | 
			
		||||
				ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protectBranch = &models.ProtectedBranch{
 | 
			
		||||
		RepoID:                   ctx.Repo.Repository.ID,
 | 
			
		||||
		BranchName:               form.BranchName,
 | 
			
		||||
		CanPush:                  form.EnablePush,
 | 
			
		||||
		EnableWhitelist:          form.EnablePush && form.EnablePushWhitelist,
 | 
			
		||||
		EnableMergeWhitelist:     form.EnableMergeWhitelist,
 | 
			
		||||
		WhitelistDeployKeys:      form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys,
 | 
			
		||||
		EnableStatusCheck:        form.EnableStatusCheck,
 | 
			
		||||
		StatusCheckContexts:      form.StatusCheckContexts,
 | 
			
		||||
		EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
 | 
			
		||||
		RequiredApprovals:        requiredApprovals,
 | 
			
		||||
		BlockOnRejectedReviews:   form.BlockOnRejectedReviews,
 | 
			
		||||
		DismissStaleApprovals:    form.DismissStaleApprovals,
 | 
			
		||||
		RequireSignedCommits:     form.RequireSignedCommits,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
 | 
			
		||||
		UserIDs:          whitelistUsers,
 | 
			
		||||
		TeamIDs:          whitelistTeams,
 | 
			
		||||
		MergeUserIDs:     mergeWhitelistUsers,
 | 
			
		||||
		MergeTeamIDs:     mergeWhitelistTeams,
 | 
			
		||||
		ApprovalsUserIDs: approvalsWhitelistUsers,
 | 
			
		||||
		ApprovalsTeamIDs: approvalsWhitelistTeams,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reload from db to get all whitelists
 | 
			
		||||
	bp, err := models.GetProtectedBranchBy(ctx.Repo.Repository.ID, form.BranchName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EditBranchProtection edits a branch protection for a repo
 | 
			
		||||
func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) {
 | 
			
		||||
	// swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Edit a branch protections for a repository. Only fields that are set will be changed
 | 
			
		||||
	// consumes:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: owner
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: owner of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: repo
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: name
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of protected branch
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: body
 | 
			
		||||
	//   in: body
 | 
			
		||||
	//   schema:
 | 
			
		||||
	//     "$ref": "#/definitions/EditBranchProtectionOption"
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/BranchProtection"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
	//   "422":
 | 
			
		||||
	//     "$ref": "#/responses/validationError"
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	bpName := ctx.Params(":name")
 | 
			
		||||
	protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if protectBranch == nil || protectBranch.RepoID != repo.ID {
 | 
			
		||||
		ctx.NotFound()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.EnablePush != nil {
 | 
			
		||||
		if !*form.EnablePush {
 | 
			
		||||
			protectBranch.CanPush = false
 | 
			
		||||
			protectBranch.EnableWhitelist = false
 | 
			
		||||
			protectBranch.WhitelistDeployKeys = false
 | 
			
		||||
		} else {
 | 
			
		||||
			protectBranch.CanPush = true
 | 
			
		||||
			if form.EnablePushWhitelist != nil {
 | 
			
		||||
				if !*form.EnablePushWhitelist {
 | 
			
		||||
					protectBranch.EnableWhitelist = false
 | 
			
		||||
					protectBranch.WhitelistDeployKeys = false
 | 
			
		||||
				} else {
 | 
			
		||||
					protectBranch.EnableWhitelist = true
 | 
			
		||||
					if form.PushWhitelistDeployKeys != nil {
 | 
			
		||||
						protectBranch.WhitelistDeployKeys = *form.PushWhitelistDeployKeys
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.EnableMergeWhitelist != nil {
 | 
			
		||||
		protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.EnableStatusCheck != nil {
 | 
			
		||||
		protectBranch.EnableStatusCheck = *form.EnableStatusCheck
 | 
			
		||||
	}
 | 
			
		||||
	if protectBranch.EnableStatusCheck {
 | 
			
		||||
		protectBranch.StatusCheckContexts = form.StatusCheckContexts
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.RequiredApprovals != nil && *form.RequiredApprovals >= 0 {
 | 
			
		||||
		protectBranch.RequiredApprovals = *form.RequiredApprovals
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.EnableApprovalsWhitelist != nil {
 | 
			
		||||
		protectBranch.EnableApprovalsWhitelist = *form.EnableApprovalsWhitelist
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.BlockOnRejectedReviews != nil {
 | 
			
		||||
		protectBranch.BlockOnRejectedReviews = *form.BlockOnRejectedReviews
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.DismissStaleApprovals != nil {
 | 
			
		||||
		protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if form.RequireSignedCommits != nil {
 | 
			
		||||
		protectBranch.RequireSignedCommits = *form.RequireSignedCommits
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var whitelistUsers []int64
 | 
			
		||||
	if form.PushWhitelistUsernames != nil {
 | 
			
		||||
		whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrUserNotExist(err) {
 | 
			
		||||
				ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		whitelistUsers = protectBranch.WhitelistUserIDs
 | 
			
		||||
	}
 | 
			
		||||
	var mergeWhitelistUsers []int64
 | 
			
		||||
	if form.MergeWhitelistUsernames != nil {
 | 
			
		||||
		mergeWhitelistUsers, err = models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrUserNotExist(err) {
 | 
			
		||||
				ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs
 | 
			
		||||
	}
 | 
			
		||||
	var approvalsWhitelistUsers []int64
 | 
			
		||||
	if form.ApprovalsWhitelistUsernames != nil {
 | 
			
		||||
		approvalsWhitelistUsers, err = models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrUserNotExist(err) {
 | 
			
		||||
				ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
 | 
			
		||||
	if repo.Owner.IsOrganization() {
 | 
			
		||||
		if form.PushWhitelistTeams != nil {
 | 
			
		||||
			whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if models.IsErrTeamNotExist(err) {
 | 
			
		||||
					ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			whitelistTeams = protectBranch.WhitelistTeamIDs
 | 
			
		||||
		}
 | 
			
		||||
		if form.MergeWhitelistTeams != nil {
 | 
			
		||||
			mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if models.IsErrTeamNotExist(err) {
 | 
			
		||||
					ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			mergeWhitelistTeams = protectBranch.MergeWhitelistTeamIDs
 | 
			
		||||
		}
 | 
			
		||||
		if form.ApprovalsWhitelistTeams != nil {
 | 
			
		||||
			approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if models.IsErrTeamNotExist(err) {
 | 
			
		||||
					ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			approvalsWhitelistTeams = protectBranch.ApprovalsWhitelistTeamIDs
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
 | 
			
		||||
		UserIDs:          whitelistUsers,
 | 
			
		||||
		TeamIDs:          whitelistTeams,
 | 
			
		||||
		MergeUserIDs:     mergeWhitelistUsers,
 | 
			
		||||
		MergeTeamIDs:     mergeWhitelistTeams,
 | 
			
		||||
		ApprovalsUserIDs: approvalsWhitelistUsers,
 | 
			
		||||
		ApprovalsTeamIDs: approvalsWhitelistTeams,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reload from db to ensure get all whitelists
 | 
			
		||||
	bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToBranchProtection(bp))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteBranchProtection deletes a branch protection for a repo
 | 
			
		||||
func DeleteBranchProtection(ctx *context.APIContext) {
 | 
			
		||||
	// swagger:operation DELETE /repos/{owner}/{repo}/branch_protections/{name} repository repoDeleteBranchProtection
 | 
			
		||||
	// ---
 | 
			
		||||
	// summary: Delete a specific branch protection for the repository
 | 
			
		||||
	// produces:
 | 
			
		||||
	// - application/json
 | 
			
		||||
	// parameters:
 | 
			
		||||
	// - name: owner
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: owner of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: repo
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of the repo
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: name
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: name of protected branch
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "204":
 | 
			
		||||
	//     "$ref": "#/responses/empty"
 | 
			
		||||
	//   "404":
 | 
			
		||||
	//     "$ref": "#/responses/notFound"
 | 
			
		||||
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	bpName := ctx.Params(":name")
 | 
			
		||||
	bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if bp == nil || bp.RepoID != repo.ID {
 | 
			
		||||
		ctx.NotFound()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ctx.Repo.Repository.DeleteProtectedBranch(bp.ID); err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "DeleteProtectedBranch", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Status(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,10 @@ type swaggerParameterBodies struct {
 | 
			
		||||
 | 
			
		||||
	// in:body
 | 
			
		||||
	EditReactionOption api.EditReactionOption
 | 
			
		||||
 | 
			
		||||
	// in:body
 | 
			
		||||
	CreateBranchProtectionOption api.CreateBranchProtectionOption
 | 
			
		||||
 | 
			
		||||
	// in:body
 | 
			
		||||
	EditBranchProtectionOption api.EditBranchProtectionOption
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,20 @@ type swaggerResponseBranchList struct {
 | 
			
		||||
	Body []api.Branch `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BranchProtection
 | 
			
		||||
// swagger:response BranchProtection
 | 
			
		||||
type swaggerResponseBranchProtection struct {
 | 
			
		||||
	// in:body
 | 
			
		||||
	Body api.BranchProtection `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BranchProtectionList
 | 
			
		||||
// swagger:response BranchProtectionList
 | 
			
		||||
type swaggerResponseBranchProtectionList struct {
 | 
			
		||||
	// in:body
 | 
			
		||||
	Body []api.BranchProtection `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TagList
 | 
			
		||||
// swagger:response TagList
 | 
			
		||||
type swaggerResponseTagList struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -1797,6 +1797,227 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/repos/{owner}/{repo}/branch_protections": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "repository"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "List branch protections for a repository",
 | 
			
		||||
        "operationId": "repoListBranchProtection",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "owner of the repo",
 | 
			
		||||
            "name": "owner",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the repo",
 | 
			
		||||
            "name": "repo",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "$ref": "#/responses/BranchProtectionList"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "post": {
 | 
			
		||||
        "consumes": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "repository"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Create a branch protections for a repository",
 | 
			
		||||
        "operationId": "repoCreateBranchProtection",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "owner of the repo",
 | 
			
		||||
            "name": "owner",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the repo",
 | 
			
		||||
            "name": "repo",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "name": "body",
 | 
			
		||||
            "in": "body",
 | 
			
		||||
            "schema": {
 | 
			
		||||
              "$ref": "#/definitions/CreateBranchProtectionOption"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "201": {
 | 
			
		||||
            "$ref": "#/responses/BranchProtection"
 | 
			
		||||
          },
 | 
			
		||||
          "403": {
 | 
			
		||||
            "$ref": "#/responses/forbidden"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          },
 | 
			
		||||
          "422": {
 | 
			
		||||
            "$ref": "#/responses/validationError"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/repos/{owner}/{repo}/branch_protections/{name}": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "repository"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Get a specific branch protection for the repository",
 | 
			
		||||
        "operationId": "repoGetBranchProtection",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "owner of the repo",
 | 
			
		||||
            "name": "owner",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the repo",
 | 
			
		||||
            "name": "repo",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of protected branch",
 | 
			
		||||
            "name": "name",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "$ref": "#/responses/BranchProtection"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "delete": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "repository"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Delete a specific branch protection for the repository",
 | 
			
		||||
        "operationId": "repoDeleteBranchProtection",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "owner of the repo",
 | 
			
		||||
            "name": "owner",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the repo",
 | 
			
		||||
            "name": "repo",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of protected branch",
 | 
			
		||||
            "name": "name",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "204": {
 | 
			
		||||
            "$ref": "#/responses/empty"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "patch": {
 | 
			
		||||
        "consumes": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "produces": [
 | 
			
		||||
          "application/json"
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "repository"
 | 
			
		||||
        ],
 | 
			
		||||
        "summary": "Edit a branch protections for a repository. Only fields that are set will be changed",
 | 
			
		||||
        "operationId": "repoEditBranchProtection",
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "owner of the repo",
 | 
			
		||||
            "name": "owner",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of the repo",
 | 
			
		||||
            "name": "repo",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "name of protected branch",
 | 
			
		||||
            "name": "name",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "name": "body",
 | 
			
		||||
            "in": "body",
 | 
			
		||||
            "schema": {
 | 
			
		||||
              "$ref": "#/definitions/EditBranchProtectionOption"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "$ref": "#/responses/BranchProtection"
 | 
			
		||||
          },
 | 
			
		||||
          "404": {
 | 
			
		||||
            "$ref": "#/responses/notFound"
 | 
			
		||||
          },
 | 
			
		||||
          "422": {
 | 
			
		||||
            "$ref": "#/responses/validationError"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/repos/{owner}/{repo}/branches": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "produces": [
 | 
			
		||||
@@ -9394,6 +9615,10 @@
 | 
			
		||||
        "commit": {
 | 
			
		||||
          "$ref": "#/definitions/PayloadCommit"
 | 
			
		||||
        },
 | 
			
		||||
        "effective_branch_protection_name": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "x-go-name": "EffectiveBranchProtectionName"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_status_check": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableStatusCheck"
 | 
			
		||||
@@ -9429,6 +9654,117 @@
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "BranchProtection": {
 | 
			
		||||
      "description": "BranchProtection represents a branch protection for a repository",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "properties": {
 | 
			
		||||
        "approvals_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "ApprovalsWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "approvals_whitelist_username": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "block_on_rejected_reviews": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "BlockOnRejectedReviews"
 | 
			
		||||
        },
 | 
			
		||||
        "branch_name": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "x-go-name": "BranchName"
 | 
			
		||||
        },
 | 
			
		||||
        "created_at": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "format": "date-time",
 | 
			
		||||
          "x-go-name": "Created"
 | 
			
		||||
        },
 | 
			
		||||
        "dismiss_stale_approvals": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "DismissStaleApprovals"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_approvals_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableApprovalsWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_merge_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableMergeWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_push": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnablePush"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_push_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnablePushWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_status_check": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableStatusCheck"
 | 
			
		||||
        },
 | 
			
		||||
        "merge_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "MergeWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "merge_whitelist_usernames": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "MergeWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_deploy_keys": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "PushWhitelistDeployKeys"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "PushWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_usernames": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "PushWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "require_signed_commits": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "RequireSignedCommits"
 | 
			
		||||
        },
 | 
			
		||||
        "required_approvals": {
 | 
			
		||||
          "type": "integer",
 | 
			
		||||
          "format": "int64",
 | 
			
		||||
          "x-go-name": "RequiredApprovals"
 | 
			
		||||
        },
 | 
			
		||||
        "status_check_contexts": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "StatusCheckContexts"
 | 
			
		||||
        },
 | 
			
		||||
        "updated_at": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "format": "date-time",
 | 
			
		||||
          "x-go-name": "Updated"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "Comment": {
 | 
			
		||||
      "description": "Comment represents a comment on a commit or issue",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
@@ -9634,6 +9970,107 @@
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "CreateBranchProtectionOption": {
 | 
			
		||||
      "description": "CreateBranchProtectionOption options for creating a branch protection",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "properties": {
 | 
			
		||||
        "approvals_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "ApprovalsWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "approvals_whitelist_username": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "block_on_rejected_reviews": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "BlockOnRejectedReviews"
 | 
			
		||||
        },
 | 
			
		||||
        "branch_name": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "x-go-name": "BranchName"
 | 
			
		||||
        },
 | 
			
		||||
        "dismiss_stale_approvals": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "DismissStaleApprovals"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_approvals_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableApprovalsWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_merge_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableMergeWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_push": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnablePush"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_push_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnablePushWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_status_check": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableStatusCheck"
 | 
			
		||||
        },
 | 
			
		||||
        "merge_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "MergeWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "merge_whitelist_usernames": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "MergeWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_deploy_keys": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "PushWhitelistDeployKeys"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "PushWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_usernames": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "PushWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "require_signed_commits": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "RequireSignedCommits"
 | 
			
		||||
        },
 | 
			
		||||
        "required_approvals": {
 | 
			
		||||
          "type": "integer",
 | 
			
		||||
          "format": "int64",
 | 
			
		||||
          "x-go-name": "RequiredApprovals"
 | 
			
		||||
        },
 | 
			
		||||
        "status_check_contexts": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "StatusCheckContexts"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "CreateEmailOption": {
 | 
			
		||||
      "description": "CreateEmailOption options when creating email addresses",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
@@ -10318,6 +10755,103 @@
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "EditBranchProtectionOption": {
 | 
			
		||||
      "description": "EditBranchProtectionOption options for editing a branch protection",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "properties": {
 | 
			
		||||
        "approvals_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "ApprovalsWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "approvals_whitelist_username": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "ApprovalsWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "block_on_rejected_reviews": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "BlockOnRejectedReviews"
 | 
			
		||||
        },
 | 
			
		||||
        "dismiss_stale_approvals": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "DismissStaleApprovals"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_approvals_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableApprovalsWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_merge_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableMergeWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_push": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnablePush"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_push_whitelist": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnablePushWhitelist"
 | 
			
		||||
        },
 | 
			
		||||
        "enable_status_check": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "EnableStatusCheck"
 | 
			
		||||
        },
 | 
			
		||||
        "merge_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "MergeWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "merge_whitelist_usernames": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "MergeWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_deploy_keys": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "PushWhitelistDeployKeys"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_teams": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "PushWhitelistTeams"
 | 
			
		||||
        },
 | 
			
		||||
        "push_whitelist_usernames": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "PushWhitelistUsernames"
 | 
			
		||||
        },
 | 
			
		||||
        "require_signed_commits": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "x-go-name": "RequireSignedCommits"
 | 
			
		||||
        },
 | 
			
		||||
        "required_approvals": {
 | 
			
		||||
          "type": "integer",
 | 
			
		||||
          "format": "int64",
 | 
			
		||||
          "x-go-name": "RequiredApprovals"
 | 
			
		||||
        },
 | 
			
		||||
        "status_check_contexts": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "string"
 | 
			
		||||
          },
 | 
			
		||||
          "x-go-name": "StatusCheckContexts"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
    },
 | 
			
		||||
    "EditDeadlineOption": {
 | 
			
		||||
      "description": "EditDeadlineOption options for creating a deadline",
 | 
			
		||||
      "type": "object",
 | 
			
		||||
@@ -12880,6 +13414,21 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "BranchProtection": {
 | 
			
		||||
      "description": "BranchProtection",
 | 
			
		||||
      "schema": {
 | 
			
		||||
        "$ref": "#/definitions/BranchProtection"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "BranchProtectionList": {
 | 
			
		||||
      "description": "BranchProtectionList",
 | 
			
		||||
      "schema": {
 | 
			
		||||
        "type": "array",
 | 
			
		||||
        "items": {
 | 
			
		||||
          "$ref": "#/definitions/BranchProtection"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Comment": {
 | 
			
		||||
      "description": "Comment",
 | 
			
		||||
      "schema": {
 | 
			
		||||
@@ -13410,7 +13959,7 @@
 | 
			
		||||
    "parameterBodies": {
 | 
			
		||||
      "description": "parameterBodies",
 | 
			
		||||
      "schema": {
 | 
			
		||||
        "$ref": "#/definitions/EditReactionOption"
 | 
			
		||||
        "$ref": "#/definitions/EditBranchProtectionOption"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "redirect": {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user