@spinnaker/core 2026.1.2 → 2026.1.3

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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/spinnaker/spinnaker.git"
6
6
  },
7
7
  "license": "Apache-2.0",
8
- "version": "2026.1.2",
8
+ "version": "2026.1.3",
9
9
  "module": "dist/index.js",
10
10
  "typings": "dist/index.d.ts",
11
11
  "publishConfig": {
@@ -128,5 +128,5 @@
128
128
  "shx": "0.3.3",
129
129
  "typescript": "5.0.4"
130
130
  },
131
- "gitHead": "15f13ef44f58be042ff7fdbd3bb9747f0628a80c"
131
+ "gitHead": "f185bdabd7b71b84e25c9536cd82ca8775579e3d"
132
132
  }
@@ -1,169 +1,85 @@
1
1
  import { cloneDeep } from 'lodash';
2
2
  import React from 'react';
3
- import type { Option } from 'react-select';
4
3
 
5
- import { ArtifactService } from '../ArtifactService';
4
+ import { ArtifactEditor } from '../ArtifactEditor';
6
5
  import { ArtifactTypePatterns } from '../../../../../artifact/ArtifactTypes';
7
6
  import type { IArtifact, IArtifactEditorProps, IArtifactKindConfig } from '../../../../../domain';
8
- import { TetheredCreatable, TetheredSelect } from '../../../../../presentation';
7
+ import { singleFieldArtifactEditor } from '../singleFieldArtifactEditor';
9
8
  import { StageConfigField } from '../../../stages/common';
10
- import { Spinner } from '../../../../../widgets/spinners/Spinner';
9
+ import { SpelText } from '../../../../../widgets/spelText/SpelText';
11
10
 
12
11
  const TYPE = 'helm/image';
13
12
 
