[WIP] Move from space_owner to repo_admin for protection rules (#745)

This commit is contained in:
Johannes Batzill 2023-10-30 22:35:52 +00:00 committed by Harness
parent e0df722ce3
commit b0e519b571
20 changed files with 102 additions and 96 deletions

View File

@ -57,15 +57,16 @@ func CheckRepo(
return Check(ctx, authorizer, session, scope, resource, permission) return Check(ctx, authorizer, session, scope, resource, permission)
} }
func IsSpaceAdmin( func IsRepoOwner(
ctx context.Context, ctx context.Context,
authorizer authz.Authorizer, authorizer authz.Authorizer,
session *auth.Session, session *auth.Session,
repo *types.Repository, repo *types.Repository,
) (bool, error) { ) (bool, error) {
err := CheckRepo(ctx, authorizer, session, repo, enum.PermissionSpaceCreate, false) // for now we use repoedit as permission to verify if someone is a SpaceOwner and hence a RepoOwner.
err := CheckRepo(ctx, authorizer, session, repo, enum.PermissionRepoEdit, false)
if err != nil && !errors.Is(err, ErrNotAuthorized) { if err != nil && !errors.Is(err, ErrNotAuthorized) {
return false, fmt.Errorf("failed to check access to find if the user is space admin: %w", err) return false, fmt.Errorf("failed to check access user access: %w", err)
} }
return err == nil, nil return err == nil, nil

View File

@ -98,9 +98,9 @@ func (c *Controller) checkProtectionRules(
refUpdates changedRefs, refUpdates changedRefs,
output *githook.Output, output *githook.Output,
) error { ) error {
isSpaceOwner, err := apiauth.IsSpaceAdmin(ctx, c.authorizer, session, repo) isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, repo)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to determine if user is repo owner: %w", err)
} }
protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID) protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID)
@ -117,12 +117,12 @@ func (c *Controller) checkProtectionRules(
} }
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{ violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
Repo: repo, Repo: repo,
RefAction: refAction, RefAction: refAction,
RefType: refType, RefType: refType,
RefNames: names, RefNames: names,
}) })
if err != nil { if err != nil {
errCheckAction = fmt.Errorf("failed to verify protection rules for git push: %w", err) errCheckAction = fmt.Errorf("failed to verify protection rules for git push: %w", err)

View File

@ -129,9 +129,9 @@ func (c *Controller) Merge(
} }
} }
isSpaceOwner, err := apiauth.IsSpaceAdmin(ctx, c.authorizer, session, targetRepo) isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, targetRepo)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to determine if the user is space admin: %w", err) return nil, nil, fmt.Errorf("failed to determine if user is repo owner: %w", err)
} }
checkResults, err := c.checkStore.ListResults(ctx, targetRepo.ID, pr.SourceSHA) checkResults, err := c.checkStore.ListResults(ctx, targetRepo.ID, pr.SourceSHA)
@ -152,7 +152,7 @@ func (c *Controller) Merge(
ruleOut, violations, err := protectionRules.MergeVerify(ctx, protection.MergeVerifyInput{ ruleOut, violations, err := protectionRules.MergeVerify(ctx, protection.MergeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
TargetRepo: targetRepo, TargetRepo: targetRepo,
SourceRepo: sourceRepo, SourceRepo: sourceRepo,
PullReq: pr, PullReq: pr,

View File

@ -62,7 +62,7 @@ func (c *Controller) CommitFiles(ctx context.Context,
return types.CommitFilesResponse{}, nil, err return types.CommitFilesResponse{}, nil, err
} }
rules, isSpaceOwner, err := c.fetchRules(ctx, session, repo) rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
if err != nil { if err != nil {
return types.CommitFilesResponse{}, nil, err return types.CommitFilesResponse{}, nil, err
} }
@ -78,12 +78,12 @@ func (c *Controller) CommitFiles(ctx context.Context,
} }
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{ violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
Repo: repo, Repo: repo,
RefAction: refAction, RefAction: refAction,
RefType: protection.RefTypeBranch, RefType: protection.RefTypeBranch,
RefNames: []string{branchName}, RefNames: []string{branchName},
}) })
if err != nil { if err != nil {
return types.CommitFilesResponse{}, nil, fmt.Errorf("failed to verify protection rules: %w", err) return types.CommitFilesResponse{}, nil, fmt.Errorf("failed to verify protection rules: %w", err)

View File

@ -130,9 +130,9 @@ func (c *Controller) fetchRules(
session *auth.Session, session *auth.Session,
repo *types.Repository, repo *types.Repository,
) (protection.Protection, bool, error) { ) (protection.Protection, bool, error) {
isSpaceOwner, err := apiauth.IsSpaceAdmin(ctx, c.authorizer, session, repo) isRepoOwner, err := apiauth.IsRepoOwner(ctx, c.authorizer, session, repo)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to determine space ownership: %w", err) return nil, false, fmt.Errorf("failed to determine if user is repo owner: %w", err)
} }
protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID) protectionRules, err := c.protectionManager.ForRepository(ctx, repo.ID)
@ -140,5 +140,5 @@ func (c *Controller) fetchRules(
return nil, false, fmt.Errorf("failed to fetch protection rules for the repository: %w", err) return nil, false, fmt.Errorf("failed to fetch protection rules for the repository: %w", err)
} }
return protectionRules, isSpaceOwner, nil return protectionRules, isRepoOwner, nil
} }

