@spinnaker/core 0.24.1 → 0.25.0
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/CHANGELOG.md +18 -0
- package/dist/config/settings.d.ts +1 -0
- package/dist/domain/IArtifact.d.ts +2 -0
- package/dist/domain/ITrigger.d.ts +5 -0
- package/dist/index.js +36 -36
- package/dist/index.js.map +1 -1
- package/dist/manifest/ManifestYaml.d.ts +10 -4
- package/dist/pipeline/config/stages/bakeManifest/ManifestRenderers.d.ts +2 -0
- package/dist/pipeline/config/stages/bakeManifest/helmfile/BakeHelmfileConfigForm.d.ts +18 -0
- package/dist/pipeline/config/stages/bakeManifest/utils/getBakedArtifacts.d.ts +4 -0
- package/dist/pipeline/config/stages/bakeManifest/utils/getContentReference.d.ts +1 -0
- package/dist/pipeline/config/triggers/artifacts/ArtifactService.d.ts +3 -0
- package/dist/pipeline/config/triggers/cdevents/CDEventsTrigger.d.ts +7 -0
- package/dist/pipeline/config/triggers/cdevents/cdevents.trigger.d.ts +1 -0
- package/dist/pipeline/config/triggers/index.d.ts +1 -0
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/presentation/forms/inputs/NumberConcurrencyInput.d.ts +7 -0
- package/package.json +2 -2
- package/src/artifact/ArtifactIconService.ts +1 -0
- package/src/artifact/ArtifactTypes.ts +1 -0
- package/src/config/settings.ts +1 -0
- package/src/domain/IArtifact.ts +3 -0
- package/src/domain/ITrigger.ts +4 -0
- package/src/help/help.contents.ts +10 -0
- package/src/manifest/ManifestYaml.tsx +29 -7
- package/src/pipeline/config/stages/bakeManifest/BakeManifestConfig.tsx +4 -1
- package/src/pipeline/config/stages/bakeManifest/BakeManifestDetailsTab.tsx +24 -12
- package/src/pipeline/config/stages/bakeManifest/BakeManifestStageForm.tsx +9 -2
- package/src/pipeline/config/stages/bakeManifest/ManifestRenderers.ts +2 -0
- package/src/pipeline/config/stages/bakeManifest/helmfile/BakeHelmfileConfigForm.spec.tsx +132 -0
- package/src/pipeline/config/stages/bakeManifest/helmfile/BakeHelmfileConfigForm.tsx +271 -0
- package/src/pipeline/config/stages/bakeManifest/utils/getBakedArtifacts.ts +13 -0
- package/src/pipeline/config/stages/bakeManifest/utils/getContentReference.ts +3 -0
- package/src/pipeline/config/triggers/artifacts/ArtifactService.ts +4 -0
- package/src/pipeline/config/triggers/cdevents/CDEventsTrigger.tsx +48 -0
- package/src/pipeline/config/triggers/cdevents/cdevents.trigger.ts +19 -0
- package/src/pipeline/config/triggers/index.ts +1 -0
- package/src/pipeline/create/CreatePipelineModal.tsx +1 -1
- package/src/pipeline/index.ts +2 -0
- package/src/presentation/forms/inputs/NumberConcurrencyInput.tsx +29 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IFormikStageConfigInjectedProps } from '../../FormikStageConfig';
|
|
4
|
+
import { AccountService } from '../../../../../account';
|
|
5
|
+
import {
|
|
6
|
+
ArtifactTypePatterns,
|
|
7
|
+
excludeAllTypesExcept,
|
|
8
|
+
ExpectedArtifactService,
|
|
9
|
+
StageArtifactSelectorDelegate,
|
|
10
|
+
} from '../../../../../artifact';
|
|
11
|
+
import { StageConfigField } from '../../common/stageConfigField/StageConfigField';
|
|
12
|
+
import type { IArtifact, IExpectedArtifact } from '../../../../../domain';
|
|
13
|
+
import { MapEditor } from '../../../../../forms';
|
|
14
|
+
import { CheckboxInput, TextInput } from '../../../../../presentation';
|
|
15
|
+
|
|
16
|
+
export interface IBakeHelmfileConfigFormState {
|
|
17
|
+
gitRepoArtifactAccountNames: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class BakeHelmfileConfigForm extends React.Component<
|
|
21
|
+
IFormikStageConfigInjectedProps,
|
|
22
|
+
IBakeHelmfileConfigFormState
|
|
23
|
+
> {
|
|
24
|
+
constructor(props: IFormikStageConfigInjectedProps) {
|
|
25
|
+
super(props);
|
|
26
|
+
this.state = { gitRepoArtifactAccountNames: [] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private static readonly excludedArtifactTypes = excludeAllTypesExcept(
|
|
30
|
+
ArtifactTypePatterns.BITBUCKET_FILE,
|
|
31
|
+
ArtifactTypePatterns.CUSTOM_OBJECT,
|
|
32
|
+
ArtifactTypePatterns.EMBEDDED_BASE64,
|
|
33
|
+
ArtifactTypePatterns.GCS_OBJECT,
|
|
34
|
+
ArtifactTypePatterns.GIT_REPO,
|
|
35
|
+
ArtifactTypePatterns.GITHUB_FILE,
|
|
36
|
+
ArtifactTypePatterns.GITLAB_FILE,
|
|
37
|
+
ArtifactTypePatterns.S3_OBJECT,
|
|
38
|
+
ArtifactTypePatterns.HELM_CHART,
|
|
39
|
+
ArtifactTypePatterns.HTTP_FILE,
|
|
40
|
+
ArtifactTypePatterns.ORACLE_OBJECT,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
public componentDidMount() {
|
|
44
|
+
const stage = this.props.formik.values;
|
|
45
|
+
if (stage.inputArtifacts && stage.inputArtifacts.length === 0) {
|
|
46
|
+
this.props.formik.setFieldValue('inputArtifacts', [
|
|
47
|
+
{
|
|
48
|
+
account: '',
|
|
49
|
+
id: '',
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// If the Expected Artifact id is provided but the account is not, then attempt to find the artifact from
|
|
55
|
+
// upstream stages and set the account value.
|
|
56
|
+
// This is needed because helmfile file path field will need to be rendered if the artifact has a git repo account type
|
|
57
|
+
const expectedArtifact = this.getInputArtifact(stage, 0);
|
|
58
|
+
if (expectedArtifact.id && !expectedArtifact.account) {
|
|
59
|
+
const availableArtifacts = ExpectedArtifactService.getExpectedArtifactsAvailableToStage(
|
|
60
|
+
stage,
|
|
61
|
+
this.props.pipeline,
|
|
62
|
+
);
|
|
63
|
+
const expectedMatchedArtifact = availableArtifacts.find((a) => a.id === expectedArtifact.id);
|
|
64
|
+
if (expectedMatchedArtifact && expectedMatchedArtifact.matchArtifact) {
|
|
65
|
+
this.props.formik.setFieldValue(
|
|
66
|
+
`inputArtifacts[0].account`,
|
|
67
|
+
expectedMatchedArtifact.matchArtifact.artifactAccount,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
AccountService.getArtifactAccounts().then((artifactAccounts) => {
|
|
73
|
+
this.setState({
|
|
74
|
+
gitRepoArtifactAccountNames: artifactAccounts
|
|
75
|
+
.filter((account) => account.types.some((type) => ArtifactTypePatterns.GIT_REPO.test(type)))
|
|
76
|
+
.map((account) => account.name),
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private onTemplateArtifactEdited = (artifact: IArtifact, index: number) => {
|
|
82
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].id`, null);
|
|
83
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].artifact`, artifact);
|
|
84
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].account`, artifact.artifactAccount);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
private onTemplateArtifactSelected = (artifact: IExpectedArtifact, index: number) => {
|
|
88
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].id`, artifact.id);
|
|
89
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].artifact`, null);
|
|
90
|
+
// Set the account to matchArtifact.artifactAccount if it exists.
|
|
91
|
+
// This account value will be used to determine if the Helm Chart File Path should be displayed.
|
|
92
|
+
if (artifact.matchArtifact) {
|
|
93
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].account`, artifact.matchArtifact.artifactAccount);
|
|
94
|
+
} else {
|
|
95
|
+
this.props.formik.setFieldValue(`inputArtifacts[${index}].account`, null);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
private addInputArtifact = () => {
|
|
100
|
+
const stage = this.props.formik.values;
|
|
101
|
+
const newInputArtifacts = [
|
|
102
|
+
...stage.inputArtifacts,
|
|
103
|
+
{
|
|
104
|
+
account: '',
|
|
105
|
+
id: '',
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
this.props.formik.setFieldValue('inputArtifacts', newInputArtifacts);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
private removeInputArtifact = (index: number) => {
|
|
113
|
+
const stage = this.props.formik.values;
|
|
114
|
+
const newInputArtifacts = [...stage.inputArtifacts];
|
|
115
|
+
newInputArtifacts.splice(index, 1);
|
|
116
|
+
this.props.formik.setFieldValue('inputArtifacts', newInputArtifacts);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
private getInputArtifact = (stage: any, index: number) => {
|
|
120
|
+
if (!stage.inputArtifacts || stage.inputArtifacts.length === 0) {
|
|
121
|
+
return {
|
|
122
|
+
account: '',
|
|
123
|
+
id: '',
|
|
124
|
+
};
|
|
125
|
+
} else {
|
|
126
|
+
return stage.inputArtifacts[index];
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
private outputNameChange = (outputName: string) => {
|
|
131
|
+
const stage = this.props.formik.values;
|
|
132
|
+
const expectedArtifacts = stage.expectedArtifacts;
|
|
133
|
+
if (
|
|
134
|
+
expectedArtifacts &&
|
|
135
|
+
expectedArtifacts.length === 1 &&
|
|
136
|
+
expectedArtifacts[0].matchArtifact &&
|
|
137
|
+
expectedArtifacts[0].matchArtifact.type === 'embedded/base64'
|
|
138
|
+
) {
|
|
139
|
+
this.props.formik.setFieldValue('expectedArtifacts', [
|
|
140
|
+
{
|
|
141
|
+
...expectedArtifacts[0],
|
|
142
|
+
matchArtifact: {
|
|
143
|
+
...expectedArtifacts[0].matchArtifact,
|
|
144
|
+
name: outputName,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
private overrideChanged = (overrides: any) => {
|
|
152
|
+
this.props.formik.setFieldValue('overrides', overrides);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
public render() {
|
|
156
|
+
const stage = this.props.formik.values;
|
|
157
|
+
return (
|
|
158
|
+
<>
|
|
159
|
+
<h4>Helmfile Options</h4>
|
|
160
|
+
<StageConfigField fieldColumns={3} label={'Name'} helpKey="pipeline.config.bake.manifest.helmfile.name">
|
|
161
|
+
<TextInput
|
|
162
|
+
onChange={(e: React.ChangeEvent<any>) => {
|
|
163
|
+
this.props.formik.setFieldValue('outputName', e.target.value);
|
|
164
|
+
this.outputNameChange(e.target.value);
|
|
165
|
+
}}
|
|
166
|
+
value={stage.outputName}
|
|
167
|
+
/>
|
|
168
|
+
</StageConfigField>
|
|
169
|
+
<h4>Template Artifact</h4>
|
|
170
|
+
<StageArtifactSelectorDelegate
|
|
171
|
+
artifact={this.getInputArtifact(stage, 0).artifact}
|
|
172
|
+
excludedArtifactTypePatterns={BakeHelmfileConfigForm.excludedArtifactTypes}
|
|
173
|
+
expectedArtifactId={this.getInputArtifact(stage, 0).id}
|
|
174
|
+
helpKey="pipeline.config.bake.manifest.expectedArtifact"
|
|
175
|
+
label="Expected Artifact"
|
|
176
|
+
onArtifactEdited={(artifact) => {
|
|
177
|
+
this.onTemplateArtifactEdited(artifact, 0);
|
|
178
|
+
}}
|
|
179
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) => this.onTemplateArtifactSelected(artifact, 0)}
|
|
180
|
+
pipeline={this.props.pipeline}
|
|
181
|
+
stage={stage}
|
|
182
|
+
/>
|
|
183
|
+
{this.state.gitRepoArtifactAccountNames.includes(this.getInputArtifact(stage, 0).account) && (
|
|
184
|
+
<StageConfigField label="Helmfile File Path" helpKey="pipeline.config.bake.manifest.helmfile.filePath">
|
|
185
|
+
<TextInput
|
|
186
|
+
onChange={(e: React.ChangeEvent<any>) => {
|
|
187
|
+
this.props.formik.setFieldValue('helmfileFilePath', e.target.value);
|
|
188
|
+
}}
|
|
189
|
+
value={stage.helmfileFilePath}
|
|
190
|
+
/>
|
|
191
|
+
</StageConfigField>
|
|
192
|
+
)}
|
|
193
|
+
<h4>Overrides</h4>
|
|
194
|
+
{stage.inputArtifacts && stage.inputArtifacts.length > 1 && (
|
|
195
|
+
<div className="row form-group">
|
|
196
|
+
{stage.inputArtifacts.slice(1).map((a: any, index: number) => {
|
|
197
|
+
return (
|
|
198
|
+
<div key={index}>
|
|
199
|
+
<div className="col-md-offset-1 col-md-9">
|
|
200
|
+
<StageArtifactSelectorDelegate
|
|
201
|
+
artifact={a.artifact}
|
|
202
|
+
excludedArtifactTypePatterns={[]}
|
|
203
|
+
expectedArtifactId={a.id}
|
|
204
|
+
label="Expected Artifact"
|
|
205
|
+
onArtifactEdited={(artifact) => {
|
|
206
|
+
this.onTemplateArtifactEdited(artifact, index + 1);
|
|
207
|
+
}}
|
|
208
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) =>
|
|
209
|
+
this.onTemplateArtifactSelected(artifact, index + 1)
|
|
210
|
+
}
|
|
211
|
+
pipeline={this.props.pipeline}
|
|
212
|
+
stage={stage}
|
|
213
|
+
/>
|
|
214
|
+
</div>
|
|
215
|
+
<div className="col-md-1">
|
|
216
|
+
<div className="form-control-static">
|
|
217
|
+
<button onClick={() => this.removeInputArtifact(index + 1)}>
|
|
218
|
+
<span className="glyphicon glyphicon-trash" />
|
|
219
|
+
<span className="sr-only">Remove field</span>
|
|
220
|
+
</button>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
})}
|
|
226
|
+
</div>
|
|
227
|
+
)}
|
|
228
|
+
<StageConfigField fieldColumns={8} label={''}>
|
|
229
|
+
<button className="btn btn-block btn-sm add-new" onClick={() => this.addInputArtifact()}>
|
|
230
|
+
<span className="glyphicon glyphicon-plus-sign" />
|
|
231
|
+
Add value artifact
|
|
232
|
+
</button>
|
|
233
|
+
</StageConfigField>
|
|
234
|
+
<StageConfigField fieldColumns={6} label="Overrides">
|
|
235
|
+
{stage.overrides && (
|
|
236
|
+
<MapEditor
|
|
237
|
+
addButtonLabel={'Add override'}
|
|
238
|
+
model={stage.overrides}
|
|
239
|
+
allowEmpty={true}
|
|
240
|
+
onChange={(o: any) => this.overrideChanged(o)}
|
|
241
|
+
/>
|
|
242
|
+
)}
|
|
243
|
+
</StageConfigField>
|
|
244
|
+
<StageConfigField
|
|
245
|
+
fieldColumns={6}
|
|
246
|
+
helpKey={'pipeline.config.bake.manifest.helm.includeCRDs'}
|
|
247
|
+
label="Include CRDs"
|
|
248
|
+
>
|
|
249
|
+
<CheckboxInput
|
|
250
|
+
value={stage.includeCRDs}
|
|
251
|
+
text={''}
|
|
252
|
+
onChange={() => this.props.formik.setFieldValue('includeCRDs', !stage.includeCRDs)}
|
|
253
|
+
/>
|
|
254
|
+
</StageConfigField>
|
|
255
|
+
<StageConfigField
|
|
256
|
+
fieldColumns={6}
|
|
257
|
+
helpKey={'pipeline.config.bake.manifest.overrideExpressionEvaluation'}
|
|
258
|
+
label="Expression Evaluation"
|
|
259
|
+
>
|
|
260
|
+
<CheckboxInput
|
|
261
|
+
value={stage.evaluateOverrideExpressions}
|
|
262
|
+
text={'Evaluate SpEL expressions in overrides at bake time'}
|
|
263
|
+
onChange={() =>
|
|
264
|
+
this.props.formik.setFieldValue('evaluateOverrideExpressions', !stage.evaluateOverrideExpressions)
|
|
265
|
+
}
|
|
266
|
+
/>
|
|
267
|
+
</StageConfigField>
|
|
268
|
+
</>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IArtifact, IExecutionContext } from '../../../../../domain';
|
|
2
|
+
import { ARTIFACT_TYPE_EMBEDDED, ARTIFACT_TYPE_REMOTE } from '../../../../../domain';
|
|
3
|
+
|
|
4
|
+
// IArtifact type is wrong and does not represent the real value
|
|
5
|
+
export const getBakedArtifacts = (context: IExecutionContext): Array<IArtifact & { reference: string }> => {
|
|
6
|
+
if ('artifacts' in context) {
|
|
7
|
+
return context.artifacts.filter(
|
|
8
|
+
(a: IArtifact) => (a.type === ARTIFACT_TYPE_EMBEDDED || a.type === ARTIFACT_TYPE_REMOTE) && a.reference,
|
|
9
|
+
);
|
|
10
|
+
} else {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
};
|
|
@@ -11,4 +11,8 @@ export class ArtifactService {
|
|
|
11
11
|
.query({ type: type, artifactName: artifactName })
|
|
12
12
|
.get();
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
public static getArtifactByContentReference(contentRef: string): PromiseLike<{ reference: string }> {
|
|
16
|
+
return REST(`/artifacts/content-address/${contentRef}`).get();
|
|
17
|
+
}
|
|
14
18
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FormikProps } from 'formik';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { SETTINGS } from '../../../../config/settings';
|
|
5
|
+
import type { ICDEventsTrigger } from '../../../../domain';
|
|
6
|
+
import { MapEditorInput } from '../../../../forms';
|
|
7
|
+
import { HelpField } from '../../../../help';
|
|
8
|
+
import { FormikFormField, TextInput } from '../../../../presentation';
|
|
9
|
+
|
|
10
|
+
export interface ICDEventsTriggerProps {
|
|
11
|
+
formik: FormikProps<ICDEventsTrigger>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function CDEventsTrigger(cdeventsTriggerProps: ICDEventsTriggerProps) {
|
|
15
|
+
const { formik } = cdeventsTriggerProps;
|
|
16
|
+
const trigger = formik.values;
|
|
17
|
+
const { source, type } = trigger;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<FormikFormField
|
|
22
|
+
name="source"
|
|
23
|
+
label="Source"
|
|
24
|
+
help={<HelpField id="pipeline.config.trigger.webhook.source" />}
|
|
25
|
+
input={(props) => (
|
|
26
|
+
<div className="flex-container-v">
|
|
27
|
+
<TextInput {...props} />
|
|
28
|
+
<i>{`${SETTINGS.gateUrl}/webhooks/${type}/${source || '<source>'}`}</i>
|
|
29
|
+
</div>
|
|
30
|
+
)}
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
<FormikFormField
|
|
34
|
+
name="payloadConstraints"
|
|
35
|
+
label="Payload Constraints"
|
|
36
|
+
help={<HelpField id="pipeline.config.trigger.webhook.payloadConstraints" />}
|
|
37
|
+
input={(props) => <MapEditorInput {...props} addButtonLabel="Add payload constraint" />}
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
<FormikFormField
|
|
41
|
+
name="attributeConstraints"
|
|
42
|
+
label="Attribute Constraints "
|
|
43
|
+
help={<HelpField id="pipeline.config.trigger.cdevents.attributeConstraints" />}
|
|
44
|
+
input={(props) => <MapEditorInput {...props} addButtonLabel="Add attribute constraint" />}
|
|
45
|
+
/>
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CDEventsTrigger } from './CDEventsTrigger';
|
|
2
|
+
import { ArtifactTypePatterns } from '../../../../artifact';
|
|
3
|
+
import { Registry } from '../../../../registry';
|
|
4
|
+
|
|
5
|
+
Registry.pipeline.registerTrigger({
|
|
6
|
+
component: CDEventsTrigger,
|
|
7
|
+
description: 'Executes the pipeline when a CDEvents webhook is received.',
|
|
8
|
+
excludedArtifactTypePatterns: [ArtifactTypePatterns.JENKINS_FILE],
|
|
9
|
+
key: 'cdevents',
|
|
10
|
+
label: 'CDEvents',
|
|
11
|
+
validators: [
|
|
12
|
+
{
|
|
13
|
+
type: 'serviceAccountAccess',
|
|
14
|
+
message: `You do not have access to the service account configured in this pipeline's CDEvents trigger.
|
|
15
|
+
You will not be able to save your edits to this pipeline.`,
|
|
16
|
+
preventSave: true,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
});
|
|
@@ -129,7 +129,7 @@ export class CreatePipelineModal extends React.Component<ICreatePipelineModalPro
|
|
|
129
129
|
? this.getDefaultConfig()
|
|
130
130
|
: command.config;
|
|
131
131
|
|
|
132
|
-
pipelineConfig.name = command.name;
|
|
132
|
+
pipelineConfig.name = command.name.trim();
|
|
133
133
|
pipelineConfig.index = this.props.application.getDataSource('pipelineConfigs').data.length;
|
|
134
134
|
delete pipelineConfig.id;
|
|
135
135
|
|
package/src/pipeline/index.ts
CHANGED
|
@@ -23,3 +23,5 @@ export * from './manualExecution/TriggerTemplate';
|
|
|
23
23
|
export * from './service/ExecutionsTransformer';
|
|
24
24
|
export * from './service/execution.service';
|
|
25
25
|
export * from './status/ArtifactList';
|
|
26
|
+
export * from './config/stages/bakeManifest/utils/getBakedArtifacts';
|
|
27
|
+
export * from './config/stages/bakeManifest/utils/getContentReference';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useInternalValidator } from './hooks';
|
|
4
|
+
import type { IFormInputProps, OmitControlledInputPropsFrom } from './interface';
|
|
5
|
+
import { orEmptyString, validationClassName } from './utils';
|
|
6
|
+
import type { IValidator } from '../validation';
|
|
7
|
+
import { composeValidators, Validators } from '../validation';
|
|
8
|
+
|
|
9
|
+
interface INumberInputProps extends IFormInputProps, OmitControlledInputPropsFrom<React.InputHTMLAttributes<any>> {
|
|
10
|
+
inputClassName?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const isNumber = (val: any): val is number => typeof val === 'number';
|
|
14
|
+
|
|
15
|
+
export function NumberConcurrencyInput(props: INumberInputProps) {
|
|
16
|
+
const { value, validation, inputClassName, ...otherProps } = props;
|
|
17
|
+
|
|
18
|
+
const minMaxValidator: IValidator = (val: any, label?: string) => {
|
|
19
|
+
const minValidator = isNumber(props.min) ? Validators.minValue(props.min) : undefined;
|
|
20
|
+
const maxValidator = isNumber(props.max) ? Validators.maxValue(props.max) : undefined;
|
|
21
|
+
const validator = composeValidators([minValidator, maxValidator]);
|
|
22
|
+
return validator ? validator(val, label) : null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
useInternalValidator(validation, minMaxValidator);
|
|
26
|
+
|
|
27
|
+
const className = `NumberInput form-control ${orEmptyString(inputClassName)} ${validationClassName(validation)}`;
|
|
28
|
+
return <input className={className} type="number" value={orEmptyString(value)} {...otherProps} />;
|
|
29
|
+
}
|