diff --git a/package.json b/package.json index c056950486..58bd631223 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.11.0", + "@devtron-labs/devtron-fe-common-lib": "1.11.0-pre-1", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/ApplicationGroup/AppGroup.types.ts b/src/components/ApplicationGroup/AppGroup.types.ts index e0e0a3cbad..d18510e936 100644 --- a/src/components/ApplicationGroup/AppGroup.types.ts +++ b/src/components/ApplicationGroup/AppGroup.types.ts @@ -133,7 +133,7 @@ export interface BulkCDTriggerType extends BulkRuntimeParamsType { appList: BulkCDDetailType[] closePopup: (e) => void updateBulkInputMaterial: (materialList: Record) => void - onClickTriggerBulkCD: (appsToRetry?: Record) => void + onClickTriggerBulkCD: (skipIfHibernated: boolean, appsToRetry?: Record) => void changeTab?: ( materrialId: string | number, artifactId: number, @@ -185,10 +185,21 @@ export interface TriggerResponseModalBodyProps { envName?: string } -export interface TriggerResponseModalFooterProps extends Pick { - onClickRetryBuild: (appsToRetry: Record) => void +type RetryFailedType = + | { + onClickRetryDeploy: BulkCDTriggerType['onClickTriggerBulkCD'] + skipHibernatedApps: boolean + onClickRetryBuild?: never + } + | { + onClickRetryDeploy?: never + skipHibernatedApps?: never + onClickRetryBuild: (appsToRetry: Record) => void + } + +export type TriggerResponseModalFooterProps = Pick & { closePopup: (e) => void -} +} & RetryFailedType export interface TriggerModalRowType { rowData: ResponseRowType diff --git a/src/components/ApplicationGroup/Constants.ts b/src/components/ApplicationGroup/Constants.ts index fd3e96ce62..3ccbbedfd9 100644 --- a/src/components/ApplicationGroup/Constants.ts +++ b/src/components/ApplicationGroup/Constants.ts @@ -69,24 +69,28 @@ export const BULK_VIRTUAL_RESPONSE_STATUS = { [BulkResponseStatus.PASS]: 'Succeeded', [BulkResponseStatus.FAIL]: 'Failed', [BulkResponseStatus.UNAUTHORIZE]: 'Not authorised', + [BulkResponseStatus.SKIP]: 'Skipped', } export const BULK_CI_RESPONSE_STATUS_TEXT = { [BulkResponseStatus.PASS]: 'Build triggered', [BulkResponseStatus.FAIL]: 'Build not triggered', [BulkResponseStatus.UNAUTHORIZE]: 'Not authorized', + [BulkResponseStatus.SKIP]: 'Skipped', } export const BULK_CD_RESPONSE_STATUS_TEXT = { [BulkResponseStatus.PASS]: 'Deployment triggered', [BulkResponseStatus.FAIL]: 'Deployment not triggered', [BulkResponseStatus.UNAUTHORIZE]: 'Not authorized', + [BulkResponseStatus.SKIP]: 'Skipped', } export const responseListOrder = { [BulkResponseStatus.FAIL]: 0, [BulkResponseStatus.UNAUTHORIZE]: 1, - [BulkResponseStatus.PASS]: 2, + [BulkResponseStatus.SKIP]: 2, + [BulkResponseStatus.PASS]: 3, } export const BULK_HIBERNATE_ERROR_MESSAGE = { diff --git a/src/components/ApplicationGroup/Details/TriggerView/BulkCDTrigger.tsx b/src/components/ApplicationGroup/Details/TriggerView/BulkCDTrigger.tsx index 6de4241b14..2f67edbba2 100644 --- a/src/components/ApplicationGroup/Details/TriggerView/BulkCDTrigger.tsx +++ b/src/components/ApplicationGroup/Details/TriggerView/BulkCDTrigger.tsx @@ -19,7 +19,6 @@ import { CDMaterialResponseType, DeploymentNodeType, Drawer, - Progressing, ReleaseTag, ImageComment, showError, @@ -48,9 +47,12 @@ import { Button, ComponentSizeType, AnimatedDeployButton, + ButtonVariantType, + Icon, + ButtonStyleType, + useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { useHistory, useLocation } from 'react-router-dom' -import { ReactComponent as Close } from '@Icons/ic-cross.svg' import { ReactComponent as DeployIcon } from '@Icons/ic-nav-rocket.svg' import { ReactComponent as PlayIcon } from '@Icons/ic-play-outline.svg' import { ReactComponent as Error } from '@Icons/ic-warning.svg' @@ -89,6 +91,7 @@ const validateRuntimeParameters = importComponentFromFELibrary( () => ({ isValid: true, cellError: {} }), 'function', ) +const SkipHibernatedCheckbox = importComponentFromFELibrary('SkipHibernatedCheckbox', null, 'function') // TODO: Fix release tags selection export default function BulkCDTrigger({ @@ -109,6 +112,7 @@ export default function BulkCDTrigger({ runtimeParamsErrorState, setRuntimeParamsErrorState, }: BulkCDTriggerType) { + const { canFetchHelmAppStatus } = useMainContext() const [selectedApp, setSelectedApp] = useState( appList.find((app) => !app.warningMessage) || appList[0], ) @@ -125,6 +129,7 @@ export default function BulkCDTrigger({ const [isPartialActionAllowed, setIsPartialActionAllowed] = useState(false) const [showResistanceBox, setShowResistanceBox] = useState(false) const [currentSidebarTab, setCurrentSidebarTab] = useState(CDMaterialSidebarType.IMAGE) + const [skipHibernatedApps, setSkipHibernatedApps] = useState(false) const location = useLocation() const history = useHistory() @@ -351,15 +356,18 @@ export default function BulkCDTrigger({ const renderHeaderSection = (): JSX.Element => { return (
-

