diff --git a/ui/src/features/stage/promotion-steps.tsx b/ui/src/features/stage/promotion-steps.tsx index 1fb40d593b..9ab9016d96 100644 --- a/ui/src/features/stage/promotion-steps.tsx +++ b/ui/src/features/stage/promotion-steps.tsx @@ -34,22 +34,31 @@ export const PromotionSteps = (props: PromotionStepsProps) => { const phase = getPromotionStatusPhase(props.promotion); - const shouldShowMessage = - isPromotionPhaseTerminal(phase) && - phase !== PromotionStatusPhase.SUCCEEDED && - phase !== PromotionStatusPhase.ERRORED && // because its already handled at individual step level - !!props.promotion?.status?.message; + const message = props.promotion?.status?.message; + + // A failed step's message becomes the Promotion's, so it is already on screen. + const hasIndividualPromotionStepTerminalMessage = ( + props.promotion.status?.stepExecutionMetadata ?? [] + ).some((meta, i) => isFailedStep(i, props.promotion.status) && !!meta.message); + + let shouldShowMessage = false; + + if (isPromotionPhaseTerminal(phase)) { + switch (phase) { + case PromotionStatusPhase.FAILED: + case PromotionStatusPhase.ERRORED: + // The failing step already shows this message, so don't repeat it. + shouldShowMessage = !hasIndividualPromotionStepTerminalMessage; + break; + case PromotionStatusPhase.ABORTED: + // An abort is not attributable to any one step. + shouldShowMessage = true; + break; + } + } const steps = props.promotion?.spec?.steps ?? []; - const errorItem = { - key: 'error', - label: , - showArrow: false, - collapsible: 'disabled' as const, - styles: { header: { paddingTop: 0 } } - }; - // Steps with a registered extension are interactive const hasExtension = (step: (typeof steps)[number]) => promotionStepExtensions.some((ext) => ext.identifier === step.uses); @@ -76,9 +85,26 @@ export const PromotionSteps = (props: PromotionStepsProps) => { key }; - return isFailedStep(i, props.promotion.status) - ? [{ ...item, className: `${item.className || ''} !border-none` }, errorItem] - : [item]; + if (!isFailedStep(i, props.promotion.status)) { + return [item]; + } + + const stepMessage = props.promotion.status?.stepExecutionMetadata?.[i]?.message; + + if (!stepMessage) { + return [item]; + } + + return [ + { ...item, className: `${item.className || ''} !border-none` }, + { + key: `${key}-error`, + label: , + showArrow: false, + collapsible: 'disabled' as const, + styles: { header: { paddingTop: 0 } } + } + ]; }); useEffect(() => { @@ -97,9 +123,7 @@ export const PromotionSteps = (props: PromotionStepsProps) => { activeKey={activeKeys} onChange={(keys) => setActiveKeys(typeof keys === 'string' ? [keys] : keys)} /> - {shouldShowMessage && ( - - )} + {shouldShowMessage && !!message && } ); };