Skip to content

Commit 51e337c

Browse files
Merge pull request #2613 from devtron-labs/feat/skip-hibernated-deploy
feat: add support for skipping hibernated apps in bulk deploy
2 parents de1c231 + 801a0a1 commit 51e337c

File tree

11 files changed

+146
-63
lines changed

11 files changed

+146
-63
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.11.0",
7+
"@devtron-labs/devtron-fe-common-lib": "1.11.0-pre-1",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",

src/components/ApplicationGroup/AppGroup.types.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export interface BulkCDTriggerType extends BulkRuntimeParamsType {
133133
appList: BulkCDDetailType[]
134134
closePopup: (e) => void
135135
updateBulkInputMaterial: (materialList: Record<string, any>) => void
136-
onClickTriggerBulkCD: (appsToRetry?: Record<string, boolean>) => void
136+
onClickTriggerBulkCD: (skipIfHibernated: boolean, appsToRetry?: Record<string, boolean>) => void
137137
changeTab?: (
138138
materrialId: string | number,
139139
artifactId: number,
@@ -185,10 +185,21 @@ export interface TriggerResponseModalBodyProps {
185185
envName?: string
186186
}
187187

188-
export interface TriggerResponseModalFooterProps extends Pick<TriggerResponseModalBodyProps, 'isLoading' | 'responseList'> {
189-
onClickRetryBuild: (appsToRetry: Record<string, boolean>) => void
188+
type RetryFailedType =
189+
| {
190+
onClickRetryDeploy: BulkCDTriggerType['onClickTriggerBulkCD']
191+
skipHibernatedApps: boolean
192+
onClickRetryBuild?: never
193+
}
194+
| {
195+
onClickRetryDeploy?: never
196+
skipHibernatedApps?: never
197+
onClickRetryBuild: (appsToRetry: Record<string, boolean>) => void
198+
}
199+
200+
export type TriggerResponseModalFooterProps = Pick<TriggerResponseModalBodyProps, 'isLoading' | 'responseList'> & {
190201
closePopup: (e) => void
191-
}
202+
} & RetryFailedType
192203

193204
export interface TriggerModalRowType {
194205
rowData: ResponseRowType

src/components/ApplicationGroup/Constants.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,28 @@ export const BULK_VIRTUAL_RESPONSE_STATUS = {
6969
[BulkResponseStatus.PASS]: 'Succeeded',
7070
[BulkResponseStatus.FAIL]: 'Failed',
7171
[BulkResponseStatus.UNAUTHORIZE]: 'Not authorised',
72+
[BulkResponseStatus.SKIP]: 'Skipped',
7273
}
7374

7475
export const BULK_CI_RESPONSE_STATUS_TEXT = {
7576
[BulkResponseStatus.PASS]: 'Build triggered',
7677
[BulkResponseStatus.FAIL]: 'Build not triggered',
7778
[BulkResponseStatus.UNAUTHORIZE]: 'Not authorized',
79+
[BulkResponseStatus.SKIP]: 'Skipped',
7880
}
7981

8082
export const BULK_CD_RESPONSE_STATUS_TEXT = {
8183
[BulkResponseStatus.PASS]: 'Deployment triggered',
8284
[BulkResponseStatus.FAIL]: 'Deployment not triggered',
8385
[BulkResponseStatus.UNAUTHORIZE]: 'Not authorized',
86+
[BulkResponseStatus.SKIP]: 'Skipped',
8487
}
8588

8689
export const responseListOrder = {
8790
[BulkResponseStatus.FAIL]: 0,
8891
[BulkResponseStatus.UNAUTHORIZE]: 1,
89-
[BulkResponseStatus.PASS]: 2,
92+
[BulkResponseStatus.SKIP]: 2,
93+
[BulkResponseStatus.PASS]: 3,
9094
}
9195

9296
export const BULK_HIBERNATE_ERROR_MESSAGE = {

src/components/ApplicationGroup/Details/TriggerView/BulkCDTrigger.tsx

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
CDMaterialResponseType,
2020
DeploymentNodeType,
2121
Drawer,
22-
Progressing,
2322
ReleaseTag,
2423
ImageComment,
2524
showError,
@@ -48,9 +47,12 @@ import {
4847
Button,
4948
ComponentSizeType,
5049
AnimatedDeployButton,
50+
ButtonVariantType,
51+
Icon,
52+
ButtonStyleType,
53+
useMainContext,
5154
} from '@devtron-labs/devtron-fe-common-lib'
5255
import { useHistory, useLocation } from 'react-router-dom'
53-
import { ReactComponent as Close } from '@Icons/ic-cross.svg'
5456
import { ReactComponent as DeployIcon } from '@Icons/ic-nav-rocket.svg'
5557
import { ReactComponent as PlayIcon } from '@Icons/ic-play-outline.svg'
5658
import { ReactComponent as Error } from '@Icons/ic-warning.svg'
@@ -89,6 +91,7 @@ const validateRuntimeParameters = importComponentFromFELibrary(
8991
() => ({ isValid: true, cellError: {} }),
9092
'function',
9193
)
94+
const SkipHibernatedCheckbox = importComponentFromFELibrary('SkipHibernatedCheckbox', null, 'function')
9295

9396
// TODO: Fix release tags selection
9497
export default function BulkCDTrigger({
@@ -109,6 +112,7 @@ export default function BulkCDTrigger({
109112
runtimeParamsErrorState,
110113
setRuntimeParamsErrorState,
111114
}: BulkCDTriggerType) {
115+
const { canFetchHelmAppStatus } = useMainContext()
112116
const [selectedApp, setSelectedApp] = useState<BulkCDDetailType>(
113117
appList.find((app) => !app.warningMessage) || appList[0],
114118
)
@@ -125,6 +129,7 @@ export default function BulkCDTrigger({
125129
const [isPartialActionAllowed, setIsPartialActionAllowed] = useState(false)
126130
const [showResistanceBox, setShowResistanceBox] = useState(false)
127131
const [currentSidebarTab, setCurrentSidebarTab] = useState<CDMaterialSidebarType>(CDMaterialSidebarType.IMAGE)
132+
const [skipHibernatedApps, setSkipHibernatedApps] = useState<boolean>(false)
128133

129134
const location = useLocation()
130135
const history = useHistory()
@@ -351,15 +356,18 @@ export default function BulkCDTrigger({
351356
const renderHeaderSection = (): JSX.Element => {
352357
return (
353358
<div className="flex flex-align-center flex-justify dc__border-bottom bg__primary pt-16 pr-20 pb-16 pl-20">
354-
<h2 className="fs-16 fw-6 lh-1-43 m-0">Deploy to {appList[0].envName}</h2>
355-
<button
356-
type="button"
357-
className="dc__transparent flex icon-dim-24"
359+
<h2 className="fs-16 fw-6 lh-1-5 m-0 dc__truncate">Deploy to {appList[0].envName}</h2>
360+
<Button
361+
dataTestId="bulk-cd-modal-close"
358362
disabled={isLoading}
359363
onClick={closeBulkCDModal}
360-
>
361-
<Close className="icon-dim-24" />
362-
</button>
364+
size={ComponentSizeType.xs}
365+
icon={<Icon name="ic-close-small" size={null} color={null} />}
366+
ariaLabel="close bulk cd trigger modal"
367+
showAriaLabelInTippy={false}
368+
style={ButtonStyleType.negativeGrey}
369+
variant={ButtonVariantType.borderLess}
370+
/>
363371
</div>
364372
)
365373
}
@@ -835,7 +843,7 @@ export default function BulkCDTrigger({
835843
} else {
836844
isBulkDeploymentTriggered.current = true
837845
stopPropagation(e)
838-
onClickTriggerBulkCD()
846+
onClickTriggerBulkCD(skipHibernatedApps)
839847
setShowResistanceBox(false)
840848
}
841849
}
@@ -848,8 +856,21 @@ export default function BulkCDTrigger({
848856

849857
const renderFooterSection = (): JSX.Element => {
850858
const isDeployButtonDisabled: boolean = isDeployDisabled()
859+
const showSkipHibernatedCheckbox = !!SkipHibernatedCheckbox && canFetchHelmAppStatus
851860
return (
852-
<div className="dc__border-top flex right bg__primary px-20 py-16">
861+
<div
862+
className={`dc__border-top flex ${showSkipHibernatedCheckbox ? 'dc__content-space' : 'right'} bg__primary px-20 py-16`}
863+
>
864+
{showSkipHibernatedCheckbox && (
865+
<SkipHibernatedCheckbox
866+
isDeploymentLoading={isLoading}
867+
envId={appList[0].envId}
868+
envName={appList[0].envName}
869+
appIds={appList.map((app) => app.appId)}
870+
skipHibernated={skipHibernatedApps}
871+
setSkipHibernated={setSkipHibernatedApps}
872+
/>
873+
)}
853874
<div className="dc__position-rel tippy-over">
854875
{!isDeployButtonDisabled && stage === DeploymentNodeType.CD && !isLoading ? (
855876
<AnimatedDeployButton onButtonClick={onClickStartDeploy} isVirtualEnvironment={false} />
@@ -874,7 +895,7 @@ export default function BulkCDTrigger({
874895
return (
875896
<Drawer position="right" width="75%" minWidth="1024px" maxWidth="1200px">
876897
<div className="bg__primary bulk-ci-trigger-container">
877-
<div className='flexbox-col flex-grow-1 dc__overflow-hidden'>
898+
<div className="flexbox-col flex-grow-1 dc__overflow-hidden">
878899
{renderHeaderSection()}
879900
{responseListLength ? (
880901
<TriggerResponseModalBody
@@ -892,7 +913,8 @@ export default function BulkCDTrigger({
892913
closePopup={closeBulkCDModal}
893914
responseList={responseList}
894915
isLoading={isLoading}
895-
onClickRetryBuild={onClickTriggerBulkCD}
916+
onClickRetryDeploy={onClickTriggerBulkCD}
917+
skipHibernatedApps={skipHibernatedApps}
896918
/>
897919
) : (
898920
renderFooterSection()

src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
ButtonStyleType,
5656
ButtonVariantType,
5757
ComponentSizeType,
58+
API_STATUS_CODES,
5859
} from '@devtron-labs/devtron-fe-common-lib'
5960
import Tippy from '@tippyjs/react'
6061
import { BUILD_STATUS, DEFAULT_GIT_BRANCH_VALUE, NO_COMMIT_SELECTED, URLS, ViewType } from '../../../../config'
@@ -1429,7 +1430,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
14291430
return true
14301431
}
14311432

1432-
const onClickTriggerBulkCD = (appsToRetry?: Record<string, boolean>) => {
1433+
const onClickTriggerBulkCD = (skipIfHibernated: boolean, appsToRetry?: Record<string, boolean>) => {
14331434
if (isCDLoading || !validateBulkRuntimeParams()) {
14341435
return
14351436
}
@@ -1482,6 +1483,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
14821483
...(getRuntimeParamsPayload
14831484
? { runtimeParamsPayload: getRuntimeParamsPayload(runtimeParams[currentAppId] ?? []) }
14841485
: {}),
1486+
skipIfHibernated,
14851487
}),
14861488
)
14871489
} else {
@@ -1544,19 +1546,19 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
15441546
})
15451547
} else {
15461548
const errorReason = response.reason
1547-
if (errorReason.code === 409) {
1549+
if (errorReason.code === API_STATUS_CODES.EXPECTATION_FAILED) {
15481550
const statusType = filterStatusType(
15491551
type,
1550-
BULK_CI_RESPONSE_STATUS_TEXT[BulkResponseStatus.FAIL],
1551-
BULK_VIRTUAL_RESPONSE_STATUS[BulkResponseStatus.FAIL],
1552-
BULK_CD_RESPONSE_STATUS_TEXT[BulkResponseStatus.FAIL],
1552+
BULK_CI_RESPONSE_STATUS_TEXT[BulkResponseStatus.SKIP],
1553+
BULK_VIRTUAL_RESPONSE_STATUS[BulkResponseStatus.SKIP],
1554+
BULK_CD_RESPONSE_STATUS_TEXT[BulkResponseStatus.SKIP],
15531555
)
15541556
_responseList.push({
15551557
appId: triggeredAppList[index].appId,
15561558
appName: triggeredAppList[index].appName,
15571559
statusText: statusType,
1558-
status: BulkResponseStatus.FAIL,
1559-
message: errorReason.errors[0].internalMessage,
1560+
status: BulkResponseStatus.SKIP,
1561+
message: errorReason.errors[0].userMessage,
15601562
})
15611563
} else if (errorReason.code === 403 || errorReason.code === 422) {
15621564
// Adding 422 to handle the unauthorized state due to deployment window
@@ -1941,7 +1943,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
19411943
}
19421944
if (!filteredWorkflows.length) {
19431945
return (
1944-
<div className='flex-grow-1'>
1946+
<div className="flex-grow-1">
19451947
<AppNotConfigured />
19461948
</div>
19471949
)
@@ -2099,7 +2101,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
20992101
)
21002102
}
21012103

2102-
const renderBulkSourchChange = (): JSX.Element | null => {
2104+
const renderBulkSourceChange = (): JSX.Element | null => {
21032105
if (!showBulkSourceChangeModal) {
21042106
return null
21052107
}
@@ -2452,12 +2454,12 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
24522454
{renderBulkCDMaterial()}
24532455
{renderBulkCIMaterial()}
24542456
{renderApprovalMaterial()}
2455-
{renderBulkSourchChange()}
2457+
{renderBulkSourceChange()}
24562458
</TriggerViewContext.Provider>
24572459
<div />
24582460
</div>
24592461
{!!selectedAppList.length && (
2460-
<div className="flexbox dc__gap-8 dc__content-space dc__border-top w-100 bg__primary pt-12 pr-20 pb-12 pl-20">
2462+
<div className="flexbox dc__gap-8 dc__content-space dc__border-top w-100 bg__primary px-20 py-12">
24612463
{renderSelectedApps()}
24622464
{renderBulkTriggerActionButtons()}
24632465
</div>

src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ export const TriggerResponseModalFooter = ({
3333
closePopup,
3434
isLoading,
3535
responseList,
36+
skipHibernatedApps,
3637
onClickRetryBuild,
38+
onClickRetryDeploy,
3739
}: TriggerResponseModalFooterProps) => {
3840
const isShowRetryButton = responseList?.some((response) => response.status === BulkResponseStatus.FAIL)
3941

@@ -45,7 +47,12 @@ export const TriggerResponseModalFooter = ({
4547
appsToRetry[response.appId] = true
4648
}
4749
})
48-
onClickRetryBuild(appsToRetry)
50+
51+
if (onClickRetryBuild) {
52+
onClickRetryBuild(appsToRetry)
53+
} else {
54+
onClickRetryDeploy(skipHibernatedApps, appsToRetry)
55+
}
4956
}
5057

5158
return (

src/components/app/details/triggerView/cdMaterial.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,7 @@ const CDMaterial = ({
894894
...(getRuntimeParamsPayload
895895
? { runtimeParamsPayload: getRuntimeParamsPayload(runtimeParamsList ?? []) }
896896
: {}),
897+
skipIfHibernated: false,
897898
})
898899
.then((response: any) => {
899900
if (response.result) {

0 commit comments

Comments
 (0)