mirror of
https://github.com/harness/drone.git
synced 2025-05-20 19:09:59 +08:00
[CODE-1116] UI : Protection rules integration with Branch Creation/Deletion and Commits (#824)
This commit is contained in:
parent
5b16c72d4a
commit
274a5a01ab
@ -56,6 +56,18 @@
|
|||||||
font-size: var(--form-input-font-size);
|
font-size: var(--form-input-font-size);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warningMessageLayout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warningMessage {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.newBranchContainer {
|
.newBranchContainer {
|
||||||
|
@ -22,3 +22,5 @@ export declare const main: string
|
|||||||
export declare const newBranch: string
|
export declare const newBranch: string
|
||||||
export declare const newBranchContainer: string
|
export declare const newBranchContainer: string
|
||||||
export declare const radioGroup: string
|
export declare const radioGroup: string
|
||||||
|
export declare const warningMessage: string
|
||||||
|
export declare const warningMessageLayout: string
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Dialog, Intent } from '@blueprintjs/core'
|
import { Dialog, Intent } from '@blueprintjs/core'
|
||||||
import * as yup from 'yup'
|
import * as yup from 'yup'
|
||||||
import {
|
import {
|
||||||
@ -22,6 +22,7 @@ import {
|
|||||||
ButtonProps,
|
ButtonProps,
|
||||||
Container,
|
Container,
|
||||||
Layout,
|
Layout,
|
||||||
|
Text,
|
||||||
FlexExpander,
|
FlexExpander,
|
||||||
Formik,
|
Formik,
|
||||||
FormikForm,
|
FormikForm,
|
||||||
@ -32,10 +33,12 @@ import {
|
|||||||
} from '@harnessio/uicore'
|
} from '@harnessio/uicore'
|
||||||
import { Icon } from '@harnessio/icons'
|
import { Icon } from '@harnessio/icons'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
import { FontVariation } from '@harnessio/design-system'
|
import { FontVariation, Color } from '@harnessio/design-system'
|
||||||
import { useMutate } from 'restful-react'
|
import { useMutate } from 'restful-react'
|
||||||
import { get } from 'lodash-es'
|
import { get } from 'lodash-es'
|
||||||
|
import { Render } from 'react-jsx-match'
|
||||||
import { useModalHook } from 'hooks/useModalHook'
|
import { useModalHook } from 'hooks/useModalHook'
|
||||||
|
import { useRuleViolationCheck } from 'hooks/useRuleViolationCheck'
|
||||||
import { String, useStrings } from 'framework/strings'
|
import { String, useStrings } from 'framework/strings'
|
||||||
import { getErrorMessage } from 'utils/Utils'
|
import { getErrorMessage } from 'utils/Utils'
|
||||||
import type { OpenapiCommitFilesRequest, TypesListCommitResponse } from 'services/code'
|
import type { OpenapiCommitFilesRequest, TypesListCommitResponse } from 'services/code'
|
||||||
@ -82,10 +85,20 @@ export function useCommitModal({
|
|||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const [targetBranchOption, setTargetBranchOption] = useState(CommitToGitRefOption.DIRECTLY)
|
const [targetBranchOption, setTargetBranchOption] = useState(CommitToGitRefOption.DIRECTLY)
|
||||||
const { showError, showSuccess } = useToaster()
|
const { showError, showSuccess } = useToaster()
|
||||||
|
const { violation, bypassable, bypassed, setAllStates, resetViolation } = useRuleViolationCheck()
|
||||||
|
const [disableCTA, setDisableCTA] = useState(false)
|
||||||
const { mutate, loading } = useMutate<TypesListCommitResponse>({
|
const { mutate, loading } = useMutate<TypesListCommitResponse>({
|
||||||
verb: 'POST',
|
verb: 'POST',
|
||||||
path: `/api/v1/repos/${repoMetadata.path}/+/commits`
|
path: `/api/v1/repos/${repoMetadata.path}/+/commits`
|
||||||
})
|
})
|
||||||
|
const { mutate: dryRunCall } = useMutate({
|
||||||
|
verb: 'POST',
|
||||||
|
path: `/api/v1/repos/${repoMetadata.path}/+/commits`
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dryRun(CommitToGitRefOption.DIRECTLY)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleSubmit = (formData: FormData) => {
|
const handleSubmit = (formData: FormData) => {
|
||||||
try {
|
try {
|
||||||
@ -101,28 +114,73 @@ export function useCommitModal({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
branch: gitRef,
|
branch: gitRef,
|
||||||
new_branch: formData.newBranch,
|
new_branch: targetBranchOption === CommitToGitRefOption.NEW_BRANCH ? formData.newBranch : '',
|
||||||
title: formData.title || commitTitlePlaceHolder,
|
title: formData.title || commitTitlePlaceHolder,
|
||||||
message: formData.message
|
message: formData.message,
|
||||||
|
bypass_rules: bypassed
|
||||||
}
|
}
|
||||||
|
|
||||||
mutate(data)
|
mutate(data)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
hideModal()
|
hideModal()
|
||||||
onSuccess(response, formData.newBranch)
|
onSuccess(response, targetBranchOption === CommitToGitRefOption.NEW_BRANCH ? formData.newBranch : '')
|
||||||
|
|
||||||
if (commitAction === GitCommitAction.DELETE) {
|
if (commitAction === GitCommitAction.DELETE) {
|
||||||
showSuccess(getString('fileDeleted').replace('__path__', resourcePath))
|
showSuccess(getString('fileDeleted').replace('__path__', resourcePath))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(_error => {
|
.catch(_error => {
|
||||||
showError(getErrorMessage(_error), 0, getString('failedToCreateRepo'))
|
if (_error.status === 422) {
|
||||||
|
setAllStates({
|
||||||
|
violation: true,
|
||||||
|
bypassed: true,
|
||||||
|
bypassable: _error?.data?.violations[0]?.bypassable
|
||||||
|
})
|
||||||
|
} else showError(getErrorMessage(_error), 0, getString('failedToCreateRepo'))
|
||||||
})
|
})
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
showError(getErrorMessage(exception), 0, getString('failedToCreateRepo'))
|
showError(getErrorMessage(exception), 0, getString('failedToCreateRepo'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dryRun = async (targetBranch: CommitToGitRefOption) => {
|
||||||
|
resetViolation()
|
||||||
|
setDisableCTA(false)
|
||||||
|
if (targetBranch === CommitToGitRefOption.DIRECTLY) {
|
||||||
|
try {
|
||||||
|
const data: OpenapiCommitFilesRequest = {
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
action: commitAction,
|
||||||
|
path: oldResourcePath || resourcePath,
|
||||||
|
payload: `${oldResourcePath ? `file://${resourcePath}\n` : ''}${payload}`,
|
||||||
|
sha
|
||||||
|
}
|
||||||
|
],
|
||||||
|
branch: gitRef,
|
||||||
|
new_branch: '',
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
bypass_rules: false,
|
||||||
|
dry_run_rules: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await dryRunCall(data)
|
||||||
|
|
||||||
|
if (response?.rule_violations?.length) {
|
||||||
|
setAllStates({
|
||||||
|
violation: true,
|
||||||
|
bypassed: false,
|
||||||
|
bypassable: response?.rule_violations[0]?.bypassable
|
||||||
|
})
|
||||||
|
setDisableCTA(!response?.rule_violations[0]?.bypassable)
|
||||||
|
}
|
||||||
|
} catch (exception) {
|
||||||
|
showError(getErrorMessage(exception), 0, getString('failedToCreateRepo'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
isOpen
|
isOpen
|
||||||
@ -186,14 +244,43 @@ export function useCommitModal({
|
|||||||
label=""
|
label=""
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
setTargetBranchOption(get(e.target, 'defaultValue') as unknown as CommitToGitRefOption)
|
setTargetBranchOption(get(e.target, 'defaultValue') as unknown as CommitToGitRefOption)
|
||||||
|
dryRun(get(e.target, 'defaultValue') as unknown as CommitToGitRefOption)
|
||||||
}}
|
}}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
label: <String stringID="commitDirectlyTo" vars={{ gitRef }} useRichText />,
|
label: (
|
||||||
|
<Layout.Horizontal className={css.warningMessageLayout}>
|
||||||
|
<String stringID="commitDirectlyTo" vars={{ gitRef }} useRichText />
|
||||||
|
<Render when={violation && targetBranchOption === CommitToGitRefOption.DIRECTLY}>
|
||||||
|
<Layout.Horizontal className={css.warningMessage}>
|
||||||
|
<Icon intent={Intent.WARNING} name="danger-icon" size={16} />
|
||||||
|
<Text font={{ variation: FontVariation.BODY2 }} color={Color.RED_800}>
|
||||||
|
{bypassable
|
||||||
|
? getString('branchProtection.commitDirectlyBlockText')
|
||||||
|
: getString('branchProtection.commitNewBranchBlockText')}
|
||||||
|
</Text>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</Render>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
),
|
||||||
value: CommitToGitRefOption.DIRECTLY
|
value: CommitToGitRefOption.DIRECTLY
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: <String stringID="commitToNewBranch" useRichText />,
|
label: (
|
||||||
|
<Layout.Horizontal className={css.warningMessageLayout}>
|
||||||
|
<String stringID="commitToNewBranch" useRichText />
|
||||||
|
<Render when={violation && targetBranchOption === CommitToGitRefOption.NEW_BRANCH}>
|
||||||
|
<Layout.Horizontal className={css.warningMessage}>
|
||||||
|
<Icon intent={Intent.WARNING} name="danger-icon" size={16} />
|
||||||
|
<Text font={{ variation: FontVariation.BODY2 }} color={Color.RED_800}>
|
||||||
|
{bypassable
|
||||||
|
? getString('branchProtection.commitNewBranchAlertText')
|
||||||
|
: getString('branchProtection.commitNewBranchBlockText')}
|
||||||
|
</Text>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</Render>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
),
|
||||||
value: CommitToGitRefOption.NEW_BRANCH
|
value: CommitToGitRefOption.NEW_BRANCH
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
@ -209,6 +296,9 @@ export function useCommitModal({
|
|||||||
dataTooltipId: 'enterNewBranchName'
|
dataTooltipId: 'enterNewBranchName'
|
||||||
}}
|
}}
|
||||||
inputGroup={{ autoFocus: true }}
|
inputGroup={{ autoFocus: true }}
|
||||||
|
onChange={() => {
|
||||||
|
setAllStates({ violation: false, bypassable: false, bypassed: false })
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
</Container>
|
</Container>
|
||||||
@ -216,12 +306,22 @@ export function useCommitModal({
|
|||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<Layout.Horizontal spacing="small" padding={{ right: 'xxlarge', top: 'xxlarge', bottom: 'large' }}>
|
<Layout.Horizontal spacing="small" padding={{ right: 'xxlarge', top: 'xxlarge', bottom: 'large' }}>
|
||||||
<Button
|
{!bypassable ? (
|
||||||
type="submit"
|
<Button
|
||||||
variation={ButtonVariation.PRIMARY}
|
type="submit"
|
||||||
text={getString('commit')}
|
variation={ButtonVariation.PRIMARY}
|
||||||
disabled={loading}
|
text={getString('commit')}
|
||||||
/>
|
disabled={loading || disableCTA}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
disabled={loading}
|
||||||
|
type="submit"
|
||||||
|
variation={ButtonVariation.SECONDARY}
|
||||||
|
text={getString('branchProtection.commitNewBranchAlertBtn')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Button text={getString('cancel')} variation={ButtonVariation.LINK} onClick={hideModal} />
|
<Button text={getString('cancel')} variation={ButtonVariation.LINK} onClick={hideModal} />
|
||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
|
|
||||||
|
@ -30,6 +30,12 @@
|
|||||||
margin-right: var(--spacing-10) !important;
|
margin-right: var(--spacing-10) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warningMessage {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
// .branchSourceDesc {
|
// .branchSourceDesc {
|
||||||
// color: var(--grey-400) !important;
|
// color: var(--grey-400) !important;
|
||||||
// font-size: var(--form-input-font-size) !important;
|
// font-size: var(--form-input-font-size) !important;
|
||||||
|
@ -24,3 +24,4 @@ export declare const maxContainer: string
|
|||||||
export declare const popoverContainer: string
|
export declare const popoverContainer: string
|
||||||
export declare const selectContainer: string
|
export declare const selectContainer: string
|
||||||
export declare const title: string
|
export declare const title: string
|
||||||
|
export declare const warningMessage: string
|
||||||
|
@ -29,13 +29,15 @@ import {
|
|||||||
useToaster,
|
useToaster,
|
||||||
FormInput,
|
FormInput,
|
||||||
Label,
|
Label,
|
||||||
|
Text,
|
||||||
ButtonVariation,
|
ButtonVariation,
|
||||||
StringSubstitute
|
StringSubstitute
|
||||||
} from '@harnessio/uicore'
|
} from '@harnessio/uicore'
|
||||||
import { Icon } from '@harnessio/icons'
|
import { Icon } from '@harnessio/icons'
|
||||||
import { FontVariation } from '@harnessio/design-system'
|
import { FontVariation, Color } from '@harnessio/design-system'
|
||||||
import { useMutate } from 'restful-react'
|
import { useMutate } from 'restful-react'
|
||||||
import { get } from 'lodash-es'
|
import { get } from 'lodash-es'
|
||||||
|
import { Render } from 'react-jsx-match'
|
||||||
import { useModalHook } from 'hooks/useModalHook'
|
import { useModalHook } from 'hooks/useModalHook'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { getErrorMessage, permissionProps } from 'utils/Utils'
|
import { getErrorMessage, permissionProps } from 'utils/Utils'
|
||||||
@ -43,6 +45,7 @@ import { GitInfoProps, normalizeGitRef, isGitBranchNameValid } from 'utils/GitUt
|
|||||||
import { BranchTagSelect } from 'components/BranchTagSelect/BranchTagSelect'
|
import { BranchTagSelect } from 'components/BranchTagSelect/BranchTagSelect'
|
||||||
import type { RepoBranch } from 'services/code'
|
import type { RepoBranch } from 'services/code'
|
||||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||||
|
import { useRuleViolationCheck } from 'hooks/useRuleViolationCheck'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
import css from './CreateBranchModal.module.scss'
|
import css from './CreateBranchModal.module.scss'
|
||||||
|
|
||||||
@ -79,6 +82,7 @@ export function useCreateBranchModal({
|
|||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const [sourceBranch, setSourceBranch] = useState(suggestedSourceBranch || (repoMetadata.default_branch as string))
|
const [sourceBranch, setSourceBranch] = useState(suggestedSourceBranch || (repoMetadata.default_branch as string))
|
||||||
const { showError, showSuccess } = useToaster()
|
const { showError, showSuccess } = useToaster()
|
||||||
|
const { violation, bypassable, bypassed, setAllStates } = useRuleViolationCheck()
|
||||||
const { mutate: createBranch, loading } = useMutate<RepoBranch>({
|
const { mutate: createBranch, loading } = useMutate<RepoBranch>({
|
||||||
verb: 'POST',
|
verb: 'POST',
|
||||||
path: `/api/v1/repos/${repoMetadata.path}/+/branches`
|
path: `/api/v1/repos/${repoMetadata.path}/+/branches`
|
||||||
@ -88,7 +92,8 @@ export function useCreateBranchModal({
|
|||||||
try {
|
try {
|
||||||
createBranch({
|
createBranch({
|
||||||
name,
|
name,
|
||||||
target: normalizeGitRef(refIsATag ? `refs/tags/${sourceBranch}` : sourceBranch)
|
target: normalizeGitRef(refIsATag ? `refs/tags/${sourceBranch}` : sourceBranch),
|
||||||
|
bypass_rules: bypassed
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
hideModal()
|
hideModal()
|
||||||
@ -106,7 +111,13 @@ export function useCreateBranchModal({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(_error => {
|
.catch(_error => {
|
||||||
showError(getErrorMessage(_error), 0, 'failedToCreateBranch')
|
if (_error.status === 422) {
|
||||||
|
setAllStates({
|
||||||
|
violation: true,
|
||||||
|
bypassed: true,
|
||||||
|
bypassable: _error?.data?.violations[0]?.bypassable
|
||||||
|
})
|
||||||
|
} else showError(getErrorMessage(_error), 0, 'failedToCreateBranch')
|
||||||
})
|
})
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
showError(getErrorMessage(exception), 0, 'failedToCreateBranch')
|
showError(getErrorMessage(exception), 0, 'failedToCreateBranch')
|
||||||
@ -154,6 +165,9 @@ export function useCreateBranchModal({
|
|||||||
dataTooltipId: 'repositoryBranchTextField'
|
dataTooltipId: 'repositoryBranchTextField'
|
||||||
}}
|
}}
|
||||||
inputGroup={{ autoFocus: true }}
|
inputGroup={{ autoFocus: true }}
|
||||||
|
onChange={() => {
|
||||||
|
setAllStates({ violation: false, bypassable: false, bypassed: false })
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Container margin={{ top: 'medium' }}>
|
<Container margin={{ top: 'medium' }}>
|
||||||
<Label className={css.label}>{getString('basedOn')}</Label>
|
<Label className={css.label}>{getString('basedOn')}</Label>
|
||||||
@ -176,17 +190,37 @@ export function useCreateBranchModal({
|
|||||||
spacing="small"
|
spacing="small"
|
||||||
padding={{ right: 'xxlarge', top: 'xxlarge', bottom: 'large' }}
|
padding={{ right: 'xxlarge', top: 'xxlarge', bottom: 'large' }}
|
||||||
style={{ alignItems: 'center' }}>
|
style={{ alignItems: 'center' }}>
|
||||||
<Button
|
{!bypassable ? (
|
||||||
type="submit"
|
<Button
|
||||||
text={getString('createBranch')}
|
type="submit"
|
||||||
variation={ButtonVariation.PRIMARY}
|
text={getString('createBranch')}
|
||||||
disabled={loading}
|
variation={ButtonVariation.PRIMARY}
|
||||||
/>
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
disabled={loading}
|
||||||
|
type="submit"
|
||||||
|
variation={ButtonVariation.SECONDARY}
|
||||||
|
text={getString('branchProtection.createBranchAlertBtn')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Button text={getString('cancel')} variation={ButtonVariation.LINK} onClick={hideModal} />
|
<Button text={getString('cancel')} variation={ButtonVariation.LINK} onClick={hideModal} />
|
||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
|
|
||||||
{loading && <Icon intent={Intent.PRIMARY} name="steps-spinner" size={16} />}
|
{loading && <Icon intent={Intent.PRIMARY} name="steps-spinner" size={16} />}
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
|
<Render when={violation}>
|
||||||
|
<Layout.Horizontal className={css.warningMessage}>
|
||||||
|
<Icon intent={Intent.WARNING} name="danger-icon" size={16} />
|
||||||
|
<Text font={{ variation: FontVariation.BODY2 }} color={Color.RED_800}>
|
||||||
|
{bypassable
|
||||||
|
? getString('branchProtection.createBranchAlertText')
|
||||||
|
: getString('branchProtection.createBranchBlockText')}
|
||||||
|
</Text>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</Render>
|
||||||
</FormikForm>
|
</FormikForm>
|
||||||
</Formik>
|
</Formik>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -54,9 +54,19 @@ export interface StringsMap {
|
|||||||
'branchProtection.blockBranchDeletion': string
|
'branchProtection.blockBranchDeletion': string
|
||||||
'branchProtection.blockBranchDeletionText': string
|
'branchProtection.blockBranchDeletionText': string
|
||||||
'branchProtection.bypassList': string
|
'branchProtection.bypassList': string
|
||||||
|
'branchProtection.commitDirectlyBlockText': string
|
||||||
|
'branchProtection.commitNewBranchAlertBtn': string
|
||||||
|
'branchProtection.commitNewBranchAlertText': string
|
||||||
|
'branchProtection.commitNewBranchBlockText': string
|
||||||
'branchProtection.create': string
|
'branchProtection.create': string
|
||||||
|
'branchProtection.createBranchAlertBtn': string
|
||||||
|
'branchProtection.createBranchAlertText': string
|
||||||
|
'branchProtection.createBranchBlockText': string
|
||||||
'branchProtection.createRule': string
|
'branchProtection.createRule': string
|
||||||
'branchProtection.defaultBranch': string
|
'branchProtection.defaultBranch': string
|
||||||
|
'branchProtection.deleteBranchAlertBtn': string
|
||||||
|
'branchProtection.deleteBranchAlertText': string
|
||||||
|
'branchProtection.deleteBranchBlockText': string
|
||||||
'branchProtection.deleteProtectionRule': string
|
'branchProtection.deleteProtectionRule': string
|
||||||
'branchProtection.deleteRule': string
|
'branchProtection.deleteRule': string
|
||||||
'branchProtection.deleteText': string
|
'branchProtection.deleteText': string
|
||||||
|
@ -24,22 +24,25 @@ import { useConfirmationDialog } from './useConfirmationDialog'
|
|||||||
|
|
||||||
export interface UseConfirmActionDialogProps {
|
export interface UseConfirmActionDialogProps {
|
||||||
message: React.ReactElement
|
message: React.ReactElement
|
||||||
|
childtag?: React.ReactElement
|
||||||
intent?: Intent
|
intent?: Intent
|
||||||
title?: string
|
title?: string
|
||||||
confirmText?: string
|
confirmText?: string
|
||||||
cancelText?: string
|
cancelText?: string
|
||||||
action: (params?: Unknown) => void
|
action: (params?: Unknown) => void
|
||||||
|
persistDialog?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use useConfirmAct() hook instead
|
* @deprecated Use useConfirmAct() hook instead
|
||||||
*/
|
*/
|
||||||
export const useConfirmAction = (props: UseConfirmActionDialogProps) => {
|
export const useConfirmAction = (props: UseConfirmActionDialogProps) => {
|
||||||
const { title, message, confirmText, cancelText, intent, action } = props
|
const { title, message, confirmText, cancelText, intent, childtag, action, persistDialog } = props
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const [params, setParams] = useState<Unknown>()
|
const [params, setParams] = useState<Unknown>()
|
||||||
const { openDialog } = useConfirmationDialog({
|
const { openDialog } = useConfirmationDialog({
|
||||||
intent,
|
intent,
|
||||||
|
persistDialog: persistDialog,
|
||||||
titleText: title || getString('confirmation'),
|
titleText: title || getString('confirmation'),
|
||||||
contentText: message,
|
contentText: message,
|
||||||
confirmButtonText: confirmText || getString('confirm'),
|
confirmButtonText: confirmText || getString('confirm'),
|
||||||
@ -49,7 +52,8 @@ export const useConfirmAction = (props: UseConfirmActionDialogProps) => {
|
|||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
action(params)
|
action(params)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
children: childtag || <></>
|
||||||
})
|
})
|
||||||
const confirm = useCallback(
|
const confirm = useCallback(
|
||||||
(_params?: Unknown) => {
|
(_params?: Unknown) => {
|
||||||
|
@ -33,6 +33,7 @@ export interface UseConfirmationDialogProps {
|
|||||||
canEscapeKeyClose?: boolean
|
canEscapeKeyClose?: boolean
|
||||||
children?: JSX.Element
|
children?: JSX.Element
|
||||||
className?: string
|
className?: string
|
||||||
|
persistDialog?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UseConfirmationDialogReturn {
|
export interface UseConfirmationDialogReturn {
|
||||||
@ -54,7 +55,8 @@ export const useConfirmationDialog = (props: UseConfirmationDialogProps): UseCon
|
|||||||
canOutsideClickClose,
|
canOutsideClickClose,
|
||||||
canEscapeKeyClose,
|
canEscapeKeyClose,
|
||||||
children,
|
children,
|
||||||
className
|
className,
|
||||||
|
persistDialog
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const [showModal, hideModal] = useModalHook(() => {
|
const [showModal, hideModal] = useModalHook(() => {
|
||||||
@ -81,9 +83,10 @@ export const useConfirmationDialog = (props: UseConfirmationDialogProps): UseCon
|
|||||||
const onClose = React.useCallback(
|
const onClose = React.useCallback(
|
||||||
(isConfirmed: boolean): void => {
|
(isConfirmed: boolean): void => {
|
||||||
onCloseDialog?.(isConfirmed)
|
onCloseDialog?.(isConfirmed)
|
||||||
hideModal()
|
if (!isConfirmed) hideModal()
|
||||||
|
else if (persistDialog) showModal()
|
||||||
},
|
},
|
||||||
[hideModal, onCloseDialog]
|
[hideModal, onCloseDialog, persistDialog]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
68
web/src/hooks/useRuleViolationCheck.tsx
Normal file
68
web/src/hooks/useRuleViolationCheck.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { useReducer } from 'react'
|
||||||
|
|
||||||
|
enum ActionTypes {
|
||||||
|
SET_VIOLATION = 'SET_VIOLATION',
|
||||||
|
SET_BYPASSED = 'SET_BYPASSED',
|
||||||
|
SET_BYPASSABLE = 'SET_BYPASSABLE',
|
||||||
|
SET_ALL_STATES = 'SET_ALL_STATES'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ViolationState {
|
||||||
|
violation: boolean
|
||||||
|
bypassable: boolean
|
||||||
|
bypassed: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type ViolationAction =
|
||||||
|
| { type: ActionTypes.SET_VIOLATION; payload: boolean }
|
||||||
|
| { type: ActionTypes.SET_BYPASSED; payload: boolean }
|
||||||
|
| { type: ActionTypes.SET_BYPASSABLE; payload: boolean }
|
||||||
|
| { type: ActionTypes.SET_ALL_STATES; payload: Partial<ViolationState> }
|
||||||
|
|
||||||
|
const initialState: ViolationState = {
|
||||||
|
violation: false,
|
||||||
|
bypassable: false,
|
||||||
|
bypassed: false
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducer = (state: ViolationState, action: ViolationAction): ViolationState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionTypes.SET_VIOLATION:
|
||||||
|
return { ...state, violation: action.payload }
|
||||||
|
case ActionTypes.SET_BYPASSABLE:
|
||||||
|
return { ...state, bypassed: action.payload }
|
||||||
|
case ActionTypes.SET_BYPASSED:
|
||||||
|
return { ...state, bypassable: action.payload }
|
||||||
|
case ActionTypes.SET_ALL_STATES:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
violation: action.payload.violation !== undefined ? action.payload.violation : state.violation,
|
||||||
|
bypassable: action.payload.bypassable !== undefined ? action.payload.bypassable : state.bypassable,
|
||||||
|
bypassed: action.payload.bypassed !== undefined ? action.payload.bypassed : state.bypassed
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useRuleViolationCheck = () => {
|
||||||
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
|
const setViolation = (value: boolean) => dispatch({ type: ActionTypes.SET_VIOLATION, payload: value })
|
||||||
|
const setBypassable = (value: boolean) => dispatch({ type: ActionTypes.SET_BYPASSABLE, payload: value })
|
||||||
|
const setBypassed = (value: boolean) => dispatch({ type: ActionTypes.SET_BYPASSED, payload: value })
|
||||||
|
const setAllStates = (payload: Partial<ViolationState>) => {
|
||||||
|
dispatch({ type: ActionTypes.SET_ALL_STATES, payload })
|
||||||
|
}
|
||||||
|
const resetViolation = () => setAllStates({ violation: false, bypassable: false, bypassed: false })
|
||||||
|
|
||||||
|
return {
|
||||||
|
violation: state.violation,
|
||||||
|
setViolation,
|
||||||
|
bypassable: state.bypassable,
|
||||||
|
setBypassable,
|
||||||
|
bypassed: state.bypassed,
|
||||||
|
setBypassed,
|
||||||
|
setAllStates,
|
||||||
|
resetViolation
|
||||||
|
}
|
||||||
|
}
|
@ -913,6 +913,16 @@ branchProtection:
|
|||||||
mergePrAlertTitle: Merge pull request alert
|
mergePrAlertTitle: Merge pull request alert
|
||||||
mergePrAlertText: 'Merge cannot be completed. {{ruleCount}} branch rules failed: '
|
mergePrAlertText: 'Merge cannot be completed. {{ruleCount}} branch rules failed: '
|
||||||
mergeCheckboxAlert: Bypass branch rules and merge
|
mergeCheckboxAlert: Bypass branch rules and merge
|
||||||
|
createBranchAlertBtn: Bypass rules and create branch
|
||||||
|
createBranchAlertText: Some rules will be bypassed by creating branch
|
||||||
|
createBranchBlockText: Some rules don't allow you to create branch
|
||||||
|
deleteBranchAlertBtn: Bypass rule and confirm delete
|
||||||
|
deleteBranchAlertText: Some rules will be bypassed while deleting branch
|
||||||
|
deleteBranchBlockText: Some rules don't allow you to delete branch
|
||||||
|
commitNewBranchAlertBtn: Bypass rules and commit via new branch
|
||||||
|
commitNewBranchAlertText: Some rules will be bypassed to commit by creating branch
|
||||||
|
commitNewBranchBlockText: Some rules don't allow you to create new branch for commit
|
||||||
|
commitDirectlyBlockText: Some rules don't allow you to commit directly
|
||||||
codeOwner:
|
codeOwner:
|
||||||
title: Code Owner
|
title: Code Owner
|
||||||
changesRequested: '{count} {count|1:change,changes} requested'
|
changesRequested: '{count} {count|1:change,changes} requested'
|
||||||
|
@ -80,6 +80,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warningMessage {
|
||||||
|
order: 3;
|
||||||
|
display: flex !important;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.popover {
|
.popover {
|
||||||
:global {
|
:global {
|
||||||
.bp3-popover-content {
|
.bp3-popover-content {
|
||||||
|
@ -25,3 +25,4 @@ export declare const row: string
|
|||||||
export declare const rowText: string
|
export declare const rowText: string
|
||||||
export declare const spacer: string
|
export declare const spacer: string
|
||||||
export declare const table: string
|
export declare const table: string
|
||||||
|
export declare const warningMessage: string
|
||||||
|
@ -18,6 +18,7 @@ import React, { useEffect, useMemo, useState } from 'react'
|
|||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
TableV2 as Table,
|
TableV2 as Table,
|
||||||
|
Layout,
|
||||||
Text,
|
Text,
|
||||||
Avatar,
|
Avatar,
|
||||||
Tag,
|
Tag,
|
||||||
@ -25,8 +26,10 @@ import {
|
|||||||
StringSubstitute,
|
StringSubstitute,
|
||||||
useIsMounted
|
useIsMounted
|
||||||
} from '@harnessio/uicore'
|
} from '@harnessio/uicore'
|
||||||
|
import { Icon } from '@harnessio/icons'
|
||||||
import { noop } from 'lodash-es'
|
import { noop } from 'lodash-es'
|
||||||
import { Color, Intent } from '@harnessio/design-system'
|
import { Color, Intent, FontVariation } from '@harnessio/design-system'
|
||||||
|
import { Render } from 'react-jsx-match'
|
||||||
import type { CellProps, Column } from 'react-table'
|
import type { CellProps, Column } from 'react-table'
|
||||||
import { Link, useHistory } from 'react-router-dom'
|
import { Link, useHistory } from 'react-router-dom'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
@ -43,6 +46,7 @@ import type {
|
|||||||
import { CommitActions } from 'components/CommitActions/CommitActions'
|
import { CommitActions } from 'components/CommitActions/CommitActions'
|
||||||
import { formatDate, getErrorMessage } from 'utils/Utils'
|
import { formatDate, getErrorMessage } from 'utils/Utils'
|
||||||
import { useConfirmAction } from 'hooks/useConfirmAction'
|
import { useConfirmAction } from 'hooks/useConfirmAction'
|
||||||
|
import { useRuleViolationCheck } from 'hooks/useRuleViolationCheck'
|
||||||
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
||||||
import { CommitDivergence } from 'components/CommitDivergence/CommitDivergence'
|
import { CommitDivergence } from 'components/CommitDivergence/CommitDivergence'
|
||||||
import { makeDiffRefs } from 'utils/GitUtils'
|
import { makeDiffRefs } from 'utils/GitUtils'
|
||||||
@ -167,19 +171,23 @@ export function BranchesContent({ repoMetadata, searchTerm = '', branches, onDel
|
|||||||
id: 'action',
|
id: 'action',
|
||||||
width: '30px',
|
width: '30px',
|
||||||
Cell: ({ row }: CellProps<RepoBranch>) => {
|
Cell: ({ row }: CellProps<RepoBranch>) => {
|
||||||
|
const { violation, bypassable, bypassed, setAllStates } = useRuleViolationCheck()
|
||||||
|
const [persistModal, setPersistModal] = useState(true)
|
||||||
const { mutate: deleteBranch } = useMutate({
|
const { mutate: deleteBranch } = useMutate({
|
||||||
verb: 'DELETE',
|
verb: 'DELETE',
|
||||||
path: `/api/v1/repos/${repoMetadata.path}/+/branches/${row.original.name}`
|
path: `/api/v1/repos/${repoMetadata.path}/+/branches/${row.original.name}?bypass_rules=${bypassed}`
|
||||||
})
|
})
|
||||||
const { showSuccess, showError } = useToaster()
|
const { showSuccess, showError } = useToaster()
|
||||||
const confirmDeleteBranch = useConfirmAction({
|
const confirmDeleteBranch = useConfirmAction({
|
||||||
title: getString('deleteBranch'),
|
title: getString('deleteBranch'),
|
||||||
confirmText: getString('delete'),
|
confirmText: !bypassable ? getString('delete') : getString('branchProtection.deleteBranchAlertBtn'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
message: <String useRichText stringID="deleteBranchConfirm" vars={{ name: row.original.name }} />,
|
message: <String useRichText stringID="deleteBranchConfirm" vars={{ name: row.original.name }} />,
|
||||||
|
persistDialog: persistModal,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
deleteBranch({})
|
deleteBranch({})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
setPersistModal(false)
|
||||||
showSuccess(
|
showSuccess(
|
||||||
<StringSubstitute
|
<StringSubstitute
|
||||||
str={getString('branchDeleted')}
|
str={getString('branchDeleted')}
|
||||||
@ -192,9 +200,27 @@ export function BranchesContent({ repoMetadata, searchTerm = '', branches, onDel
|
|||||||
onDeleteSuccess()
|
onDeleteSuccess()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
showError(getErrorMessage(error), 0, 'failedToDeleteBranch')
|
if (error.status === 422) {
|
||||||
|
setAllStates({
|
||||||
|
violation: true,
|
||||||
|
bypassed: true,
|
||||||
|
bypassable: error?.data?.violations[0]?.bypassable
|
||||||
|
})
|
||||||
|
} else showError(getErrorMessage(error), 0, 'failedToDeleteBranch')
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
childtag: (
|
||||||
|
<Render when={violation}>
|
||||||
|
<Layout.Horizontal className={css.warningMessage}>
|
||||||
|
<Icon intent={Intent.WARNING} name="danger-icon" size={16} />
|
||||||
|
<Text font={{ variation: FontVariation.BODY2 }} color={Color.RED_800}>
|
||||||
|
{bypassable
|
||||||
|
? getString('branchProtection.deleteBranchAlertText')
|
||||||
|
: getString('branchProtection.deleteBranchBlockText')}
|
||||||
|
</Text>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</Render>
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -184,6 +184,8 @@ export interface OpenapiCommitFilesRequest {
|
|||||||
message?: string
|
message?: string
|
||||||
new_branch?: string
|
new_branch?: string
|
||||||
title?: string
|
title?: string
|
||||||
|
bypass_rules?: boolean
|
||||||
|
dry_run_rules?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OpenapiContent = RepoFileContent | OpenapiDirContent | RepoSymlinkContent | RepoSubmoduleContent
|
export type OpenapiContent = RepoFileContent | OpenapiDirContent | RepoSymlinkContent | RepoSubmoduleContent
|
||||||
|
@ -867,7 +867,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/GitrpcBlamePart'
|
$ref: '#/components/schemas/GitBlamePart'
|
||||||
type: array
|
type: array
|
||||||
description: OK
|
description: OK
|
||||||
'401':
|
'401':
|
||||||
@ -1047,6 +1047,13 @@ paths:
|
|||||||
delete:
|
delete:
|
||||||
operationId: deleteBranch
|
operationId: deleteBranch
|
||||||
parameters:
|
parameters:
|
||||||
|
- description: Bypass rule violations if possible.
|
||||||
|
in: query
|
||||||
|
name: bypass_rules
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
- in: path
|
- in: path
|
||||||
name: repo_ref
|
name: repo_ref
|
||||||
required: true
|
required: true
|
||||||
@ -1814,7 +1821,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/GitrpcFileDiff'
|
$ref: '#/components/schemas/GitFileDiff'
|
||||||
type: array
|
type: array
|
||||||
text/plain:
|
text/plain:
|
||||||
schema:
|
schema:
|
||||||
@ -4413,6 +4420,8 @@ paths:
|
|||||||
$ref: '#/components/schemas/EnumRuleState'
|
$ref: '#/components/schemas/EnumRuleState'
|
||||||
type:
|
type:
|
||||||
$ref: '#/components/schemas/OpenapiRuleType'
|
$ref: '#/components/schemas/OpenapiRuleType'
|
||||||
|
uid:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@ -4648,6 +4657,13 @@ paths:
|
|||||||
delete:
|
delete:
|
||||||
operationId: deleteTag
|
operationId: deleteTag
|
||||||
parameters:
|
parameters:
|
||||||
|
- description: Bypass rule violations if possible.
|
||||||
|
in: query
|
||||||
|
name: bypass_rules
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
- in: path
|
- in: path
|
||||||
name: repo_ref
|
name: repo_ref
|
||||||
required: true
|
required: true
|
||||||
@ -6899,8 +6915,8 @@ components:
|
|||||||
EnumMergeMethod:
|
EnumMergeMethod:
|
||||||
enum:
|
enum:
|
||||||
- merge
|
- merge
|
||||||
- squash
|
|
||||||
- rebase
|
- rebase
|
||||||
|
- squash
|
||||||
type: string
|
type: string
|
||||||
EnumParentResourceType:
|
EnumParentResourceType:
|
||||||
enum:
|
enum:
|
||||||
@ -7000,22 +7016,22 @@ components:
|
|||||||
- tag_deleted
|
- tag_deleted
|
||||||
- tag_updated
|
- tag_updated
|
||||||
type: string
|
type: string
|
||||||
GitrpcBlamePart:
|
GitBlamePart:
|
||||||
properties:
|
properties:
|
||||||
commit:
|
commit:
|
||||||
$ref: '#/components/schemas/GitrpcCommit'
|
$ref: '#/components/schemas/GitCommit'
|
||||||
lines:
|
lines:
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
GitrpcCommit:
|
GitCommit:
|
||||||
properties:
|
properties:
|
||||||
author:
|
author:
|
||||||
$ref: '#/components/schemas/GitrpcSignature'
|
$ref: '#/components/schemas/GitSignature'
|
||||||
committer:
|
committer:
|
||||||
$ref: '#/components/schemas/GitrpcSignature'
|
$ref: '#/components/schemas/GitSignature'
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
sha:
|
sha:
|
||||||
@ -7023,14 +7039,14 @@ components:
|
|||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
GitrpcFileAction:
|
GitFileAction:
|
||||||
enum:
|
enum:
|
||||||
- CREATE
|
- CREATE
|
||||||
- UPDATE
|
- UPDATE
|
||||||
- DELETE
|
- DELETE
|
||||||
- MOVE
|
- MOVE
|
||||||
type: string
|
type: string
|
||||||
GitrpcFileDiff:
|
GitFileDiff:
|
||||||
properties:
|
properties:
|
||||||
additions:
|
additions:
|
||||||
type: integer
|
type: integer
|
||||||
@ -7056,30 +7072,30 @@ components:
|
|||||||
sha:
|
sha:
|
||||||
type: string
|
type: string
|
||||||
status:
|
status:
|
||||||
$ref: '#/components/schemas/GitrpcFileDiffStatus'
|
$ref: '#/components/schemas/GitFileDiffStatus'
|
||||||
type: object
|
type: object
|
||||||
GitrpcFileDiffStatus:
|
GitFileDiffStatus:
|
||||||
type: string
|
type: string
|
||||||
GitrpcIdentity:
|
GitIdentity:
|
||||||
properties:
|
properties:
|
||||||
email:
|
email:
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
GitrpcPathDetails:
|
GitPathDetails:
|
||||||
properties:
|
properties:
|
||||||
last_commit:
|
last_commit:
|
||||||
$ref: '#/components/schemas/GitrpcCommit'
|
$ref: '#/components/schemas/GitCommit'
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
size:
|
size:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
GitrpcSignature:
|
GitSignature:
|
||||||
properties:
|
properties:
|
||||||
identity:
|
identity:
|
||||||
$ref: '#/components/schemas/GitrpcIdentity'
|
$ref: '#/components/schemas/GitIdentity'
|
||||||
when:
|
when:
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
@ -7191,6 +7207,10 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
branch:
|
branch:
|
||||||
type: string
|
type: string
|
||||||
|
bypass_rules:
|
||||||
|
type: boolean
|
||||||
|
dry_run_rules:
|
||||||
|
type: boolean
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
new_branch:
|
new_branch:
|
||||||
@ -7227,6 +7247,8 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
OpenapiCreateBranchRequest:
|
OpenapiCreateBranchRequest:
|
||||||
properties:
|
properties:
|
||||||
|
bypass_rules:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
target:
|
target:
|
||||||
@ -7318,6 +7340,8 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
OpenapiCreateTagRequest:
|
OpenapiCreateTagRequest:
|
||||||
properties:
|
properties:
|
||||||
|
bypass_rules:
|
||||||
|
type: boolean
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
@ -7477,8 +7501,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
decision:
|
decision:
|
||||||
$ref: '#/components/schemas/EnumPullReqReviewDecision'
|
$ref: '#/components/schemas/EnumPullReqReviewDecision'
|
||||||
message:
|
|
||||||
type: string
|
|
||||||
type: object
|
type: object
|
||||||
OpenapiReviewerAddPullReqRequest:
|
OpenapiReviewerAddPullReqRequest:
|
||||||
properties:
|
properties:
|
||||||
@ -7518,8 +7540,6 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
is_draft:
|
is_draft:
|
||||||
type: boolean
|
type: boolean
|
||||||
message:
|
|
||||||
type: string
|
|
||||||
state:
|
state:
|
||||||
$ref: '#/components/schemas/EnumPullReqState'
|
$ref: '#/components/schemas/EnumPullReqState'
|
||||||
type: object
|
type: object
|
||||||
@ -7794,7 +7814,7 @@ components:
|
|||||||
RepoCommitFileAction:
|
RepoCommitFileAction:
|
||||||
properties:
|
properties:
|
||||||
action:
|
action:
|
||||||
$ref: '#/components/schemas/GitrpcFileAction'
|
$ref: '#/components/schemas/GitFileAction'
|
||||||
encoding:
|
encoding:
|
||||||
$ref: '#/components/schemas/EnumContentEncodingType'
|
$ref: '#/components/schemas/EnumContentEncodingType'
|
||||||
path:
|
path:
|
||||||
@ -7861,7 +7881,7 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
details:
|
details:
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/GitrpcPathDetails'
|
$ref: '#/components/schemas/GitPathDetails'
|
||||||
nullable: true
|
nullable: true
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
@ -7994,6 +8014,12 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
commit_id:
|
commit_id:
|
||||||
type: string
|
type: string
|
||||||
|
dry_run_rules:
|
||||||
|
type: boolean
|
||||||
|
rule_violations:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TypesRuleViolations'
|
||||||
|
type: array
|
||||||
type: object
|
type: object
|
||||||
TypesConnector:
|
TypesConnector:
|
||||||
properties:
|
properties:
|
||||||
@ -8159,6 +8185,10 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
TypesMergeResponse:
|
TypesMergeResponse:
|
||||||
properties:
|
properties:
|
||||||
|
allowed_methods:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/EnumMergeMethod'
|
||||||
|
type: array
|
||||||
branch_deleted:
|
branch_deleted:
|
||||||
type: boolean
|
type: boolean
|
||||||
conflict_files:
|
conflict_files:
|
||||||
@ -8457,6 +8487,8 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
TypesRuleViolations:
|
TypesRuleViolations:
|
||||||
properties:
|
properties:
|
||||||
|
bypassable:
|
||||||
|
type: boolean
|
||||||
bypassed:
|
bypassed:
|
||||||
type: boolean
|
type: boolean
|
||||||
rule:
|
rule:
|
||||||
|
Loading…
Reference in New Issue
Block a user