View File

@ -51,18 +51,18 @@ func (c *Controller) CreateBranch(ctx context.Context,
in.Target = repo.DefaultBranch in.Target = repo.DefaultBranch
} }
rules, isSpaceOwner, err := c.fetchRules(ctx, session, repo) rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{ violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
Repo: repo, Repo: repo,
RefAction: protection.RefActionCreate, RefAction: protection.RefActionCreate,
RefType: protection.RefTypeBranch, RefType: protection.RefTypeBranch,
RefNames: []string{in.Name}, RefNames: []string{in.Name},
}) })
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err) return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err)

View File

@ -55,18 +55,18 @@ func (c *Controller) CreateCommitTag(ctx context.Context,
in.Target = repo.DefaultBranch in.Target = repo.DefaultBranch
} }
rules, isSpaceOwner, err := c.fetchRules(ctx, session, repo) rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{ violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
Repo: repo, Repo: repo,
RefAction: protection.RefActionCreate, RefAction: protection.RefActionCreate,
RefType: protection.RefTypeTag, RefType: protection.RefTypeTag,
RefNames: []string{in.Name}, RefNames: []string{in.Name},
}) })
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err) return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err)

View File

@ -46,18 +46,18 @@ func (c *Controller) DeleteBranch(ctx context.Context,
return nil, usererror.ErrDefaultBranchCantBeDeleted return nil, usererror.ErrDefaultBranchCantBeDeleted
} }
rules, isSpaceOwner, err := c.fetchRules(ctx, session, repo) rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{ violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
Repo: repo, Repo: repo,
RefAction: protection.RefActionDelete, RefAction: protection.RefActionDelete,
RefType: protection.RefTypeBranch, RefType: protection.RefTypeBranch,
RefNames: []string{branchName}, RefNames: []string{branchName},
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to verify protection rules: %w", err) return nil, fmt.Errorf("failed to verify protection rules: %w", err)

View File

@ -37,18 +37,18 @@ func (c *Controller) DeleteTag(ctx context.Context,
return nil, err return nil, err
} }
rules, isSpaceOwner, err := c.fetchRules(ctx, session, repo) rules, isRepoOwner, err := c.fetchRules(ctx, session, repo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{ violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
Actor: &session.Principal, Actor: &session.Principal,
IsSpaceOwner: isSpaceOwner, IsRepoOwner: isRepoOwner,
Repo: repo, Repo: repo,
RefAction: protection.RefActionDelete, RefAction: protection.RefActionDelete,
RefType: protection.RefTypeTag, RefType: protection.RefTypeTag,
RefNames: []string{tagName}, RefNames: []string{tagName},
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to verify protection rules: %w", err) return nil, fmt.Errorf("failed to verify protection rules: %w", err)

View File

@ -19,8 +19,8 @@ import (
) )
type DefBypass struct { type DefBypass struct {
UserIDs []int64 `json:"user_ids,omitempty"` UserIDs []int64 `json:"user_ids,omitempty"`
SpaceOwners bool `json:"space_owners,omitempty"` RepoOwners bool `json:"repo_owners,omitempty"`
} }
func (v DefBypass) Sanitize() error { func (v DefBypass) Sanitize() error {

View File

@ -42,7 +42,7 @@ func (v *Branch) MergeVerify(
ctx context.Context, ctx context.Context,
in MergeVerifyInput, in MergeVerifyInput,
) (MergeVerifyOutput, []types.RuleViolations, error) { ) (MergeVerifyOutput, []types.RuleViolations, error) {
if v.isBypassed(in.Actor, in.IsSpaceOwner) { if v.isBypassed(in.Actor, in.IsRepoOwner) {
return MergeVerifyOutput{}, nil, nil return MergeVerifyOutput{}, nil, nil
} }
@ -53,7 +53,7 @@ func (v *Branch) RefChangeVerify(
ctx context.Context, ctx context.Context,
in RefChangeVerifyInput, in RefChangeVerifyInput,
) ([]types.RuleViolations, error) { ) ([]types.RuleViolations, error) {
if v.isBypassed(in.Actor, in.IsSpaceOwner) || in.RefType != RefTypeBranch || len(in.RefNames) == 0 { if v.isBypassed(in.Actor, in.IsRepoOwner) || in.RefType != RefTypeBranch || len(in.RefNames) == 0 {
return nil, nil return nil, nil
} }
@ -76,9 +76,9 @@ func (v *Branch) Sanitize() error {
return nil return nil
} }
func (v *Branch) isBypassed(actor *types.Principal, isSpaceOwner bool) bool { func (v *Branch) isBypassed(actor *types.Principal, isRepoOwner bool) bool {
return actor != nil && return actor != nil &&
(actor.Admin || (actor.Admin ||
v.Bypass.SpaceOwners && isSpaceOwner || v.Bypass.RepoOwners && isRepoOwner ||
slices.Contains(v.Bypass.UserIDs, actor.ID)) slices.Contains(v.Bypass.UserIDs, actor.ID))
} }

View File

@ -33,39 +33,39 @@ func TestBranch_isBypass(t *testing.T) {
}{ }{
{ {
name: "empty", name: "empty",
bypass: DefBypass{UserIDs: nil, SpaceOwners: false}, bypass: DefBypass{UserIDs: nil, RepoOwners: false},
actor: user, actor: user,
exp: false, exp: false,
}, },
{ {
name: "admin", name: "admin",
bypass: DefBypass{UserIDs: nil, SpaceOwners: false}, bypass: DefBypass{UserIDs: nil, RepoOwners: false},
actor: admin, actor: admin,
exp: true, exp: true,
}, },
{ {
name: "space-owners-false", name: "repo-owners-false",
bypass: DefBypass{UserIDs: nil, SpaceOwners: false}, bypass: DefBypass{UserIDs: nil, RepoOwners: false},
actor: user, actor: user,
owner: true, owner: true,
exp: false, exp: false,
}, },
{ {
name: "space-owners-true", name: "repo-owners-true",
bypass: DefBypass{UserIDs: nil, SpaceOwners: true}, bypass: DefBypass{UserIDs: nil, RepoOwners: true},
actor: user, actor: user,
owner: true, owner: true,
exp: true, exp: true,
}, },
{ {
name: "selected-false", name: "selected-false",
bypass: DefBypass{UserIDs: []int64{1, 66}, SpaceOwners: false}, bypass: DefBypass{UserIDs: []int64{1, 66}, RepoOwners: false},
actor: user, actor: user,
exp: false, exp: false,
}, },
{ {
name: "selected-true", name: "selected-true",
bypass: DefBypass{UserIDs: []int64{1, 42, 66}, SpaceOwners: false}, bypass: DefBypass{UserIDs: []int64{1, 42, 66}, RepoOwners: false},
actor: user, actor: user,
exp: true, exp: true,
}, },

View File

@ -26,12 +26,12 @@ type (
} }
RefChangeVerifyInput struct { RefChangeVerifyInput struct {
Actor *types.Principal Actor *types.Principal
IsSpaceOwner bool IsRepoOwner bool
Repo *types.Repository Repo *types.Repository
RefAction RefAction RefAction RefAction
RefType RefType RefType RefType
RefNames []string RefNames []string
} }
RefType int RefType int

View File

@ -33,7 +33,7 @@ type (
MergeVerifyInput struct { MergeVerifyInput struct {
Actor *types.Principal Actor *types.Principal
IsSpaceOwner bool IsRepoOwner bool
Membership *types.Membership Membership *types.Membership
TargetRepo *types.Repository TargetRepo *types.Repository
SourceRepo *types.Repository SourceRepo *types.Repository

View File

@ -152,8 +152,7 @@ const BranchProtectionForm = (props: {
const excludeArr = excludeList?.map((arr: string) => ['exclude', arr]) const excludeArr = excludeList?.map((arr: string) => ['exclude', arr])
const finalArray = [...includeArr, ...excludeArr] const finalArray = [...includeArr, ...excludeArr]
const idsArray = (rule?.definition as ProtectionBranch)?.bypass?.user_ids const idsArray = (rule?.definition as ProtectionBranch)?.bypass?.user_ids
const filteredArray = users?.filter(user => idsArray?.includes(user.id as number)) const bypassList = users?.filter(user => idsArray?.includes(user.id as number))?.map(user => `${user.id} ${user.display_name}`)
const resultArray = filteredArray?.map(user => `${user.id} ${user.display_name}`)
return { return {
name: rule?.uid, name: rule?.uid,
desc: rule.description, desc: rule.description,
@ -161,8 +160,8 @@ const BranchProtectionForm = (props: {
target: '', target: '',
targetDefault: (rule?.pattern as ProtectionPattern)?.default, targetDefault: (rule?.pattern as ProtectionPattern)?.default,
targetList: finalArray, targetList: finalArray,
allProjectOwners: (rule.definition as ProtectionBranch)?.bypass?.space_owners, allRepoOwners: (rule.definition as ProtectionBranch)?.bypass?.repo_owners,
projectOwners: resultArray, bypassList: bypassList,
requireMinReviewers: minReviewerCheck, requireMinReviewers: minReviewerCheck,
minReviewers: minReviewerCheck minReviewers: minReviewerCheck
? (rule.definition as ProtectionBranch)?.pullreq?.approvals?.require_minimum_count ? (rule.definition as ProtectionBranch)?.pullreq?.approvals?.require_minimum_count
@ -202,7 +201,7 @@ const BranchProtectionForm = (props: {
const excludeArray = const excludeArray =
formData?.targetList?.filter(([type]) => type === 'exclude').map(([, value]) => value) ?? [] formData?.targetList?.filter(([type]) => type === 'exclude').map(([, value]) => value) ?? []
const intArray = formData?.projectOwners?.map(item => parseInt(item.split(' ')[0])) const bypassList = formData?.bypassList?.map(item => parseInt(item.split(' ')[0]))
const payload: OpenapiRule = { const payload: OpenapiRule = {
uid: formData.name, uid: formData.name,
type: 'branch', type: 'branch',
@ -215,8 +214,8 @@ const BranchProtectionForm = (props: {
}, },
definition: { definition: {
bypass: { bypass: {
user_ids: intArray, user_ids: bypassList,
space_owners: formData.allProjectOwners repo_owners: formData.allRepoOwners
}, },
pullreq: { pullreq: {
approvals: { approvals: {
@ -253,7 +252,7 @@ const BranchProtectionForm = (props: {
}}> }}>
{formik => { {formik => {
const targetList = formik.values.targetList const targetList = formik.values.targetList
const bypassList = formik.values.projectOwners const bypassList = formik.values.bypassList
const minReviewers = formik.values.requireMinReviewers const minReviewers = formik.values.requireMinReviewers
const statusChecks = formik.values.statusChecks const statusChecks = formik.values.statusChecks
const limitMergeStrats = formik.values.limitMergeStrategies const limitMergeStrats = formik.values.limitMergeStrategies
@ -394,22 +393,26 @@ const BranchProtectionForm = (props: {
<Text className={css.headingSize} padding={{ bottom: 'medium' }} font={{ variation: FontVariation.H4 }}> <Text className={css.headingSize} padding={{ bottom: 'medium' }} font={{ variation: FontVariation.H4 }}>
{getString('branchProtection.bypassList')} {getString('branchProtection.bypassList')}
</Text> </Text>
<FormInput.CheckBox label={getString('branchProtection.allProjectOwners')} name={'allProjectOwners'} /> <FormInput.CheckBox label={getString('branchProtection.allRepoOwners')} name={'allRepoOwners'} />
<FormInput.Select <FormInput.Select
items={userOptions} items={userOptions}
onQueryChange={setSearchTerm} onQueryChange={setSearchTerm}
className={css.widthContainer} className={css.widthContainer}
onChange={item => { onChange={item => {
bypassList?.push(item.value as string) const id = item.value?.toString().split(' ')[0]
const uniqueArr = Array.from(new Set(bypassList)) const displayName = item.label
formik.setFieldValue('projectOwners', uniqueArr) const bypassEntry = `${id} ${displayName}`
// formik.values.projectOwners.push(item.value as number) bypassList?.push(bypassEntry)
const uniqueArr = Array.from(new Set(bypassList))
formik.setFieldValue('bypassList', uniqueArr)
}} }}
name={'projectOwners'}></FormInput.Select> name={'bypassList'}></FormInput.Select>
<Container className={cx(css.widthContainer, css.bypassContainer)}> <Container className={cx(css.widthContainer, css.bypassContainer)}>
{bypassList?.map((owner, idx) => { {bypassList?.map((owner, idx) => {
const name = owner.split(' ')[1] const nameIdx = owner.indexOf(" ") + 1
const name = owner.substring(nameIdx)
return ( return (
<Layout.Horizontal key={`${name}-${idx}`} flex={{ align: 'center-center' }} padding={'small'}> <Layout.Horizontal key={`${name}-${idx}`} flex={{ align: 'center-center' }} padding={'small'}>
<Avatar hoverCard={false} size="small" name={name.toString()} /> <Avatar hoverCard={false} size="small" name={name.toString()} />
@ -423,7 +426,7 @@ const BranchProtectionForm = (props: {
const filteredData = bypassList.filter( const filteredData = bypassList.filter(
item => !(item[0] === owner[0] && item[1] === owner[1]) item => !(item[0] === owner[0] && item[1] === owner[1])
) )
formik.setFieldValue('projectOwners', filteredData) formik.setFieldValue('bypassList', filteredData)
}} }}
/> />
</Layout.Horizontal> </Layout.Horizontal>
@ -444,7 +447,7 @@ const BranchProtectionForm = (props: {
<Layout.Horizontal spacing="small"> <Layout.Horizontal spacing="small">
<Button <Button
type="submit" type="submit"
text={editMode ? getString('branchProtection.editRule') : getString('branchProtection.createRule')} text={editMode ? getString('branchProtection.saveRule') : getString('branchProtection.createRule')}
variation={ButtonVariation.PRIMARY} variation={ButtonVariation.PRIMARY}
disabled={false} disabled={false}
/> />

View File

@ -44,7 +44,7 @@ export interface StringsMap {
branchDoesNotHaveFile: string branchDoesNotHaveFile: string
branchName: string branchName: string
branchNotFound: string branchNotFound: string
'branchProtection.allProjectOwners': string 'branchProtection.allRepoOwners': string
'branchProtection.autoDeleteText': string 'branchProtection.autoDeleteText': string
'branchProtection.autoDeleteTitle': string 'branchProtection.autoDeleteTitle': string
'branchProtection.blockBranchCreation': string 'branchProtection.blockBranchCreation': string
@ -86,6 +86,7 @@ export interface StringsMap {
'branchProtection.ruleDeleted': string 'branchProtection.ruleDeleted': string
'branchProtection.ruleEmpty': string 'branchProtection.ruleEmpty': string
'branchProtection.ruleUpdated': string 'branchProtection.ruleUpdated': string
'branchProtection.saveRule': string
'branchProtection.statusCheck': string 'branchProtection.statusCheck': string
'branchProtection.targetBranches': string 'branchProtection.targetBranches': string
'branchProtection.targetPlaceholder': string 'branchProtection.targetPlaceholder': string

View File

@ -859,7 +859,7 @@ branchProtection:
defaultBranch: Default branch defaultBranch: Default branch
bypassList: Bypass List bypassList: Bypass List
newBranchProtectionRule: New Branch Protection Rule newBranchProtectionRule: New Branch Protection Rule
allProjectOwners: All project owners allRepoOwners: Allow users with edit permission on the repository to bypass
protectionSelectAll: 'Protections: Select all that apply' protectionSelectAll: 'Protections: Select all that apply'
requireMinReviewersTitle: Require a minimum number of reviewers requireMinReviewersTitle: Require a minimum number of reviewers
requireMinReviewersContent: Require approval on pull requests from a minimum number of reviewers requireMinReviewersContent: Require approval on pull requests from a minimum number of reviewers
@ -883,6 +883,7 @@ branchProtection:
blockBranchDeletion: Block branch deletion blockBranchDeletion: Block branch deletion
blockBranchDeletionText: Only allow users with bypass permission to delete matching branches blockBranchDeletionText: Only allow users with bypass permission to delete matching branches
editRule: Edit Rule editRule: Edit Rule
saveRule: Save Rule
deleteRule: Delete Rule deleteRule: Delete Rule
ruleDeleted: Rule Deleted ruleDeleted: Rule Deleted
ruleEmpty: There are no rules in your repo. Click the button below to create a rule. ruleEmpty: There are no rules in your repo. Click the button below to create a rule.

View File

@ -470,7 +470,7 @@ export interface ProtectionDefApprovals {
} }
export interface ProtectionDefBypass { export interface ProtectionDefBypass {
space_owners?: boolean repo_owners?: boolean
user_ids?: number[] user_ids?: number[]
} }

View File

@ -7557,7 +7557,7 @@ components:
type: object type: object
ProtectionDefBypass: ProtectionDefBypass:
properties: properties:
space_owners: repo_owners:
type: boolean type: boolean
user_ids: user_ids:
items: items:

View File

@ -208,8 +208,8 @@ export const rulesFormInitialPayload = {
target: '', target: '',
targetDefault: true, targetDefault: true,
targetList: [] as string[][], targetList: [] as string[][],
allProjectOwners: false, allRepoOwners: false,
projectOwners: [] as string[], bypassList: [] as string[],
requireMinReviewers: false, requireMinReviewers: false,
minReviewers: '', minReviewers: '',
requireCodeOwner: false, requireCodeOwner: false,