@spinnaker/core 2026.3.1 → 2026.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/pipeline/config/graph/PipelineGraph.d.ts +2 -0
- package/dist/pipeline/config/triggers/Triggers.d.ts +0 -1
- package/package.json +2 -2
- package/src/pipeline/config/PipelineConfigPage.tsx +135 -86
- package/src/pipeline/config/actions/pipelineJson/EditPipelineJsonModal.spec.tsx +55 -0
- package/src/pipeline/config/actions/pipelineJson/EditPipelineJsonModal.tsx +8 -0
- package/src/pipeline/config/graph/PipelineGraph.tsx +18 -1
- package/src/pipeline/config/stages/FormikStageConfig.spec.tsx +62 -0
- package/src/pipeline/config/stages/FormikStageConfig.tsx +18 -1
- package/src/pipeline/config/triggers/ExecutionOptionsPageContent.tsx +12 -5
- package/src/pipeline/config/triggers/Triggers.spec.tsx +0 -1
- package/src/pipeline/config/triggers/Triggers.tsx +1 -6
- package/src/pipeline/config/triggers/TriggersPageContent.tsx +11 -8
|
@@ -32,6 +32,17 @@ const decorate = (validate: IContextualValidator, props: IFormikStageConfigProps
|
|
|
32
32
|
return (values: IStage) => validate(values, { application, pipeline });
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
// Applies only the fields the form changed, so stage edits made outside the form are kept
|
|
36
|
+
const applyChangedValues = (stage: IStage, values: IStage, prevValues: IStage): IStage => {
|
|
37
|
+
const changes: Partial<IStage> = {};
|
|
38
|
+
new Set([...Object.keys(prevValues), ...Object.keys(values)]).forEach((key) => {
|
|
39
|
+
if (values[key] !== prevValues[key]) {
|
|
40
|
+
changes[key] = values[key];
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return { ...stage, ...changes };
|
|
44
|
+
};
|
|
45
|
+
|
|
35
46
|
export class FormikStageConfig extends React.Component<IFormikStageConfigProps> {
|
|
36
47
|
public render() {
|
|
37
48
|
const { render, onChange, stage, validate, application, pipeline } = this.props;
|
|
@@ -42,7 +53,13 @@ export class FormikStageConfig extends React.Component<IFormikStageConfigProps>
|
|
|
42
53
|
onSubmit={() => {}}
|
|
43
54
|
render={(formik) => (
|
|
44
55
|
<LayoutProvider value={ResponsiveFieldLayout}>
|
|
45
|
-
<WatchValue
|
|
56
|
+
<WatchValue
|
|
57
|
+
onChange={
|
|
58
|
+
onChange &&
|
|
59
|
+
((values: IStage, prevValues: IStage) => onChange(applyChangedValues(stage, values, prevValues)))
|
|
60
|
+
}
|
|
61
|
+
value={formik.values}
|
|
62
|
+
/>
|
|
46
63
|
{render({ application, pipeline, formik })}
|
|
47
64
|
</LayoutProvider>
|
|
48
65
|
)}
|
|
@@ -11,9 +11,16 @@ export interface IExecutionOptionsPageContentProps {
|
|
|
11
11
|
|
|
12
12
|
export function ExecutionOptionsPageContent(props: IExecutionOptionsPageContentProps) {
|
|
13
13
|
const { pipeline, updatePipelineConfig } = props;
|
|
14
|
-
const
|
|
14
|
+
const maxConcurrent = pipeline.maxConcurrentExecutions || 0;
|
|
15
|
+
const [currentMaxConcurrent, setCurrentMaxConcurrent] = useState(maxConcurrent);
|
|
16
|
+
const [lastPipeline, setLastPipeline] = useState(pipeline);
|
|
17
|
+
// Show the pipeline's value when the pipeline changes, such as after a revert or pipeline JSON edit
|
|
18
|
+
if (pipeline !== lastPipeline) {
|
|
19
|
+
setLastPipeline(pipeline);
|
|
20
|
+
setCurrentMaxConcurrent(maxConcurrent);
|
|
21
|
+
}
|
|
15
22
|
const handleMaxConcurrentChange = (changeEvent: any) => {
|
|
16
|
-
const value = Number.parseInt(changeEvent.target.value);
|
|
23
|
+
const value = Number.parseInt(changeEvent.target.value) || 0;
|
|
17
24
|
setCurrentMaxConcurrent(value);
|
|
18
25
|
updatePipelineConfig({ maxConcurrentExecutions: value });
|
|
19
26
|
};
|
|
@@ -29,11 +36,11 @@ export function ExecutionOptionsPageContent(props: IExecutionOptionsPageContentP
|
|
|
29
36
|
/>
|
|
30
37
|
)}
|
|
31
38
|
onChange={() => {
|
|
32
|
-
|
|
39
|
+
const changes: Partial<IPipeline> = { limitConcurrent: !pipeline.limitConcurrent };
|
|
33
40
|
if (!pipeline.limitConcurrent && !pipeline.keepWaitingPipelines) {
|
|
34
|
-
|
|
35
|
-
updatePipelineConfig({ keepWaitingPipelines: true });
|
|
41
|
+
changes.keepWaitingPipelines = true;
|
|
36
42
|
}
|
|
43
|
+
updatePipelineConfig(changes);
|
|
37
44
|
}}
|
|
38
45
|
value={pipeline.limitConcurrent}
|
|
39
46
|
/>
|
|
@@ -31,7 +31,6 @@ describe('<Triggers />', () => {
|
|
|
31
31
|
application: ApplicationModelBuilder.createApplicationForTests('products'),
|
|
32
32
|
fieldUpdated: jasmine.createSpy('fieldUpdated'),
|
|
33
33
|
updatePipelineConfig: jasmine.createSpy('updatePipelineConfig'),
|
|
34
|
-
revertCount: 0,
|
|
35
34
|
};
|
|
36
35
|
});
|
|
37
36
|
|
|
@@ -15,14 +15,10 @@ export interface ITriggersProps {
|
|
|
15
15
|
pipelineConfig?: IPipeline;
|
|
16
16
|
fieldUpdated: () => void;
|
|
17
17
|
updatePipelineConfig: (changes: Partial<IPipeline>) => void;
|
|
18
|
-
revertCount: number;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export function Triggers(props: ITriggersProps) {
|
|
22
21
|
const pipeline = props.pipeline;
|
|
23
|
-
// KLUDGE: This value is used as a React key when rendering the Triggers.
|
|
24
|
-
// Whenever the pipeline is reverted, this causes the Triggers to remount and reset formik state.
|
|
25
|
-
const revertCountKLUDGE = props.revertCount;
|
|
26
22
|
|
|
27
23
|
return (
|
|
28
24
|
<PageNavigator scrollableContainer=".pipeline-config-page">
|
|
@@ -35,7 +31,7 @@ export function Triggers(props: ITriggersProps) {
|
|
|
35
31
|
badge={pipeline.triggers ? pipeline.triggers.length.toString() : '0'}
|
|
36
32
|
noWrapper={true}
|
|
37
33
|
>
|
|
38
|
-
<TriggersPageContent {...props}
|
|
34
|
+
<TriggersPageContent {...props} />
|
|
39
35
|
</PageSection>
|
|
40
36
|
<PageSection
|
|
41
37
|
pageKey="parameters"
|
|
@@ -57,7 +53,6 @@ export function Triggers(props: ITriggersProps) {
|
|
|
57
53
|
<MetadataPageContent
|
|
58
54
|
pipeline={props.pipelineConfig || props.pipeline}
|
|
59
55
|
updatePipelineConfig={props.updatePipelineConfig}
|
|
60
|
-
key={revertCountKLUDGE}
|
|
61
56
|
/>
|
|
62
57
|
</PageSection>
|
|
63
58
|
</PageNavigator>
|
|
@@ -24,12 +24,15 @@ export function TriggersPageContent(props: ITriggersPageContentProps) {
|
|
|
24
24
|
const { pipeline, application, updatePipelineConfig } = props;
|
|
25
25
|
const expectedArtifacts = pipeline?.expectedArtifacts ?? [];
|
|
26
26
|
const triggers = pipeline?.triggers ?? [];
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
// Each trigger object gets its own form, so replacing a trigger remounts it.
|
|
28
|
+
// The key follows a trigger through its form's own edits.
|
|
29
|
+
const [triggerKeys] = useState(() => ({ ids: new WeakMap<ITrigger, number>(), next: 0 }));
|
|
30
|
+
const getTriggerKey = (trigger: ITrigger) => {
|
|
31
|
+
if (!triggerKeys.ids.has(trigger)) {
|
|
32
|
+
triggerKeys.ids.set(trigger, triggerKeys.next++);
|
|
33
|
+
}
|
|
34
|
+
return triggerKeys.ids.get(trigger);
|
|
35
|
+
};
|
|
33
36
|
|
|
34
37
|
function updateRoles(roles: any[]): void {
|
|
35
38
|
updatePipelineConfig({ roles });
|
|
@@ -72,7 +75,6 @@ export function TriggersPageContent(props: ITriggersPageContentProps) {
|
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
updatePipelineConfig(pipelineUpdate);
|
|
75
|
-
setDeleteCount(deleteCount + 1);
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/**
|
|
@@ -82,6 +84,7 @@ export function TriggersPageContent(props: ITriggersPageContentProps) {
|
|
|
82
84
|
* @param updatedTrigger The updated trigger
|
|
83
85
|
*/
|
|
84
86
|
function updateTrigger(indexToUpdate: number, updatedTrigger: ITrigger): void {
|
|
87
|
+
triggerKeys.ids.set(updatedTrigger, getTriggerKey(triggers[indexToUpdate]));
|
|
85
88
|
PipelineConfigValidator.validatePipeline(pipeline);
|
|
86
89
|
updatePipelineConfig({
|
|
87
90
|
triggers: triggers.map((trigger, index) => {
|
|
@@ -181,7 +184,7 @@ export function TriggersPageContent(props: ITriggersPageContentProps) {
|
|
|
181
184
|
</div>
|
|
182
185
|
)}
|
|
183
186
|
{triggers.map((trigger, index) => (
|
|
184
|
-
<div className="trigger-config" key={
|
|
187
|
+
<div className="trigger-config" key={getTriggerKey(trigger)}>
|
|
185
188
|
<Trigger
|
|
186
189
|
application={application}
|
|
187
190
|
index={index}
|