14
- interface IHelmImageArtifactEditorState {
15
- names: string[];
16
- versions: Array<Option<string>>;
17
- versionsLoading: boolean;
18
- namesLoading: boolean;
19
- }
20
-
21
- class HelmImageEditor extends React.Component<IArtifactEditorProps, IHelmImageArtifactEditorState> {
22
- public state: IHelmImageArtifactEditorState = {
23
- names: [],
24
- versions: [],
25
- versionsLoading: true,
26
- namesLoading: true,
27
- };
28
-
29
- // taken from https://github.com/semver/semver/issues/232
30
- private SEMVER = new RegExp(
31
- /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/,
32
- );
33
-
34
- constructor(props: IArtifactEditorProps) {
35
- super(props);
36
- const { artifact } = this.props;
37
- if (artifact.type !== TYPE) {
38
- const clonedArtifact = cloneDeep(artifact);
39
- clonedArtifact.type = TYPE;
40
- props.onChange(clonedArtifact);
41
- }
42
-
43
- ArtifactService.getArtifactNames(TYPE, this.props.account.name).then(
44
- (names) => {
45
- this.setState({
46
- names,
47
- namesLoading: false,
48
- });
49
- },
50
- () => {
51
- this.setState({
52
- names: [],
53
- namesLoading: false,
54
- versionsLoading: false,
55
- versions: [],
56
- });
57
- },
58
- );
59
- }
60
-
61
- public componentDidMount() {
62
- const { artifact } = this.props;
63
- if (artifact.name) {
64
- this.getChartVersionOptions(artifact.name);
65
- }
66
- }
67
-
68
- public componentDidUpdate(prevProps: IArtifactEditorProps) {
69
- if (this.props.account.name !== prevProps.account.name) {
70
- ArtifactService.getArtifactNames(TYPE, this.props.account.name).then(
71
- (names) => {
72
- this.setState({
73
- names,
74
- namesLoading: false,
75
- versions: [],
76
- });
77
- },
78
- () => {
79
- this.setState({
80
- names: [],
81
- namesLoading: false,
82
- versions: [],
83
- });
84
- },
85
- );
86
- }
13
+ const setNameAndVersionFromReference = (artifact: IArtifact) => {
14
+ const ref = artifact.reference;
15
+ if (ref == null) {
16
+ return artifact;
87
17
  }
88
18
 
89
- public render() {
90
- const { artifact } = this.props;
91
- const nameOptions = this.state.names.map((name) => ({ value: name, label: name }));
19
+ const atIndex = ref.indexOf('@');
20
+ const lastColonIndex = ref.lastIndexOf(':');
92
21
 
93
- return (
94
- <>
95
- <StageConfigField label="Name">
96
- {!this.state.namesLoading && (
97
- <TetheredSelect
98
- options={nameOptions}
99
- value={artifact.name || ''}
100
- onChange={(e: Option) => {
101
- this.onChange(e, 'name');
102
- this.getChartVersionOptions(e.value.toString());
103
- }}
104
- clearable={false}
105
- />
106
- )}
107
- {this.state.namesLoading && <Spinner />}
108
- </StageConfigField>
109
- <StageConfigField label="Version">
110
- {!this.state.versionsLoading && (
111
- <TetheredCreatable
112
- options={this.state.versions}
113
- value={artifact.version || ''}
114
- onChange={(e: Option) => {
115
- this.onChange(e, 'version');
116
- }}
117
- clearable={false}
118
- />
119
- )}
120
- {this.state.versionsLoading && <Spinner />}
121
- </StageConfigField>
122
- </>
123
- );
124
- }
125
-
126
- private onChange = (e: Option, field: keyof IArtifact) => {
127
- const clone = cloneDeep(this.props.artifact);
128
- (clone[field] as any) = e.value.toString();
129
- this.props.onChange(clone);
130
- };
131
-
132
- private getChartVersionOptions(chartName: string) {
133
- const { artifact, account } = this.props;
134
- this.setState({ versionsLoading: true });
135
- ArtifactService.getArtifactVersions(TYPE, account.name, chartName).then((versions: string[]) => {
136
- // if the version doesn't match SEMVER we assume that it's a regular expression or SpEL expression
137
- // and add it to the list of valid versions
138
- if (artifact.version && !this.SEMVER.test(artifact.version)) {
139
- versions = versions.concat(artifact.version);
140
- }
141
- this.setState({
142
- versions: versions.map((v) => ({ label: v, value: v })),
143
- versionsLoading: false,
144
- });
145
- });
22
+ if (atIndex >= 0) {
23
+ const split = ref.split('@');
24
+ artifact.name = split[0];
25
+ artifact.version = split[1];
26
+ } else if (lastColonIndex > 0) {
27
+ artifact.name = ref.substring(0, lastColonIndex);
28
+ artifact.version = ref.substring(lastColonIndex + 1);
29
+ } else {
30
+ artifact.name = ref;
146
31
  }
147
- }
32
+ return artifact;
33
+ };
148
34
 
149
35
  export const HelmImageMatch: IArtifactKindConfig = {
150
- label: 'Helm',
36
+ label: 'Docker Image (OCI)',
151
37
  typePattern: ArtifactTypePatterns.HELM_IMAGE,
152
38
  type: TYPE,
153
39
  isDefault: false,
154
40
  isMatch: true,
155
- description: 'A helm chart to be deployed',
156
- key: 'helm',
157
- editCmp: HelmImageEditor,
41
+ description: 'A Docker image stored in an OCI registry.',
42
+ key: 'helm-image',
43
+ editCmp: singleFieldArtifactEditor(
44
+ 'name',
45
+ TYPE,
46
+ 'Docker image',
47
+ 'gcr.io/project/image',
48
+ 'pipeline.config.expectedArtifact.docker.name',
49
+ ),
158
50
  };
159
51
 
160
52
  export const HelmImageDefault: IArtifactKindConfig = {
161
- label: 'Helm',
53
+ label: 'Docker Image (OCI)',
162
54
  typePattern: ArtifactTypePatterns.HELM_IMAGE,
163
55
  type: TYPE,
164
56
  isDefault: true,
165
57
  isMatch: false,
166
- description: 'A helm chart to be deployed',
167
- key: 'default.helm',
168
- editCmp: HelmImageEditor,
58
+ description: 'A Docker image stored in an OCI registry.',
59
+ key: 'default.helm-image',
60
+ editCmp: class extends ArtifactEditor {
61
+ constructor(props: IArtifactEditorProps) {
62
+ super(props, TYPE);
63
+ }
64
+
65
+ private onReferenceChanged = (reference: string) => {
66
+ const clonedArtifact = cloneDeep(this.props.artifact);
67
+ clonedArtifact.reference = reference;
68
+ this.props.onChange(setNameAndVersionFromReference(clonedArtifact));
69
+ };
70
+
71
+ public render() {
72
+ return (
73
+ <StageConfigField label="Object path" helpKey="pipeline.config.expectedArtifact.defaultDocker.reference">
74
+ <SpelText
75
+ placeholder="gcr.io/project/image@sha256:9efcc2818c9..."
76
+ value={this.props.artifact.reference}
77
+ onChange={this.onReferenceChanged}
78
+ pipeline={this.props.pipeline}
79
+ docLink={true}
80
+ />
81
+ </StageConfigField>
82
+ );
83
+ }
84
+ },
169
85
  };