feat: org branch protection per-user (username/email) allowlists + actions-bot toggle (#727) #750

Merged
jmiller merged 1 commits from feature/org-branch-protection-user-allowlists into dev 2026-07-05 22:54:51 +00:00
9 changed files with 642 additions and 11 deletions
+21 -2
View File
@@ -28,19 +28,28 @@ type OrgProtectedBranch struct {
CanPush bool `xorm:"NOT NULL DEFAULT false"`
EnableWhitelist bool `xorm:"NOT NULL DEFAULT false"`
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
WhitelistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
MergeWhitelistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
CanForcePush bool `xorm:"NOT NULL DEFAULT false"`
EnableForcePushAllowlist bool `xorm:"NOT NULL DEFAULT false"`
ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
ForcePushAllowlistUserIDs []int64 `xorm:"JSON TEXT"`
ForcePushAllowlistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
CanDelete bool `xorm:"NOT NULL DEFAULT false"`
EnableDeleteAllowlist bool `xorm:"NOT NULL DEFAULT false"`
DeleteAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
DeleteAllowlistUserIDs []int64 `xorm:"JSON TEXT"`
DeleteAllowlistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
StatusCheckContexts []string `xorm:"JSON TEXT"`
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
@@ -84,8 +93,9 @@ func (o *OrgProtectedBranch) Match(branchName string) bool {
}
// ToProtectedBranch converts an org-level rule to a ProtectedBranch for use
// in the standard enforcement path. Fields that are user-scoped (WhitelistUserIDs etc.)
// are left empty because org rules only reference teams.
// in the standard enforcement path. Both team-scoped and user-scoped allowlists
// (plus the Actions-bot toggles) are carried across; deploy-key allowances remain
// repo-only because an org rule cannot express them.
func (o *OrgProtectedBranch) ToProtectedBranch() *ProtectedBranch {
return &ProtectedBranch{
ID: o.ID,
@@ -94,19 +104,28 @@ func (o *OrgProtectedBranch) ToProtectedBranch() *ProtectedBranch {
CanPush: o.CanPush,
EnableWhitelist: o.EnableWhitelist,
WhitelistTeamIDs: o.WhitelistTeamIDs,
WhitelistUserIDs: o.WhitelistUserIDs,
WhitelistActionsUser: o.WhitelistActionsUser,
EnableMergeWhitelist: o.EnableMergeWhitelist,
MergeWhitelistTeamIDs: o.MergeWhitelistTeamIDs,
MergeWhitelistUserIDs: o.MergeWhitelistUserIDs,
MergeWhitelistActionsUser: o.MergeWhitelistActionsUser,
CanForcePush: o.CanForcePush,
EnableForcePushAllowlist: o.EnableForcePushAllowlist,
ForcePushAllowlistTeamIDs: o.ForcePushAllowlistTeamIDs,
ForcePushAllowlistUserIDs: o.ForcePushAllowlistUserIDs,
ForcePushAllowlistActionsUser: o.ForcePushAllowlistActionsUser,
CanDelete: o.CanDelete,
EnableDeleteAllowlist: o.EnableDeleteAllowlist,
DeleteAllowlistTeamIDs: o.DeleteAllowlistTeamIDs,
DeleteAllowlistUserIDs: o.DeleteAllowlistUserIDs,
DeleteAllowlistActionsUser: o.DeleteAllowlistActionsUser,
EnableStatusCheck: o.EnableStatusCheck,
StatusCheckContexts: o.StatusCheckContexts,
RequiredApprovals: o.RequiredApprovals,
EnableApprovalsWhitelist: o.EnableApprovalsWhitelist,
ApprovalsWhitelistTeamIDs: o.ApprovalsWhitelistTeamIDs,
ApprovalsWhitelistUserIDs: o.ApprovalsWhitelistUserIDs,
BlockOnRejectedReviews: o.BlockOnRejectedReviews,
BlockOnOfficialReviewRequests: o.BlockOnOfficialReviewRequests,
BlockOnOutdatedBranch: o.BlockOnOutdatedBranch,
+28 -9
View File
@@ -26,32 +26,32 @@ func mergeMostRestrictive(repoRule, orgRule *ProtectedBranch) *ProtectedBranch {
eff.CanPush = repoRule.CanPush && orgRule.CanPush
eff.EnableWhitelist, eff.WhitelistUserIDs = mergeAllowlist(repoRule.EnableWhitelist, repoRule.WhitelistUserIDs, orgRule.EnableWhitelist, orgRule.WhitelistUserIDs)
_, eff.WhitelistTeamIDs = mergeAllowlist(repoRule.EnableWhitelist, repoRule.WhitelistTeamIDs, orgRule.EnableWhitelist, orgRule.WhitelistTeamIDs)
// Deploy-key and Actions-user allowances are not expressible in an
// OrgProtectedBranch (it is team-only), so the org rule imposes no constraint on
// them; carry the repo values through unchanged. ANDing against the org side's
// always-false zero value would silently lock out every deploy-key and
// Actions-bot push in any org that has a matching branch rule (see #727 review).
// Deploy-key allowances are still not expressible in an OrgProtectedBranch, so the
// org rule imposes no constraint on them; carry the repo value through unchanged.
// ANDing against the org side's always-false zero value would silently lock out
// every deploy-key push in any org that has a matching branch rule (see #727 review).
// The Actions-bot toggle IS now expressible org-side, so merge it most-restrictively.
eff.WhitelistDeployKeys = repoRule.WhitelistDeployKeys
eff.WhitelistActionsUser = repoRule.WhitelistActionsUser
eff.WhitelistActionsUser = mergeAllowFlag(repoRule.EnableWhitelist, repoRule.WhitelistActionsUser, orgRule.EnableWhitelist, orgRule.WhitelistActionsUser)
// Force push.
eff.CanForcePush = repoRule.CanForcePush && orgRule.CanForcePush
eff.EnableForcePushAllowlist, eff.ForcePushAllowlistUserIDs = mergeAllowlist(repoRule.EnableForcePushAllowlist, repoRule.ForcePushAllowlistUserIDs, orgRule.EnableForcePushAllowlist, orgRule.ForcePushAllowlistUserIDs)
_, eff.ForcePushAllowlistTeamIDs = mergeAllowlist(repoRule.EnableForcePushAllowlist, repoRule.ForcePushAllowlistTeamIDs, orgRule.EnableForcePushAllowlist, orgRule.ForcePushAllowlistTeamIDs)
eff.ForcePushAllowlistDeployKeys = repoRule.ForcePushAllowlistDeployKeys
eff.ForcePushAllowlistActionsUser = repoRule.ForcePushAllowlistActionsUser
eff.ForcePushAllowlistActionsUser = mergeAllowFlag(repoRule.EnableForcePushAllowlist, repoRule.ForcePushAllowlistActionsUser, orgRule.EnableForcePushAllowlist, orgRule.ForcePushAllowlistActionsUser)
// Delete.
eff.CanDelete = repoRule.CanDelete && orgRule.CanDelete
eff.EnableDeleteAllowlist, eff.DeleteAllowlistUserIDs = mergeAllowlist(repoRule.EnableDeleteAllowlist, repoRule.DeleteAllowlistUserIDs, orgRule.EnableDeleteAllowlist, orgRule.DeleteAllowlistUserIDs)
_, eff.DeleteAllowlistTeamIDs = mergeAllowlist(repoRule.EnableDeleteAllowlist, repoRule.DeleteAllowlistTeamIDs, orgRule.EnableDeleteAllowlist, orgRule.DeleteAllowlistTeamIDs)
eff.DeleteAllowlistDeployKeys = repoRule.DeleteAllowlistDeployKeys
eff.DeleteAllowlistActionsUser = repoRule.DeleteAllowlistActionsUser
eff.DeleteAllowlistActionsUser = mergeAllowFlag(repoRule.EnableDeleteAllowlist, repoRule.DeleteAllowlistActionsUser, orgRule.EnableDeleteAllowlist, orgRule.DeleteAllowlistActionsUser)
// Merge whitelist.
eff.EnableMergeWhitelist, eff.MergeWhitelistUserIDs = mergeAllowlist(repoRule.EnableMergeWhitelist, repoRule.MergeWhitelistUserIDs, orgRule.EnableMergeWhitelist, orgRule.MergeWhitelistUserIDs)
_, eff.MergeWhitelistTeamIDs = mergeAllowlist(repoRule.EnableMergeWhitelist, repoRule.MergeWhitelistTeamIDs, orgRule.EnableMergeWhitelist, orgRule.MergeWhitelistTeamIDs)
eff.MergeWhitelistActionsUser = repoRule.MergeWhitelistActionsUser
eff.MergeWhitelistActionsUser = mergeAllowFlag(repoRule.EnableMergeWhitelist, repoRule.MergeWhitelistActionsUser, orgRule.EnableMergeWhitelist, orgRule.MergeWhitelistActionsUser)
// Status checks.
eff.EnableStatusCheck = repoRule.EnableStatusCheck || orgRule.EnableStatusCheck
@@ -94,6 +94,25 @@ func mergeAllowlist(aEnabled bool, aIDs []int64, bEnabled bool, bIDs []int64) (b
}
}
// mergeAllowFlag combines two "allow the Actions bot" toggles under most-restrictive
// semantics, mirroring mergeAllowlist. A toggle only grants access when its Enable flag
// is set; a disabled allowlist means "everyone", so it imposes no constraint. Therefore:
// if both allowlists are enabled the bot is allowed only when BOTH sides allow it; if
// only one is enabled that side's flag governs; if neither is enabled the flag is
// irrelevant (fall back to the repo/a value).
func mergeAllowFlag(aEnabled, aFlag, bEnabled, bFlag bool) bool {
switch {
case aEnabled && bEnabled:
return aFlag && bFlag
case aEnabled:
return aFlag
case bEnabled:
return bFlag
default:
return aFlag
}
}
func intersectInt64(a, b []int64) []int64 {
if len(a) == 0 || len(b) == 0 {
return nil
+100
View File
@@ -0,0 +1,100 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package git
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMergeAllowFlag(t *testing.T) {
cases := []struct {
name string
aEnabled bool
aFlag bool
bEnabled bool
bFlag bool
expected bool
}{
// Both allowlists enabled: bot allowed only when BOTH allow it.
{"both enabled, both allow", true, true, true, true, true},
{"both enabled, a denies", true, false, true, true, false},
{"both enabled, b denies", true, true, true, false, false},
{"both enabled, both deny", true, false, true, false, false},
// Only one side enabled: that side governs.
{"only a enabled, a allows", true, true, false, false, true},
{"only a enabled, a denies", true, false, false, true, false},
{"only b enabled, b allows", false, false, true, true, true},
{"only b enabled, b denies", false, true, true, false, false},
// Neither enabled: flag irrelevant, falls back to a's flag.
{"neither enabled, a true", false, true, false, false, true},
{"neither enabled, a false", false, false, false, true, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.expected, mergeAllowFlag(c.aEnabled, c.aFlag, c.bEnabled, c.bFlag))
})
}
}
func TestMergeMostRestrictiveUserIDsIntersect(t *testing.T) {
repoRule := &ProtectedBranch{
EnableWhitelist: true,
WhitelistUserIDs: []int64{1, 2, 3},
// Deploy keys are repo-only and must always pass through unchanged.
WhitelistDeployKeys: true,
ForcePushAllowlistDeployKeys: true,
DeleteAllowlistDeployKeys: true,
}
orgRule := &ProtectedBranch{
EnableWhitelist: true,
WhitelistUserIDs: []int64{2, 3, 4},
// Org rule cannot express deploy keys; these zero values must NOT override repo.
}
eff := mergeMostRestrictive(repoRule, orgRule)
// User IDs are intersected when both allowlists are enabled.
assert.True(t, eff.EnableWhitelist)
assert.ElementsMatch(t, []int64{2, 3}, eff.WhitelistUserIDs)
// Deploy-key allowances still pass through from the repo rule.
assert.True(t, eff.WhitelistDeployKeys)
assert.True(t, eff.ForcePushAllowlistDeployKeys)
assert.True(t, eff.DeleteAllowlistDeployKeys)
}
func TestMergeMostRestrictiveActionsUserFlags(t *testing.T) {
// Both sides enable their allowlist and both allow the bot -> allowed.
repoRule := &ProtectedBranch{
EnableWhitelist: true,
WhitelistActionsUser: true,
EnableForcePushAllowlist: true,
// force-push: org denies -> effective deny.
ForcePushAllowlistActionsUser: true,
EnableDeleteAllowlist: true,
DeleteAllowlistActionsUser: true,
// merge: repo allowlist disabled, org enabled+denies -> org governs (deny).
EnableMergeWhitelist: false,
MergeWhitelistActionsUser: true,
}
orgRule := &ProtectedBranch{
EnableWhitelist: true,
WhitelistActionsUser: true,
EnableForcePushAllowlist: true,
ForcePushAllowlistActionsUser: false,
EnableDeleteAllowlist: true,
DeleteAllowlistActionsUser: true,
EnableMergeWhitelist: true,
MergeWhitelistActionsUser: false,
}
eff := mergeMostRestrictive(repoRule, orgRule)
assert.True(t, eff.WhitelistActionsUser, "push: both allow")
assert.False(t, eff.ForcePushAllowlistActionsUser, "force-push: org denies")
assert.True(t, eff.DeleteAllowlistActionsUser, "delete: both allow")
assert.False(t, eff.MergeWhitelistActionsUser, "merge: org enabled and denies")
}
+1
View File
@@ -444,6 +444,7 @@ func prepareMigrationTasks() []*migration {
newMigration(364, "Add org push policy table", v1_27.AddOrgPushPolicyTable),
newMigration(365, "Add org repo defaults table", v1_27.AddOrgRepoDefaultsTable),
newMigration(366, "Add org email domain policy table", v1_27.AddOrgEmailDomainPolicyTable),
newMigration(367, "Add user allowlists to org protected branch", v1_27.AddUserAllowlistsToOrgProtectedBranch),
}
return preparedMigrations
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package v1_27
import "xorm.io/xorm"
// AddUserAllowlistsToOrgProtectedBranch adds per-user allowlist columns (username or
// email resolved to user IDs) plus the "allow Actions bot" toggles to org-level branch
// protection rules for the push, merge, force-push and delete categories, mirroring the
// per-repo user allowlists so an org rule can also grant access to individual users and
// the Actions bot. See issue #727.
func AddUserAllowlistsToOrgProtectedBranch(x *xorm.Engine) error {
type OrgProtectedBranch struct {
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
WhitelistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
MergeWhitelistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
ForcePushAllowlistUserIDs []int64 `xorm:"JSON TEXT"`
ForcePushAllowlistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
DeleteAllowlistUserIDs []int64 `xorm:"JSON TEXT"`
DeleteAllowlistActionsUser bool `xorm:"NOT NULL DEFAULT false"`
ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
}
return x.Sync(new(OrgProtectedBranch))
}
+27
View File
@@ -14,19 +14,28 @@ type OrgBranchProtection struct {
EnablePush bool `json:"enable_push"`
EnablePushWhitelist bool `json:"enable_push_whitelist"`
PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistActionsUser bool `json:"push_whitelist_actions_user"`
EnableForcePush bool `json:"enable_force_push"`
EnableForcePushAllowlist bool `json:"enable_force_push_allowlist"`
ForcePushAllowlistTeams []string `json:"force_push_allowlist_teams"`
ForcePushAllowlistUsernames []string `json:"force_push_allowlist_usernames"`
ForcePushAllowlistActionsUser bool `json:"force_push_allowlist_actions_user"`
EnableDelete bool `json:"enable_delete"`
EnableDeleteAllowlist bool `json:"enable_delete_allowlist"`
DeleteAllowlistTeams []string `json:"delete_allowlist_teams"`
DeleteAllowlistUsernames []string `json:"delete_allowlist_usernames"`
DeleteAllowlistActionsUser bool `json:"delete_allowlist_actions_user"`
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistActionsUser bool `json:"merge_whitelist_actions_user"`
EnableStatusCheck bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
RequiredApprovals int64 `json:"required_approvals"`
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
@@ -49,19 +58,28 @@ type CreateOrgBranchProtectionOption struct {
EnablePush bool `json:"enable_push"`
EnablePushWhitelist bool `json:"enable_push_whitelist"`
PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistActionsUser bool `json:"push_whitelist_actions_user"`
EnableForcePush bool `json:"enable_force_push"`
EnableForcePushAllowlist bool `json:"enable_force_push_allowlist"`
ForcePushAllowlistTeams []string `json:"force_push_allowlist_teams"`
ForcePushAllowlistUsernames []string `json:"force_push_allowlist_usernames"`
ForcePushAllowlistActionsUser bool `json:"force_push_allowlist_actions_user"`
EnableDelete bool `json:"enable_delete"`
EnableDeleteAllowlist bool `json:"enable_delete_allowlist"`
DeleteAllowlistTeams []string `json:"delete_allowlist_teams"`
DeleteAllowlistUsernames []string `json:"delete_allowlist_usernames"`
DeleteAllowlistActionsUser bool `json:"delete_allowlist_actions_user"`
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistActionsUser bool `json:"merge_whitelist_actions_user"`
EnableStatusCheck bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
RequiredApprovals int64 `json:"required_approvals"`
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
@@ -79,19 +97,28 @@ type EditOrgBranchProtectionOption struct {
EnablePush *bool `json:"enable_push"`
EnablePushWhitelist *bool `json:"enable_push_whitelist"`
PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistActionsUser *bool `json:"push_whitelist_actions_user"`
EnableForcePush *bool `json:"enable_force_push"`
EnableForcePushAllowlist *bool `json:"enable_force_push_allowlist"`
ForcePushAllowlistTeams []string `json:"force_push_allowlist_teams"`
ForcePushAllowlistUsernames []string `json:"force_push_allowlist_usernames"`
ForcePushAllowlistActionsUser *bool `json:"force_push_allowlist_actions_user"`
EnableDelete *bool `json:"enable_delete"`
EnableDeleteAllowlist *bool `json:"enable_delete_allowlist"`
DeleteAllowlistTeams []string `json:"delete_allowlist_teams"`
DeleteAllowlistUsernames []string `json:"delete_allowlist_usernames"`
DeleteAllowlistActionsUser *bool `json:"delete_allowlist_actions_user"`
EnableMergeWhitelist *bool `json:"enable_merge_whitelist"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistActionsUser *bool `json:"merge_whitelist_actions_user"`
EnableStatusCheck *bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
RequiredApprovals *int64 `json:"required_approvals"`
EnableApprovalsWhitelist *bool `json:"enable_approvals_whitelist"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
BlockOnRejectedReviews *bool `json:"block_on_rejected_reviews"`
BlockOnOfficialReviewRequests *bool `json:"block_on_official_review_requests"`
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
+133
View File
@@ -8,6 +8,7 @@ import (
git_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/git"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/organization"
user_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/user"
api "code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/structs"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/web"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
@@ -36,6 +37,18 @@ func toAPIOrgBranchProtection(ctx *context.APIContext, rule *git_model.OrgProtec
return names
}
resolveUserNames := func(ids []int64) []string {
names := make([]string, 0, len(ids))
for _, id := range ids {
u, err := user_model.GetUserByID(ctx, id)
if err != nil {
continue
}
names = append(names, u.Name)
}
return names
}
return &api.OrgBranchProtection{
ID: rule.ID,
OrgID: rule.OrgID,
@@ -44,19 +57,28 @@ func toAPIOrgBranchProtection(ctx *context.APIContext, rule *git_model.OrgProtec
EnablePush: rule.CanPush,
EnablePushWhitelist: rule.EnableWhitelist,
PushWhitelistTeams: resolveTeamNames(rule.WhitelistTeamIDs),
PushWhitelistUsernames: resolveUserNames(rule.WhitelistUserIDs),
PushWhitelistActionsUser: rule.WhitelistActionsUser,
EnableForcePush: rule.CanForcePush,
EnableForcePushAllowlist: rule.EnableForcePushAllowlist,
ForcePushAllowlistTeams: resolveTeamNames(rule.ForcePushAllowlistTeamIDs),
ForcePushAllowlistUsernames: resolveUserNames(rule.ForcePushAllowlistUserIDs),
ForcePushAllowlistActionsUser: rule.ForcePushAllowlistActionsUser,
EnableDelete: rule.CanDelete,
EnableDeleteAllowlist: rule.EnableDeleteAllowlist,
DeleteAllowlistTeams: resolveTeamNames(rule.DeleteAllowlistTeamIDs),
DeleteAllowlistUsernames: resolveUserNames(rule.DeleteAllowlistUserIDs),
DeleteAllowlistActionsUser: rule.DeleteAllowlistActionsUser,
EnableMergeWhitelist: rule.EnableMergeWhitelist,
MergeWhitelistTeams: resolveTeamNames(rule.MergeWhitelistTeamIDs),
MergeWhitelistUsernames: resolveUserNames(rule.MergeWhitelistUserIDs),
MergeWhitelistActionsUser: rule.MergeWhitelistActionsUser,
EnableStatusCheck: rule.EnableStatusCheck,
StatusCheckContexts: rule.StatusCheckContexts,
RequiredApprovals: rule.RequiredApprovals,
EnableApprovalsWhitelist: rule.EnableApprovalsWhitelist,
ApprovalsWhitelistTeams: resolveTeamNames(rule.ApprovalsWhitelistTeamIDs),
ApprovalsWhitelistUsernames: resolveUserNames(rule.ApprovalsWhitelistUserIDs),
BlockOnRejectedReviews: rule.BlockOnRejectedReviews,
BlockOnOfficialReviewRequests: rule.BlockOnOfficialReviewRequests,
BlockOnOutdatedBranch: rule.BlockOnOutdatedBranch,
@@ -88,6 +110,41 @@ func resolveTeamIDs(ctx *context.APIContext, orgID int64, names []string) ([]int
return ids, true
}
// resolveUserIDs converts a list of user identifiers (each a username or an email
// address) to deduped user IDs, returning 422 on the first unknown identifier. Each
// entry is looked up by username first, then falls back to an activated email address.
func resolveUserIDs(ctx *context.APIContext, names []string) ([]int64, bool) {
if len(names) == 0 {
return nil, true
}
seen := make(map[int64]struct{}, len(names))
ids := make([]int64, 0, len(names))
for _, name := range names {
u, err := user_model.GetUserByName(ctx, name)
if err != nil {
if !user_model.IsErrUserNotExist(err) {
ctx.APIErrorInternal(err)
return nil, false
}
u, err = user_model.GetUserByEmail(ctx, name)
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.APIError(http.StatusUnprocessableEntity, "unknown user: "+name)
} else {
ctx.APIErrorInternal(err)
}
return nil, false
}
}
if _, dup := seen[u.ID]; dup {
continue
}
seen[u.ID] = struct{}{}
ids = append(ids, u.ID)
}
return ids, true
}
// ListOrgBranchProtections list org-level branch protection rules
func ListOrgBranchProtections(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/branch_protections organization orgListBranchProtections
@@ -218,6 +275,26 @@ func CreateOrgBranchProtection(ctx *context.APIContext) {
if !ok {
return
}
pushUsers, ok := resolveUserIDs(ctx, form.PushWhitelistUsernames)
if !ok {
return
}
forcePushUsers, ok := resolveUserIDs(ctx, form.ForcePushAllowlistUsernames)
if !ok {
return
}
mergeUsers, ok := resolveUserIDs(ctx, form.MergeWhitelistUsernames)
if !ok {
return
}
approvalsUsers, ok := resolveUserIDs(ctx, form.ApprovalsWhitelistUsernames)
if !ok {
return
}
deleteUsers, ok := resolveUserIDs(ctx, form.DeleteAllowlistUsernames)
if !ok {
return
}
rule := &git_model.OrgProtectedBranch{
OrgID: orgID,
@@ -226,19 +303,28 @@ func CreateOrgBranchProtection(ctx *context.APIContext) {
CanPush: form.EnablePush,
EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
WhitelistTeamIDs: pushTeams,
WhitelistUserIDs: pushUsers,
WhitelistActionsUser: form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistActionsUser,
CanForcePush: form.EnablePush && form.EnableForcePush,
EnableForcePushAllowlist: form.EnablePush && form.EnableForcePush && form.EnableForcePushAllowlist,
ForcePushAllowlistTeamIDs: forcePushTeams,
ForcePushAllowlistUserIDs: forcePushUsers,
ForcePushAllowlistActionsUser: form.EnablePush && form.EnableForcePush && form.EnableForcePushAllowlist && form.ForcePushAllowlistActionsUser,
CanDelete: form.EnableDelete,
EnableDeleteAllowlist: form.EnableDelete && form.EnableDeleteAllowlist,
DeleteAllowlistTeamIDs: deleteTeams,
DeleteAllowlistUserIDs: deleteUsers,
DeleteAllowlistActionsUser: form.EnableDelete && form.EnableDeleteAllowlist && form.DeleteAllowlistActionsUser,
EnableMergeWhitelist: form.EnableMergeWhitelist,
MergeWhitelistTeamIDs: mergeTeams,
MergeWhitelistUserIDs: mergeUsers,
MergeWhitelistActionsUser: form.EnableMergeWhitelist && form.MergeWhitelistActionsUser,
EnableStatusCheck: form.EnableStatusCheck,
StatusCheckContexts: form.StatusCheckContexts,
RequiredApprovals: form.RequiredApprovals,
EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
ApprovalsWhitelistTeamIDs: approvalsTeams,
ApprovalsWhitelistUserIDs: approvalsUsers,
BlockOnRejectedReviews: form.BlockOnRejectedReviews,
BlockOnOfficialReviewRequests: form.BlockOnOfficialReviewRequests,
BlockOnOutdatedBranch: form.BlockOnOutdatedBranch,
@@ -320,6 +406,16 @@ func EditOrgBranchProtection(ctx *context.APIContext) {
}
rule.WhitelistTeamIDs = ids
}
if form.PushWhitelistUsernames != nil {
ids, ok := resolveUserIDs(ctx, form.PushWhitelistUsernames)
if !ok {
return
}
rule.WhitelistUserIDs = ids
}
if form.PushWhitelistActionsUser != nil {
rule.WhitelistActionsUser = *form.PushWhitelistActionsUser
}
if form.EnableForcePush != nil {
rule.CanForcePush = *form.EnableForcePush
}
@@ -333,6 +429,16 @@ func EditOrgBranchProtection(ctx *context.APIContext) {
}
rule.ForcePushAllowlistTeamIDs = ids
}
if form.ForcePushAllowlistUsernames != nil {
ids, ok := resolveUserIDs(ctx, form.ForcePushAllowlistUsernames)
if !ok {
return
}
rule.ForcePushAllowlistUserIDs = ids
}
if form.ForcePushAllowlistActionsUser != nil {
rule.ForcePushAllowlistActionsUser = *form.ForcePushAllowlistActionsUser
}
if form.EnableDelete != nil {
rule.CanDelete = *form.EnableDelete
}
@@ -346,6 +452,16 @@ func EditOrgBranchProtection(ctx *context.APIContext) {
}
rule.DeleteAllowlistTeamIDs = ids
}
if form.DeleteAllowlistUsernames != nil {
ids, ok := resolveUserIDs(ctx, form.DeleteAllowlistUsernames)
if !ok {
return
}
rule.DeleteAllowlistUserIDs = ids
}
if form.DeleteAllowlistActionsUser != nil {
rule.DeleteAllowlistActionsUser = *form.DeleteAllowlistActionsUser
}
if form.EnableMergeWhitelist != nil {
rule.EnableMergeWhitelist = *form.EnableMergeWhitelist
}
@@ -356,6 +472,16 @@ func EditOrgBranchProtection(ctx *context.APIContext) {
}
rule.MergeWhitelistTeamIDs = ids
}
if form.MergeWhitelistUsernames != nil {
ids, ok := resolveUserIDs(ctx, form.MergeWhitelistUsernames)
if !ok {
return
}
rule.MergeWhitelistUserIDs = ids
}
if form.MergeWhitelistActionsUser != nil {
rule.MergeWhitelistActionsUser = *form.MergeWhitelistActionsUser
}
if form.EnableStatusCheck != nil {
rule.EnableStatusCheck = *form.EnableStatusCheck
}
@@ -375,6 +501,13 @@ func EditOrgBranchProtection(ctx *context.APIContext) {
}
rule.ApprovalsWhitelistTeamIDs = ids
}
if form.ApprovalsWhitelistUsernames != nil {
ids, ok := resolveUserIDs(ctx, form.ApprovalsWhitelistUsernames)
if !ok {
return
}
rule.ApprovalsWhitelistUserIDs = ids
}
if form.BlockOnRejectedReviews != nil {
rule.BlockOnRejectedReviews = *form.BlockOnRejectedReviews
}
+153
View File
@@ -25356,6 +25356,13 @@
},
"x-go-name": "ApprovalsWhitelistTeams"
},
"approvals_whitelist_username": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ApprovalsWhitelistUsernames"
},
"block_admin_merge_override": {
"type": "boolean",
"x-go-name": "BlockAdminMergeOverride"
@@ -25372,6 +25379,10 @@
"type": "boolean",
"x-go-name": "BlockOnRejectedReviews"
},
"delete_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "DeleteAllowlistActionsUser"
},
"delete_allowlist_teams": {
"type": "array",
"items": {
@@ -25379,6 +25390,13 @@
},
"x-go-name": "DeleteAllowlistTeams"
},
"delete_allowlist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "DeleteAllowlistUsernames"
},
"dismiss_stale_approvals": {
"type": "boolean",
"x-go-name": "DismissStaleApprovals"
@@ -25419,6 +25437,10 @@
"type": "boolean",
"x-go-name": "EnableStatusCheck"
},
"force_push_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "ForcePushAllowlistActionsUser"
},
"force_push_allowlist_teams": {
"type": "array",
"items": {
@@ -25426,10 +25448,21 @@
},
"x-go-name": "ForcePushAllowlistTeams"
},
"force_push_allowlist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ForcePushAllowlistUsernames"
},
"ignore_stale_approvals": {
"type": "boolean",
"x-go-name": "IgnoreStaleApprovals"
},
"merge_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "MergeWhitelistActionsUser"
},
"merge_whitelist_teams": {
"type": "array",
"items": {
@@ -25437,6 +25470,13 @@
},
"x-go-name": "MergeWhitelistTeams"
},
"merge_whitelist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "MergeWhitelistUsernames"
},
"priority": {
"type": "integer",
"format": "int64",
@@ -25446,6 +25486,10 @@
"type": "string",
"x-go-name": "ProtectedFilePatterns"
},
"push_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "PushWhitelistActionsUser"
},
"push_whitelist_teams": {
"type": "array",
"items": {
@@ -25453,6 +25497,13 @@
},
"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"
@@ -26786,6 +26837,13 @@
},
"x-go-name": "ApprovalsWhitelistTeams"
},
"approvals_whitelist_username": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ApprovalsWhitelistUsernames"
},
"block_admin_merge_override": {
"type": "boolean",
"x-go-name": "BlockAdminMergeOverride"
@@ -26802,6 +26860,10 @@
"type": "boolean",
"x-go-name": "BlockOnRejectedReviews"
},
"delete_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "DeleteAllowlistActionsUser"
},
"delete_allowlist_teams": {
"type": "array",
"items": {
@@ -26809,6 +26871,13 @@
},
"x-go-name": "DeleteAllowlistTeams"
},
"delete_allowlist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "DeleteAllowlistUsernames"
},
"dismiss_stale_approvals": {
"type": "boolean",
"x-go-name": "DismissStaleApprovals"
@@ -26849,6 +26918,10 @@
"type": "boolean",
"x-go-name": "EnableStatusCheck"
},
"force_push_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "ForcePushAllowlistActionsUser"
},
"force_push_allowlist_teams": {
"type": "array",
"items": {
@@ -26856,10 +26929,21 @@
},
"x-go-name": "ForcePushAllowlistTeams"
},
"force_push_allowlist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ForcePushAllowlistUsernames"
},
"ignore_stale_approvals": {
"type": "boolean",
"x-go-name": "IgnoreStaleApprovals"
},
"merge_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "MergeWhitelistActionsUser"
},
"merge_whitelist_teams": {
"type": "array",
"items": {
@@ -26867,6 +26951,13 @@
},
"x-go-name": "MergeWhitelistTeams"
},
"merge_whitelist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "MergeWhitelistUsernames"
},
"priority": {
"type": "integer",
"format": "int64",
@@ -26876,6 +26967,10 @@
"type": "string",
"x-go-name": "ProtectedFilePatterns"
},
"push_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "PushWhitelistActionsUser"
},
"push_whitelist_teams": {
"type": "array",
"items": {
@@ -26883,6 +26978,13 @@
},
"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"
@@ -29749,6 +29851,13 @@
},
"x-go-name": "ApprovalsWhitelistTeams"
},
"approvals_whitelist_username": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ApprovalsWhitelistUsernames"
},
"block_admin_merge_override": {
"type": "boolean",
"x-go-name": "BlockAdminMergeOverride"
@@ -29770,6 +29879,10 @@
"format": "date-time",
"x-go-name": "Created"
},
"delete_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "DeleteAllowlistActionsUser"
},
"delete_allowlist_teams": {
"type": "array",
"items": {
@@ -29777,6 +29890,13 @@
},
"x-go-name": "DeleteAllowlistTeams"
},
"delete_allowlist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "DeleteAllowlistUsernames"
},
"dismiss_stale_approvals": {
"type": "boolean",
"x-go-name": "DismissStaleApprovals"
@@ -29817,6 +29937,10 @@
"type": "boolean",
"x-go-name": "EnableStatusCheck"
},
"force_push_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "ForcePushAllowlistActionsUser"
},
"force_push_allowlist_teams": {
"type": "array",
"items": {
@@ -29824,6 +29948,13 @@
},
"x-go-name": "ForcePushAllowlistTeams"
},
"force_push_allowlist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ForcePushAllowlistUsernames"
},
"id": {
"type": "integer",
"format": "int64",
@@ -29833,6 +29964,10 @@
"type": "boolean",
"x-go-name": "IgnoreStaleApprovals"
},
"merge_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "MergeWhitelistActionsUser"
},
"merge_whitelist_teams": {
"type": "array",
"items": {
@@ -29840,6 +29975,13 @@
},
"x-go-name": "MergeWhitelistTeams"
},
"merge_whitelist_usernames": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "MergeWhitelistUsernames"
},
"org_id": {
"type": "integer",
"format": "int64",
@@ -29854,6 +29996,10 @@
"type": "string",
"x-go-name": "ProtectedFilePatterns"
},
"push_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "PushWhitelistActionsUser"
},
"push_whitelist_teams": {
"type": "array",
"items": {
@@ -29861,6 +30007,13 @@
},
"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"
+153
View File
@@ -4483,6 +4483,13 @@
"type": "array",
"x-go-name": "ApprovalsWhitelistTeams"
},
"approvals_whitelist_username": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "ApprovalsWhitelistUsernames"
},
"block_admin_merge_override": {
"type": "boolean",
"x-go-name": "BlockAdminMergeOverride"
@@ -4499,6 +4506,10 @@
"type": "boolean",
"x-go-name": "BlockOnRejectedReviews"
},
"delete_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "DeleteAllowlistActionsUser"
},
"delete_allowlist_teams": {
"items": {
"type": "string"
@@ -4506,6 +4517,13 @@
"type": "array",
"x-go-name": "DeleteAllowlistTeams"
},
"delete_allowlist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "DeleteAllowlistUsernames"
},
"dismiss_stale_approvals": {
"type": "boolean",
"x-go-name": "DismissStaleApprovals"
@@ -4546,6 +4564,10 @@
"type": "boolean",
"x-go-name": "EnableStatusCheck"
},
"force_push_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "ForcePushAllowlistActionsUser"
},
"force_push_allowlist_teams": {
"items": {
"type": "string"
@@ -4553,10 +4575,21 @@
"type": "array",
"x-go-name": "ForcePushAllowlistTeams"
},
"force_push_allowlist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "ForcePushAllowlistUsernames"
},
"ignore_stale_approvals": {
"type": "boolean",
"x-go-name": "IgnoreStaleApprovals"
},
"merge_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "MergeWhitelistActionsUser"
},
"merge_whitelist_teams": {
"items": {
"type": "string"
@@ -4564,6 +4597,13 @@
"type": "array",
"x-go-name": "MergeWhitelistTeams"
},
"merge_whitelist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "MergeWhitelistUsernames"
},
"priority": {
"format": "int64",
"type": "integer",
@@ -4573,6 +4613,10 @@
"type": "string",
"x-go-name": "ProtectedFilePatterns"
},
"push_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "PushWhitelistActionsUser"
},
"push_whitelist_teams": {
"items": {
"type": "string"
@@ -4580,6 +4624,13 @@
"type": "array",
"x-go-name": "PushWhitelistTeams"
},
"push_whitelist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "PushWhitelistUsernames"
},
"require_signed_commits": {
"type": "boolean",
"x-go-name": "RequireSignedCommits"
@@ -5885,6 +5936,13 @@
"type": "array",
"x-go-name": "ApprovalsWhitelistTeams"
},
"approvals_whitelist_username": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "ApprovalsWhitelistUsernames"
},
"block_admin_merge_override": {
"type": "boolean",
"x-go-name": "BlockAdminMergeOverride"
@@ -5901,6 +5959,10 @@
"type": "boolean",
"x-go-name": "BlockOnRejectedReviews"
},
"delete_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "DeleteAllowlistActionsUser"
},
"delete_allowlist_teams": {
"items": {
"type": "string"
@@ -5908,6 +5970,13 @@
"type": "array",
"x-go-name": "DeleteAllowlistTeams"
},
"delete_allowlist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "DeleteAllowlistUsernames"
},
"dismiss_stale_approvals": {
"type": "boolean",
"x-go-name": "DismissStaleApprovals"
@@ -5948,6 +6017,10 @@
"type": "boolean",
"x-go-name": "EnableStatusCheck"
},
"force_push_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "ForcePushAllowlistActionsUser"
},
"force_push_allowlist_teams": {
"items": {
"type": "string"
@@ -5955,10 +6028,21 @@
"type": "array",
"x-go-name": "ForcePushAllowlistTeams"
},
"force_push_allowlist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "ForcePushAllowlistUsernames"
},
"ignore_stale_approvals": {
"type": "boolean",
"x-go-name": "IgnoreStaleApprovals"
},
"merge_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "MergeWhitelistActionsUser"
},
"merge_whitelist_teams": {
"items": {
"type": "string"
@@ -5966,6 +6050,13 @@
"type": "array",
"x-go-name": "MergeWhitelistTeams"
},
"merge_whitelist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "MergeWhitelistUsernames"
},
"priority": {
"format": "int64",
"type": "integer",
@@ -5975,6 +6066,10 @@
"type": "string",
"x-go-name": "ProtectedFilePatterns"
},
"push_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "PushWhitelistActionsUser"
},
"push_whitelist_teams": {
"items": {
"type": "string"
@@ -5982,6 +6077,13 @@
"type": "array",
"x-go-name": "PushWhitelistTeams"
},
"push_whitelist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "PushWhitelistUsernames"
},
"require_signed_commits": {
"type": "boolean",
"x-go-name": "RequireSignedCommits"
@@ -8856,6 +8958,13 @@
"type": "array",
"x-go-name": "ApprovalsWhitelistTeams"
},
"approvals_whitelist_username": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "ApprovalsWhitelistUsernames"
},
"block_admin_merge_override": {
"type": "boolean",
"x-go-name": "BlockAdminMergeOverride"
@@ -8877,6 +8986,10 @@
"type": "string",
"x-go-name": "Created"
},
"delete_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "DeleteAllowlistActionsUser"
},
"delete_allowlist_teams": {
"items": {
"type": "string"
@@ -8884,6 +8997,13 @@
"type": "array",
"x-go-name": "DeleteAllowlistTeams"
},
"delete_allowlist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "DeleteAllowlistUsernames"
},
"dismiss_stale_approvals": {
"type": "boolean",
"x-go-name": "DismissStaleApprovals"
@@ -8924,6 +9044,10 @@
"type": "boolean",
"x-go-name": "EnableStatusCheck"
},
"force_push_allowlist_actions_user": {
"type": "boolean",
"x-go-name": "ForcePushAllowlistActionsUser"
},
"force_push_allowlist_teams": {
"items": {
"type": "string"
@@ -8931,6 +9055,13 @@
"type": "array",
"x-go-name": "ForcePushAllowlistTeams"
},
"force_push_allowlist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "ForcePushAllowlistUsernames"
},
"id": {
"format": "int64",
"type": "integer",
@@ -8940,6 +9071,10 @@
"type": "boolean",
"x-go-name": "IgnoreStaleApprovals"
},
"merge_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "MergeWhitelistActionsUser"
},
"merge_whitelist_teams": {
"items": {
"type": "string"
@@ -8947,6 +9082,13 @@
"type": "array",
"x-go-name": "MergeWhitelistTeams"
},
"merge_whitelist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "MergeWhitelistUsernames"
},
"org_id": {
"format": "int64",
"type": "integer",
@@ -8961,6 +9103,10 @@
"type": "string",
"x-go-name": "ProtectedFilePatterns"
},
"push_whitelist_actions_user": {
"type": "boolean",
"x-go-name": "PushWhitelistActionsUser"
},
"push_whitelist_teams": {
"items": {
"type": "string"
@@ -8968,6 +9114,13 @@
"type": "array",
"x-go-name": "PushWhitelistTeams"
},
"push_whitelist_usernames": {
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "PushWhitelistUsernames"
},
"require_signed_commits": {
"type": "boolean",
"x-go-name": "RequireSignedCommits"