Deploy to {appList[0].envName}

- + size={ComponentSizeType.xs} + icon={} + ariaLabel="close bulk cd trigger modal" + showAriaLabelInTippy={false} + style={ButtonStyleType.negativeGrey} + variant={ButtonVariantType.borderLess} + />
) } @@ -835,7 +843,7 @@ export default function BulkCDTrigger({ } else { isBulkDeploymentTriggered.current = true stopPropagation(e) - onClickTriggerBulkCD() + onClickTriggerBulkCD(skipHibernatedApps) setShowResistanceBox(false) } } @@ -848,8 +856,21 @@ export default function BulkCDTrigger({ const renderFooterSection = (): JSX.Element => { const isDeployButtonDisabled: boolean = isDeployDisabled() + const showSkipHibernatedCheckbox = !!SkipHibernatedCheckbox && canFetchHelmAppStatus return ( -
+
+ {showSkipHibernatedCheckbox && ( + app.appId)} + skipHibernated={skipHibernatedApps} + setSkipHibernated={setSkipHibernatedApps} + /> + )}
{!isDeployButtonDisabled && stage === DeploymentNodeType.CD && !isLoading ? ( @@ -874,7 +895,7 @@ export default function BulkCDTrigger({ return (
-
+
{renderHeaderSection()} {responseListLength ? ( ) : ( renderFooterSection() diff --git a/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx b/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx index 87945322bf..3e36407686 100644 --- a/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx +++ b/src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx @@ -55,6 +55,7 @@ import { ButtonStyleType, ButtonVariantType, ComponentSizeType, + API_STATUS_CODES, } from '@devtron-labs/devtron-fe-common-lib' import Tippy from '@tippyjs/react' 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 return true } - const onClickTriggerBulkCD = (appsToRetry?: Record) => { + const onClickTriggerBulkCD = (skipIfHibernated: boolean, appsToRetry?: Record) => { if (isCDLoading || !validateBulkRuntimeParams()) { return } @@ -1482,6 +1483,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou ...(getRuntimeParamsPayload ? { runtimeParamsPayload: getRuntimeParamsPayload(runtimeParams[currentAppId] ?? []) } : {}), + skipIfHibernated, }), ) } else { @@ -1544,19 +1546,19 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou }) } else { const errorReason = response.reason - if (errorReason.code === 409) { + if (errorReason.code === API_STATUS_CODES.EXPECTATION_FAILED) { const statusType = filterStatusType( type, - BULK_CI_RESPONSE_STATUS_TEXT[BulkResponseStatus.FAIL], - BULK_VIRTUAL_RESPONSE_STATUS[BulkResponseStatus.FAIL], - BULK_CD_RESPONSE_STATUS_TEXT[BulkResponseStatus.FAIL], + BULK_CI_RESPONSE_STATUS_TEXT[BulkResponseStatus.SKIP], + BULK_VIRTUAL_RESPONSE_STATUS[BulkResponseStatus.SKIP], + BULK_CD_RESPONSE_STATUS_TEXT[BulkResponseStatus.SKIP], ) _responseList.push({ appId: triggeredAppList[index].appId, appName: triggeredAppList[index].appName, statusText: statusType, - status: BulkResponseStatus.FAIL, - message: errorReason.errors[0].internalMessage, + status: BulkResponseStatus.SKIP, + message: errorReason.errors[0].userMessage, }) } else if (errorReason.code === 403 || errorReason.code === 422) { // Adding 422 to handle the unauthorized state due to deployment window @@ -1941,7 +1943,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou } if (!filteredWorkflows.length) { return ( -
+
) @@ -2099,7 +2101,7 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou ) } - const renderBulkSourchChange = (): JSX.Element | null => { + const renderBulkSourceChange = (): JSX.Element | null => { if (!showBulkSourceChangeModal) { return null } @@ -2452,12 +2454,12 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou {renderBulkCDMaterial()} {renderBulkCIMaterial()} {renderApprovalMaterial()} - {renderBulkSourchChange()} + {renderBulkSourceChange()}
{!!selectedAppList.length && ( -
+
{renderSelectedApps()} {renderBulkTriggerActionButtons()}
diff --git a/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx b/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx index 790028d20d..29a6e81d58 100644 --- a/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx +++ b/src/components/ApplicationGroup/Details/TriggerView/TriggerResponseModal.tsx @@ -33,7 +33,9 @@ export const TriggerResponseModalFooter = ({ closePopup, isLoading, responseList, + skipHibernatedApps, onClickRetryBuild, + onClickRetryDeploy, }: TriggerResponseModalFooterProps) => { const isShowRetryButton = responseList?.some((response) => response.status === BulkResponseStatus.FAIL) @@ -45,7 +47,12 @@ export const TriggerResponseModalFooter = ({ appsToRetry[response.appId] = true } }) - onClickRetryBuild(appsToRetry) + + if (onClickRetryBuild) { + onClickRetryBuild(appsToRetry) + } else { + onClickRetryDeploy(skipHibernatedApps, appsToRetry) + } } return ( diff --git a/src/components/app/details/triggerView/cdMaterial.tsx b/src/components/app/details/triggerView/cdMaterial.tsx index 3614571a1e..38d8c0b21d 100644 --- a/src/components/app/details/triggerView/cdMaterial.tsx +++ b/src/components/app/details/triggerView/cdMaterial.tsx @@ -894,6 +894,7 @@ const CDMaterial = ({ ...(getRuntimeParamsPayload ? { runtimeParamsPayload: getRuntimeParamsPayload(runtimeParamsList ?? []) } : {}), + skipIfHibernated: false, }) .then((response: any) => { if (response.result) { diff --git a/src/components/common/navigation/NavigationRoutes.tsx b/src/components/common/navigation/NavigationRoutes.tsx index 955914a67b..9ea127ef04 100644 --- a/src/components/common/navigation/NavigationRoutes.tsx +++ b/src/components/common/navigation/NavigationRoutes.tsx @@ -77,7 +77,7 @@ import 'monaco-editor' import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' import YamlWorker from '../../../yaml.worker.js?worker' import { TAB_DATA_LOCAL_STORAGE_KEY } from '../DynamicTabs/constants' -import { DEFAULT_GIT_OPS_FEATURE_FLAGS } from './constants' +import { ENVIRONMENT_DATA_FALLBACK, INITIAL_ENV_DATA_STATE } from './constants' import { ParsedTabsData, ParsedTabsDataV1 } from '../DynamicTabs/types' import { SwitchThemeDialog } from '@Pages/Shared' import { SwitchThemeDialogProps } from '@Pages/Shared/SwitchThemeDialog/types' @@ -152,16 +152,11 @@ export default function NavigationRoutes() { } const [environmentId, setEnvironmentId] = useState(null) const contextValue = useMemo(() => ({ environmentId, setEnvironmentId }), [environmentId]) - const [environmentDataState, setEnvironmentDataState] = useState({ - isAirgapped: false, - isManifestScanningEnabled: false, - canOnlyViewPermittedEnvOrgLevel: false, - featureGitOpsFlags: structuredClone(DEFAULT_GIT_OPS_FEATURE_FLAGS), - devtronManagedLicensingEnabled: false, - }) const { showThemeSwitcherDialog, handleThemeSwitcherDialogVisibilityChange, appTheme } = useTheme() + const [environmentDataState, setEnvironmentDataState] = useState(INITIAL_ENV_DATA_STATE) + const [licenseInfoDialogType, setLicenseInfoDialogType] = useState(null) const { userPreferences, userPreferencesError, @@ -170,9 +165,6 @@ export default function NavigationRoutes() { handleUpdatePipelineRBACViewSelectedTab, } = useUserPreferences({ migrateUserPreferences }) - const [licenseInfoDialogType, setLicenseInfoDialogType] = useState(null) - - const { isAirgapped, isManifestScanningEnabled, canOnlyViewPermittedEnvOrgLevel, devtronManagedLicensingEnabled } = environmentDataState @@ -338,35 +330,33 @@ export default function NavigationRoutes() { } const getEnvironmentDataValues = async (): Promise => { - const fallbackResponse: EnvironmentDataValuesDTO = { - isAirGapEnvironment: false, - isManifestScanningEnabled: false, - canOnlyViewPermittedEnvOrgLevel: false, - featureGitOpsFlags: structuredClone(DEFAULT_GIT_OPS_FEATURE_FLAGS), - devtronManagedLicensingEnabled: false, - } - if (!getEnvironmentData) { - return fallbackResponse + return ENVIRONMENT_DATA_FALLBACK } try { const { result } = await getEnvironmentData() - const parsedFeatureGitOpsFlags: typeof fallbackResponse.featureGitOpsFlags = { + const parsedFeatureGitOpsFlags: typeof ENVIRONMENT_DATA_FALLBACK.featureGitOpsFlags = { isFeatureArgoCdMigrationEnabled: result.featureGitOpsFlags?.isFeatureArgoCdMigrationEnabled || false, isFeatureGitOpsEnabled: result.featureGitOpsFlags?.isFeatureGitOpsEnabled || false, isFeatureUserDefinedGitOpsEnabled: result.featureGitOpsFlags?.isFeatureUserDefinedGitOpsEnabled || false, } return { - isAirGapEnvironment: result.isAirGapEnvironment, - isManifestScanningEnabled: result.isManifestScanningEnabled, - canOnlyViewPermittedEnvOrgLevel: result.canOnlyViewPermittedEnvOrgLevel, + isAirGapEnvironment: result.isAirGapEnvironment ?? ENVIRONMENT_DATA_FALLBACK['isAirGapEnvironment'], + isManifestScanningEnabled: + result.isManifestScanningEnabled ?? ENVIRONMENT_DATA_FALLBACK['isManifestScanningEnabled'], + canOnlyViewPermittedEnvOrgLevel: + result.canOnlyViewPermittedEnvOrgLevel ?? + ENVIRONMENT_DATA_FALLBACK['canOnlyViewPermittedEnvOrgLevel'], featureGitOpsFlags: parsedFeatureGitOpsFlags, - devtronManagedLicensingEnabled: result.devtronManagedLicensingEnabled || false, + canFetchHelmAppStatus: result.canFetchHelmAppStatus ?? ENVIRONMENT_DATA_FALLBACK['canFetchHelmAppStatus'], + devtronManagedLicensingEnabled: + result.devtronManagedLicensingEnabled ?? + ENVIRONMENT_DATA_FALLBACK['devtronManagedLicensingEnabled'], } } catch { - return fallbackResponse + return ENVIRONMENT_DATA_FALLBACK } } @@ -386,6 +376,7 @@ export default function NavigationRoutes() { isManifestScanningEnabled: environmentDataResponse.isManifestScanningEnabled, canOnlyViewPermittedEnvOrgLevel: environmentDataResponse.canOnlyViewPermittedEnvOrgLevel, featureGitOpsFlags: environmentDataResponse.featureGitOpsFlags, + canFetchHelmAppStatus: environmentDataResponse.canFetchHelmAppStatus, devtronManagedLicensingEnabled: environmentDataResponse.devtronManagedLicensingEnabled, }) @@ -492,6 +483,7 @@ export default function NavigationRoutes() { handleOpenLicenseInfoDialog, licenseData, setLicenseData, + canFetchHelmAppStatus: environmentDataState.canFetchHelmAppStatus, }} >
diff --git a/src/components/common/navigation/constants.ts b/src/components/common/navigation/constants.ts index f398972666..0676a55fa9 100644 --- a/src/components/common/navigation/constants.ts +++ b/src/components/common/navigation/constants.ts @@ -14,10 +14,30 @@ * limitations under the License. */ -import { MainContext } from '@devtron-labs/devtron-fe-common-lib' +import { EnvironmentDataValuesDTO, MainContext } from '@devtron-labs/devtron-fe-common-lib' -export const DEFAULT_GIT_OPS_FEATURE_FLAGS: MainContext['featureGitOpsFlags'] = { +import { EnvironmentDataStateType } from './types' + +const DEFAULT_GIT_OPS_FEATURE_FLAGS: MainContext['featureGitOpsFlags'] = { isFeatureArgoCdMigrationEnabled: false, isFeatureGitOpsEnabled: false, isFeatureUserDefinedGitOpsEnabled: false, } + +const COMMON_ENV_FALLBACK: Omit = { + isManifestScanningEnabled: false, + canOnlyViewPermittedEnvOrgLevel: false, + featureGitOpsFlags: structuredClone(DEFAULT_GIT_OPS_FEATURE_FLAGS), + canFetchHelmAppStatus: false, + devtronManagedLicensingEnabled: false, +} + +export const ENVIRONMENT_DATA_FALLBACK: EnvironmentDataValuesDTO = { + ...COMMON_ENV_FALLBACK, + isAirGapEnvironment: false, +} + +export const INITIAL_ENV_DATA_STATE: EnvironmentDataStateType = { + ...COMMON_ENV_FALLBACK, + isAirgapped: structuredClone(ENVIRONMENT_DATA_FALLBACK).isAirGapEnvironment, +} diff --git a/vite.config.mts b/vite.config.mts index 27ec470fdb..49a8713330 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -214,6 +214,30 @@ export default defineConfig(({ mode }) => { globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'], cleanupOutdatedCaches: true, maximumFileSizeToCacheInBytes: 10000000, + runtimeCaching: [ + { + urlPattern: ({ request }) => request.destination === 'style', + handler: 'NetworkFirst', + options: { + cacheName: 'css-cache', + expiration: { + maxEntries: 30, + maxAgeSeconds: 60 * 60 * 24 * 15, + }, + }, + }, + { + urlPattern: ({ request }) => request.destination === 'script', + handler: 'NetworkFirst', + options: { + cacheName: 'js-cache', + expiration: { + maxEntries: 30, + maxAgeSeconds: 60 * 60 * 24 * 15, + }, + }, + }, + ], }, manifest: { short_name: 'Devtron', diff --git a/yarn.lock b/yarn.lock index 39af4806f1..95d6357adf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1126,10 +1126,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.11.0.tgz#1cf1ae310119b5b0c1c8bb3d162cf291951b84bf" - integrity sha512-JRJEqnuUBcfoqEQ7x/5Oqx+HcYxNmAQAZd/++Rf2F2mvslS1UFsbA8e6irRnGSmzUuksAX5OnARXnM1VOZNIsw== +"@devtron-labs/devtron-fe-common-lib@1.11.0-pre-1": + version "1.11.0-pre-1" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.11.0-pre-1.tgz#dc75f935e46dc4f32b6e505694b93e38da25f85c" + integrity sha512-hvqUNyRn0J3Vf4uRvM+kpWRdJXefH9R0V44vEiwskpVO4jSgIMTE865lCyJ6cZnJ4ukbyPCQXq/0+JSRZgGtcg== dependencies: "@codemirror/lang-json" "6.0.1" "@codemirror/lang-yaml" "6.1.2"