diff --git a/packages/core/src/pipeline/config/stages/overrideTimeout/OverrideTimeout.tsx b/packages/core/src/pipeline/config/stages/overrideTimeout/OverrideTimeout.tsx index 8b31a7e9696..841092e09d1 100644 --- a/packages/core/src/pipeline/config/stages/overrideTimeout/OverrideTimeout.tsx +++ b/packages/core/src/pipeline/config/stages/overrideTimeout/OverrideTimeout.tsx @@ -1,4 +1,4 @@ -import { get } from 'lodash'; +import { get, isNumber } from 'lodash'; import { Duration } from 'luxon'; import React from 'react'; @@ -10,7 +10,7 @@ const { useEffect, useState } = React; export interface IOverrideTimeoutConfigProps { stageConfig: IStageConfig; - stageTimeoutMs: number; + stageTimeoutMs: number | any; updateStageField: (changes: Partial) => void; } @@ -41,8 +41,13 @@ export const OverrideTimeout = (props: IOverrideTimeoutConfigProps) => { }, [props.stageTimeoutMs]); const stageChanged = () => { - if (props.stageTimeoutMs !== undefined) { + if (props.stageTimeoutMs !== undefined && !isExpression) { enableTimeout(); + } else if (props.stageTimeoutMs !== undefined && isExpression) { + setOverrideTimeout(true); + props.updateStageField({ + stageTimeoutMs: props.stageTimeoutMs || null, + }); } else { clearTimeout(); } @@ -74,6 +79,10 @@ export const OverrideTimeout = (props: IOverrideTimeoutConfigProps) => { }; const isConfigurable = !!get(props.stageConfig, 'supportsCustomTimeout'); + const isExpression = + props.stageTimeoutMs !== undefined && props.stageTimeoutMs !== null && !isNumber(props.stageTimeoutMs) + ? props.stageTimeoutMs.includes('${') + : false; if (isConfigurable) { return ( @@ -94,7 +103,7 @@ export const OverrideTimeout = (props: IOverrideTimeoutConfigProps) => { - {overrideTimeout && ( + {overrideTimeout && !isExpression && (
@@ -123,6 +132,17 @@ export const OverrideTimeout = (props: IOverrideTimeoutConfigProps) => {
)} + {overrideTimeout && isExpression && ( +
+
+
+ + Resolved at runtime from expression: {props.stageTimeoutMs} + +
+
+
+ )} ); } else { diff --git a/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts b/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts index 19c2f7f3dcc..2e37c59b415 100644 --- a/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts +++ b/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts @@ -54,6 +54,10 @@ export interface ICustomValidator extends IStageOrTriggerValidator, IValidatorCo [k: string]: any; } +function isNumberOrSpel(valInput: any) { + return (isNumber(valInput) && valInput > 0) || (typeof valInput === 'string' && valInput.includes('${')); +} + export class PipelineConfigValidator { private static validators: Map = new Map(); private static validationStream: Subject = new Subject(); @@ -151,7 +155,7 @@ export class PipelineConfigValidator { ); } - if (stage.stageTimeoutMs !== undefined && !(isNumber(stage.stageTimeoutMs) && stage.stageTimeoutMs > 0)) { + if (stage.stageTimeoutMs !== undefined && !isNumberOrSpel(stage.stageTimeoutMs)) { stageValidations.set(stage, [ ...(stageValidations.get(stage) || []), 'Stage is configured to fail after a specific amount of time, but no time is set.',