@spinnaker/docker 2025.0.6 → 2025.1.1

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 CHANGED
@@ -1,4 +1,4 @@
1
- import { RetryService, REST, AccountService, ValidationMessage, HelpField, Tooltip, SETTINGS, Registry, BakeExecutionLabel, AuthenticationService, BakeryReader, TetheredSelect, Spinner } from '@spinnaker/core';
1
+ import { REST, RetryService, AccountService, ValidationMessage, HelpField, Tooltip, SETTINGS, Registry, BakeExecutionLabel, AuthenticationService, BakeryReader, TetheredSelect, Spinner } from '@spinnaker/core';
2
2
  import _, { groupBy, reduce, uniq, trim, get } from 'lodash';
3
3
  import React from 'react';
4
4
  import Select from 'react-select';
@@ -19,6 +19,17 @@ class DockerImageReader {
19
19
  return RetryService.buildRetrySequence(() => REST("/images/tags").query(params).get(), (results) => results.length > 0, 10, 1e3).then((results) => results).catch(() => []);
20
20
  }
21
21
  }
22
+ class DockerChartImageReader {
23
+ static getImage(imageName, region, credentials) {
24
+ return REST("/charts").path(credentials, region, imageName).query({ provider: "docker" }).get().then((results) => results && results.length ? results[0] : null).catch(() => null);
25
+ }
26
+ static findImages(params) {
27
+ return RetryService.buildRetrySequence(() => REST("/charts/find").query(params).get(), (results) => results.length > 0, 10, 1e3).then((results) => results).catch(() => []);
28
+ }
29
+ static findTags(params) {
30
+ return RetryService.buildRetrySequence(() => REST("/charts/tags").query(params).get(), (results) => results.length > 0, 10, 1e3).then((results) => results).catch(() => []);
31
+ }
32
+ }
22
33
 
23
34
  class DockerImageUtils {
24
35
  static splitImageId(imageId = "") {
@@ -57,8 +68,8 @@ class DockerImageUtils {
57
68
  }
58
69
  }
59
70
 
60
- const imageFields = ["organization", "repository", "tag", "digest"];
61
- const defineOptions = [
71
+ const imageFields$1 = ["organization", "repository", "tag", "digest"];
72
+ const defineOptions$1 = [
62
73
  { label: "Manually", value: true },
63
74
  { label: "Select from list", value: false }
64
75
  ];
@@ -301,7 +312,7 @@ class DockerImageAndTagSelector extends React.Component {
301
312
  }
302
313
  valueChanged(name, value) {
303
314
  const changes = { [name]: value };
304
- if (imageFields.some((n) => n === name)) {
315
+ if (imageFields$1.some((n) => n === name)) {
305
316
  const { organization, repository, tag, digest } = this.props;
306
317
  const imageParts = { ...{ organization, repository, tag, digest }, ...changes };
307
318
  const imageId = DockerImageUtils.generateImageId(imageParts);
@@ -354,7 +365,7 @@ class DockerImageAndTagSelector extends React.Component {
354
365
  value: defineManually,
355
366
  disabled: imagesLoading || !allowManualDefinition,
356
367
  onChange: (o) => this.showManualInput(o.value),
357
- options: defineOptions,
368
+ options: defineOptions$1,
358
369
  clearable: false
359
370
  })))));
360
371
  const warning = switchedManualWarning ? /* @__PURE__ */ React.createElement("div", {
@@ -743,155 +754,711 @@ window.angular.module("ng").run(["$templateCache", function(templateCache) {
743
754
  `);
744
755
  }]);
745
756
 
746
- function DockerTriggerConfig(props) {
747
- const { formik } = props;
748
- const trigger = formik.values;
749
- const dockerChanged = (changes) => {
750
- const { imageId, ...rest } = changes;
751
- props.triggerUpdated(rest);
752
- };
753
- return /* @__PURE__ */ React.createElement("div", {
754
- className: "form-horizontal"
755
- }, /* @__PURE__ */ React.createElement(DockerImageAndTagSelector, {
756
- allowManualDefinition: false,
757
- specifyTagByRegex: true,
758
- account: trigger.account,
759
- organization: trigger.organization,
760
- registry: trigger.registry,
761
- repository: trigger.repository,
762
- tag: trigger.tag,
763
- showRegistry: true,
764
- onChange: dockerChanged,
765
- showDigest: false
766
- }));
767
- }
768
-
769
- const lookupTypeOptions = [
770
- { value: "digest", label: "Digest" },
771
- { value: "tag", label: "Tag" }
757
+ const imageFields = ["organization", "repository", "tag", "digest"];
758
+ const defineOptions = [
759
+ { label: "Manually", value: true },
760
+ { label: "Select from list", value: false }
772
761
  ];
773
- class DockerTriggerTemplate extends React.Component {
762
+ class DockerChartAndTagSelector extends React.Component {
774
763
  constructor(props) {
775
764
  super(props);
776
- this.queryStream = new Subject();
777
- this.handleQuery = () => {
778
- const trigger = this.props.command.trigger;
779
- return from(DockerImageReader.findTags({
780
- provider: "dockerRegistry",
781
- account: trigger.account,
782
- repository: trigger.repository
783
- }));
765
+ this.unmounted = false;
766
+ this.cachedValues = {};
767
+ this.handleRefreshImages = () => {
768
+ this.refreshImages(this.props);
784
769
  };
785
770
  this.lookupTypeChanged = (o) => {
786
771
  const newType = o.value;
787
- this.updateArtifact(this.props.command, newType === "tag" ? this.state.selectedTag : this.state.digest);
788
- this.setState({ lookupType: newType });
789
- };
790
- this.updateSelectedTag = (tag) => {
791
- this.updateArtifact(this.props.command, tag);
792
- this.setState({ selectedTag: tag });
793
- this.props.command.triggerInvalid = false;
794
- };
795
- this.updateDigest = (digest) => {
796
- this.updateArtifact(this.props.command, digest);
797
- this.setState({ digest });
798
- };
799
- this.tagLoadSuccess = (tags) => {
800
- const { command } = this.props;
801
- const trigger = command.trigger;
802
- const newState = {};
803
- newState.tags = tags || [];
804
- const defaultSelection = newState.tags.find((t) => t === trigger.tag);
805
- if (defaultSelection) {
806
- newState.selectedTag = defaultSelection;
807
- this.updateSelectedTag(defaultSelection);
772
+ const oldType = this.state.lookupType;
773
+ const oldValue = this.props[oldType];
774
+ const cachedValue = this.cachedValues[newType];
775
+ this.valueChanged(oldType, void 0);
776
+ if (this.cachedValues[newType]) {
777
+ this.valueChanged(newType, cachedValue);
808
778
  }
809
- newState.tagsLoading = false;
810
- this.setState(newState);
811
- };
812
- this.tagLoadFailure = () => {
813
- this.setState({
814
- tagsLoading: false,
815
- loadError: true
816
- });
779
+ this.setState({ lookupType: newType });
780
+ this.cachedValues[oldType] = oldValue;
817
781
  };
818
- this.initialize = () => {
819
- const { command } = this.props;
820
- this.props.updateCommand("triggerInvalid", true);
821
- this.props.updateCommand("extraFields", {
822
- tag: get(command, "extraFields.tag", ""),
823
- artifacts: get(command, "extraFields.artifacts", "")
824
- });
825
- if (this.subscription) {
826
- this.subscription.unsubscribe();
827
- }
828
- if (command.trigger.type !== "docker") {
829
- return;
782
+ this.showManualInput = (defineManually) => {
783
+ if (!defineManually) {
784
+ const newFields = DockerImageUtils.splitImageId(this.props.imageId || "");
785
+ this.props.onChange(newFields);
786
+ if (this.state.switchedManualWarning) {
787
+ this.setState({ switchedManualWarning: void 0, missingFields: void 0 });
788
+ }
830
789
  }
831
- this.subscription = this.queryStream.pipe(debounceTime(250), switchMap(this.handleQuery)).subscribe(this.tagLoadSuccess, this.tagLoadFailure);
832
- this.searchTags();
833
- };
834
- this.searchTags = (query = "") => {
835
- this.setState({ tags: [`<span>Finding tags${query && ` matching ${query}`}...</span>`] });
836
- this.queryStream.next();
790
+ this.setState({ defineManually });
837
791
  };
792
+ const accountOptions = props.account ? [{ label: props.account, value: props.account }] : [];
793
+ const organizationOptions = props.organization && props.organization.length ? [{ label: props.organization, value: props.organization }] : [];
794
+ const repositoryOptions = props.repository && props.repository.length ? [{ label: props.repository, value: props.repository }] : [];
795
+ const tagOptions = props.tag && props.tag.length ? [{ label: props.tag, value: props.tag }] : [];
796
+ const parsedImageId = DockerImageUtils.splitImageId(props.imageId);
797
+ const defineManually = props.allowManualDefinition && Boolean(props.imageId && props.imageId.includes("${"));
838
798
  this.state = {
839
- digest: "",
840
- tags: [],
841
- tagsLoading: true,
842
- loadError: false,
843
- lookupType: "tag",
844
- selectedTag: ""
799
+ accountOptions,
800
+ switchedManualWarning: void 0,
801
+ imagesLoaded: false,
802
+ imagesLoading: false,
803
+ organizationOptions,
804
+ repositoryOptions,
805
+ defineManually,
806
+ tagOptions,
807
+ lookupType: props.digest || parsedImageId.digest ? "digest" : "tag"
845
808
  };
846
809
  }
847
- static formatLabel(trigger) {
848
- return $q.when(`(Docker Registry) ${trigger.account ? trigger.account + ":" : ""} ${trigger.repository || ""}`);
810
+ getAccountMap(images) {
811
+ const groupedImages = groupBy(images.filter((image) => image.account), "account");
812
+ return reduce(groupedImages, (acc, image, key) => {
813
+ acc[key] = uniq(image.map((i) => `${i.repository.split("/").slice(0, -1).join("/")}`));
814
+ return acc;
815
+ }, {});
849
816
  }
850
- updateArtifact(command, tagOrDigest) {
851
- this.props.updateCommand("extraFields.tag", tagOrDigest);
852
- const trigger = command.trigger;
853
- if (trigger && trigger.repository) {
854
- let imageName = "";
855
- if (trigger.registry) {
856
- imageName += trigger.registry + "/";
857
- }
858
- imageName += trigger.repository;
859
- let imageReference = "";
860
- if (this.state.lookupType === "digest") {
861
- imageReference = `${imageName}@${tagOrDigest}`;
862
- } else {
863
- imageReference = `${imageName}:${tagOrDigest}`;
817
+ getRegistryMap(images) {
818
+ return images.reduce((m, image) => {
819
+ m[image.account] = image.registry;
820
+ return m;
821
+ }, {});
822
+ }
823
+ getOrganizationMap(images) {
824
+ const extractGroupByKey = (image) => `${image.account}/${image.repository.split("/").slice(0, -1).join("/")}`;
825
+ const groupedImages = groupBy(images.filter((image) => image.repository), extractGroupByKey);
826
+ return reduce(groupedImages, (acc, image, key) => {
827
+ acc[key] = uniq(image.map((i) => i.repository));
828
+ return acc;
829
+ }, {});
830
+ }
831
+ getRepositoryMap(images) {
832
+ const groupedImages = groupBy(images.filter((image) => image.account), "repository");
833
+ return reduce(groupedImages, (acc, image, key) => {
834
+ acc[key] = uniq(image.map((i) => i.tag));
835
+ return acc;
836
+ }, {});
837
+ }
838
+ getOrganizationsList(accountMap) {
839
+ return accountMap ? accountMap[this.props.showRegistry ? this.props.account : this.props.registry] || [] : [];
840
+ }
841
+ getRepositoryList(organizationMap, organization, registry) {
842
+ if (organizationMap) {
843
+ const key = `${this.props.showRegistry ? this.props.account : registry}/${organization || ""}`;
844
+ return organizationMap[key] || [];
845
+ }
846
+ return [];
847
+ }
848
+ getTags(tag, repositoryMap, repository) {
849
+ let tags = [];
850
+ if (this.props.specifyTagByRegex) {
851
+ if (tag && trim(tag) === "") {
852
+ tag = void 0;
864
853
  }
865
- this.props.updateCommand("extraFields.artifacts", [
866
- {
867
- type: "docker/image",
868
- name: imageName,
869
- version: tagOrDigest,
870
- reference: imageReference
854
+ } else {
855
+ if (repositoryMap) {
856
+ tags = repositoryMap[repository] || [];
857
+ if (!tags.includes(tag) && tag && !tag.includes("${")) {
858
+ tag = void 0;
871
859
  }
872
- ]);
860
+ }
873
861
  }
862
+ return { tag, tags };
874
863
  }
875
- componentDidMount() {
876
- this.initialize();
864
+ componentWillReceiveProps(nextProps) {
865
+ if (!this.images || ["account", "showRegistry"].some((key) => this.props[key] !== nextProps[key])) {
866
+ this.refreshImages(nextProps);
867
+ } else if (["organization", "registry", "repository"].some((key) => this.props[key] !== nextProps[key])) {
868
+ this.updateThings(nextProps);
869
+ }
870
+ if (nextProps.imageId && nextProps.imageId.includes("${")) {
871
+ this.setState({ defineManually: true });
872
+ }
877
873
  }
878
874
  componentWillUnmount() {
879
- if (this.subscription) {
880
- this.subscription.unsubscribe();
875
+ this.unmounted = true;
876
+ }
877
+ synchronizeChanges(values, registry) {
878
+ const { organization, repository, tag, digest } = values;
879
+ if (this.props.onChange) {
880
+ const imageId = DockerImageUtils.generateImageId({ organization, repository, tag, digest });
881
+ const changes = {};
882
+ if (tag !== this.props.tag) {
883
+ changes.tag = tag;
884
+ }
885
+ if (imageId !== this.props.imageId) {
886
+ changes.imageId = imageId;
887
+ }
888
+ if (organization !== this.props.organization) {
889
+ changes.organization = organization;
890
+ }
891
+ if (registry !== this.props.registry) {
892
+ changes.registry = registry;
893
+ }
894
+ if (repository !== this.props.repository) {
895
+ changes.repository = repository;
896
+ }
897
+ if (digest !== this.props.digest) {
898
+ changes.digest = digest;
899
+ }
900
+ if (Object.keys(changes).length > 0) {
901
+ this.props.onChange(changes);
902
+ }
881
903
  }
882
904
  }
883
- render() {
884
- const { digest, tags, tagsLoading, loadError, selectedTag, lookupType } = this.state;
885
- const options = tags.map((tag) => {
886
- return { value: tag };
887
- });
888
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
889
- className: "form-group"
890
- }, /* @__PURE__ */ React.createElement("div", {
891
- className: "sm-label-right col-md-4"
892
- }, "Type"), /* @__PURE__ */ React.createElement("div", {
893
- className: "col-md-3"
894
- }, /* @__PURE__ */ React.createElement(TetheredSelect, {
905
+ updateThings(props, allowAutoSwitchToManualEntry = false) {
906
+ if (!this.repositoryMap || this.unmounted) {
907
+ return;
908
+ }
909
+ const { imageId, specifyTagByRegex } = props;
910
+ let { organization, registry, repository } = props;
911
+ if (props.showRegistry) {
912
+ registry = this.registryMap[props.account];
913
+ }
914
+ const organizationFound = !organization || this.organizations.includes(organization) || organization.includes("${");
915
+ if (!organizationFound) {
916
+ organization = "";
917
+ }
918
+ const repositories = this.getRepositoryList(this.organizationMap, organization, registry);
919
+ const repositoryFound = !repository || repository.includes("${") || repositories.includes(repository);
920
+ if (!repositoryFound) {
921
+ repository = "";
922
+ }
923
+ const { tag, tags } = this.getTags(props.tag, this.repositoryMap, repository);
924
+ const tagFound = tag === props.tag || specifyTagByRegex;
925
+ const newState = {
926
+ accountOptions: this.newAccounts.sort().map((a) => ({ label: a, value: a })),
927
+ organizationOptions: this.organizations.filter((o) => o).sort().map((o) => ({ label: o, value: o })),
928
+ imagesLoaded: true,
929
+ repositoryOptions: repositories.sort().map((r) => ({ label: r, value: r })),
930
+ tagOptions: tags.sort().map((t) => ({ label: t, value: t }))
931
+ };
932
+ if (imageId && (!this.state.imagesLoaded || allowAutoSwitchToManualEntry) && (!organizationFound || !repositoryFound || !tagFound)) {
933
+ newState.defineManually = true;
934
+ const missingFields = [];
935
+ if (!organizationFound) {
936
+ missingFields.push("organization");
937
+ }
938
+ if (!repositoryFound) {
939
+ missingFields.push("image");
940
+ }
941
+ if (!tagFound) {
942
+ missingFields.push("tag");
943
+ }
944
+ newState.missingFields = missingFields;
945
+ newState.switchedManualWarning = `Could not find ${missingFields.join(" or ")}, switched to manual entry`;
946
+ } else if (!imageId || !imageId.includes("${")) {
947
+ this.synchronizeChanges(this.state.defineManually ? DockerImageUtils.splitImageId(imageId) : { organization, repository, tag, digest: this.props.digest }, registry);
948
+ }
949
+ this.setState(newState);
950
+ }
951
+ initializeImages(props) {
952
+ if (this.state.imagesLoading) {
953
+ return;
954
+ }
955
+ const { showRegistry, account, registry } = props;
956
+ const imageConfig = {
957
+ provider: "dockerRegistry",
958
+ account: showRegistry ? account : registry
959
+ };
960
+ this.setState({ imagesLoading: true });
961
+ DockerChartImageReader.findImages(imageConfig).then((images) => {
962
+ this.images = images;
963
+ this.registryMap = this.getRegistryMap(this.images);
964
+ this.accountMap = this.getAccountMap(this.images);
965
+ this.newAccounts = this.accounts || Object.keys(this.accountMap);
966
+ this.organizationMap = this.getOrganizationMap(this.images);
967
+ this.repositoryMap = this.getRepositoryMap(this.images);
968
+ this.organizations = this.getOrganizationsList(this.accountMap);
969
+ this.updateThings(props, true);
970
+ }).finally(() => {
971
+ if (!this.unmounted) {
972
+ this.setState({ imagesLoading: false });
973
+ }
974
+ });
975
+ }
976
+ refreshImages(props) {
977
+ this.initializeImages(props);
978
+ }
979
+ initializeAccounts(props) {
980
+ let { account } = props;
981
+ AccountService.listAccounts("dockerRegistry").then((allAccounts) => {
982
+ const accounts = allAccounts.map((a) => a.name);
983
+ if (this.props.showRegistry && !account) {
984
+ account = accounts[0];
985
+ }
986
+ this.accounts = accounts;
987
+ this.refreshImages({ ...props, ...{ account } });
988
+ });
989
+ }
990
+ isNew() {
991
+ const { account, organization, registry, repository, tag } = this.props;
992
+ return !account && !organization && !registry && !repository && !tag;
993
+ }
994
+ componentDidMount() {
995
+ if (!this.props.deferInitialization && (this.props.registry || this.isNew())) {
996
+ this.initializeAccounts(this.props);
997
+ }
998
+ }
999
+ valueChanged(name, value) {
1000
+ const changes = { [name]: value };
1001
+ if (imageFields.some((n) => n === name)) {
1002
+ const { organization, repository, tag, digest } = this.props;
1003
+ const imageParts = { ...{ organization, repository, tag, digest }, ...changes };
1004
+ const imageId = DockerImageUtils.generateImageId(imageParts);
1005
+ changes.imageId = imageId;
1006
+ }
1007
+ this.props.onChange && this.props.onChange(changes);
1008
+ }
1009
+ render() {
1010
+ const {
1011
+ account,
1012
+ allowManualDefinition,
1013
+ digest,
1014
+ imageId,
1015
+ organization,
1016
+ repository,
1017
+ showDigest,
1018
+ showRegistry,
1019
+ specifyTagByRegex,
1020
+ tag
1021
+ } = this.props;
1022
+ const {
1023
+ accountOptions,
1024
+ switchedManualWarning,
1025
+ missingFields,
1026
+ imagesLoading,
1027
+ lookupType,
1028
+ organizationOptions,
1029
+ repositoryOptions,
1030
+ defineManually,
1031
+ tagOptions
1032
+ } = this.state;
1033
+ const parsedImageId = DockerImageUtils.splitImageId(imageId);
1034
+ const manualInputToggle = /* @__PURE__ */ React.createElement("div", {
1035
+ className: "sp-formItem groupHeader"
1036
+ }, /* @__PURE__ */ React.createElement("div", {
1037
+ className: "sp-formItem__left"
1038
+ }, /* @__PURE__ */ React.createElement("div", {
1039
+ className: "sp-formLabel"
1040
+ }, "Define Image ID"), /* @__PURE__ */ React.createElement("div", {
1041
+ className: "sp-formActions sp-formActions--mobile"
1042
+ }, /* @__PURE__ */ React.createElement("span", {
1043
+ className: "action"
1044
+ }))), /* @__PURE__ */ React.createElement("div", {
1045
+ className: "sp-formItem__right"
1046
+ }, /* @__PURE__ */ React.createElement("div", {
1047
+ className: "sp-form"
1048
+ }, /* @__PURE__ */ React.createElement("span", {
1049
+ className: "field"
1050
+ }, /* @__PURE__ */ React.createElement(Select, {
1051
+ value: defineManually,
1052
+ disabled: imagesLoading || !allowManualDefinition,
1053
+ onChange: (o) => this.showManualInput(o.value),
1054
+ options: defineOptions,
1055
+ clearable: false
1056
+ })))));
1057
+ const warning = switchedManualWarning ? /* @__PURE__ */ React.createElement("div", {
1058
+ className: "sp-formItem"
1059
+ }, /* @__PURE__ */ React.createElement("div", {
1060
+ className: "sp-formItem__left"
1061
+ }), /* @__PURE__ */ React.createElement("div", {
1062
+ className: "sp-formItem__right"
1063
+ }, /* @__PURE__ */ React.createElement(ValidationMessage, {
1064
+ type: "warning",
1065
+ message: /* @__PURE__ */ React.createElement(React.Fragment, null, switchedManualWarning, (missingFields || []).map((f) => /* @__PURE__ */ React.createElement("div", {
1066
+ key: f
1067
+ }, /* @__PURE__ */ React.createElement(HelpField, {
1068
+ expand: true,
1069
+ id: `pipeline.config.docker.trigger.missing.${f}`
1070
+ }))))
1071
+ }))) : null;
1072
+ if (defineManually) {
1073
+ return /* @__PURE__ */ React.createElement("div", {
1074
+ className: "sp-formGroup"
1075
+ }, manualInputToggle, /* @__PURE__ */ React.createElement("div", {
1076
+ className: "sp-formItem"
1077
+ }, /* @__PURE__ */ React.createElement("div", {
1078
+ className: "sp-formItem__left"
1079
+ }, /* @__PURE__ */ React.createElement("div", {
1080
+ className: "sp-formLabel"
1081
+ }, "Image ID"), /* @__PURE__ */ React.createElement("div", {
1082
+ className: "sp-formActions sp-formActions--mobile"
1083
+ }, /* @__PURE__ */ React.createElement("span", {
1084
+ className: "action"
1085
+ }))), /* @__PURE__ */ React.createElement("div", {
1086
+ className: "sp-formItem__right"
1087
+ }, /* @__PURE__ */ React.createElement("div", {
1088
+ className: "sp-form"
1089
+ }, /* @__PURE__ */ React.createElement("span", {
1090
+ className: "field"
1091
+ }, /* @__PURE__ */ React.createElement("input", {
1092
+ className: "form-control input-sm",
1093
+ value: imageId || "",
1094
+ onChange: (e) => this.valueChanged("imageId", e.target.value)
1095
+ }))))), warning);
1096
+ }
1097
+ const Registry = showRegistry ? /* @__PURE__ */ React.createElement("div", {
1098
+ className: "sp-formItem"
1099
+ }, /* @__PURE__ */ React.createElement("div", {
1100
+ className: "sp-formItem__left"
1101
+ }, /* @__PURE__ */ React.createElement("div", {
1102
+ className: "sp-formLabel"
1103
+ }, "Registry Name")), /* @__PURE__ */ React.createElement("div", {
1104
+ className: "sp-formItem__right"
1105
+ }, /* @__PURE__ */ React.createElement("div", {
1106
+ className: "sp-form"
1107
+ }, /* @__PURE__ */ React.createElement("span", {
1108
+ className: "field"
1109
+ }, /* @__PURE__ */ React.createElement(Select, {
1110
+ value: account,
1111
+ disabled: imagesLoading,
1112
+ onChange: (o) => this.valueChanged("account", o ? o.value : ""),
1113
+ options: accountOptions,
1114
+ isLoading: imagesLoading
1115
+ })), /* @__PURE__ */ React.createElement("span", {
1116
+ className: "sp-formActions sp-formActions--web"
1117
+ }, /* @__PURE__ */ React.createElement("span", {
1118
+ className: "action"
1119
+ }, /* @__PURE__ */ React.createElement(Tooltip, {
1120
+ value: imagesLoading ? "Images refreshing" : "Refresh images list"
1121
+ }, /* @__PURE__ */ React.createElement("i", {
1122
+ className: `fa icon-button-refresh-arrows ${imagesLoading ? "fa-spin" : ""}`,
1123
+ onClick: this.handleRefreshImages
1124
+ }))))))) : null;
1125
+ const Organization = /* @__PURE__ */ React.createElement("div", {
1126
+ className: "sp-formItem"
1127
+ }, /* @__PURE__ */ React.createElement("div", {
1128
+ className: "sp-formItem__left"
1129
+ }, /* @__PURE__ */ React.createElement("div", {
1130
+ className: "sp-formLabel"
1131
+ }, "Organization")), /* @__PURE__ */ React.createElement("div", {
1132
+ className: "sp-formItem__right"
1133
+ }, /* @__PURE__ */ React.createElement("div", {
1134
+ className: "sp-form"
1135
+ }, /* @__PURE__ */ React.createElement("span", {
1136
+ className: "field"
1137
+ }, organization.includes("${") ? /* @__PURE__ */ React.createElement("input", {
1138
+ disabled: imagesLoading,
1139
+ className: "form-control input-sm",
1140
+ value: organization || "",
1141
+ onChange: (e) => this.valueChanged("organization", e.target.value)
1142
+ }) : /* @__PURE__ */ React.createElement(Select, {
1143
+ value: organization || "",
1144
+ disabled: imagesLoading,
1145
+ onChange: (o) => this.valueChanged("organization", o && o.value || ""),
1146
+ placeholder: "No organization",
1147
+ options: organizationOptions,
1148
+ isLoading: imagesLoading
1149
+ })))));
1150
+ const Image = /* @__PURE__ */ React.createElement("div", {
1151
+ className: "sp-formItem"
1152
+ }, /* @__PURE__ */ React.createElement("div", {
1153
+ className: "sp-formItem__left"
1154
+ }, /* @__PURE__ */ React.createElement("div", {
1155
+ className: "sp-formLabel"
1156
+ }, "Image")), /* @__PURE__ */ React.createElement("div", {
1157
+ className: "sp-formItem__right"
1158
+ }, /* @__PURE__ */ React.createElement("div", {
1159
+ className: "sp-form"
1160
+ }, /* @__PURE__ */ React.createElement("span", {
1161
+ className: "field"
1162
+ }, repository.includes("${") ? /* @__PURE__ */ React.createElement("input", {
1163
+ className: "form-control input-sm",
1164
+ disabled: imagesLoading,
1165
+ value: repository || "",
1166
+ onChange: (e) => this.valueChanged("repository", e.target.value)
1167
+ }) : /* @__PURE__ */ React.createElement(Select, {
1168
+ value: repository || "",
1169
+ disabled: imagesLoading,
1170
+ onChange: (o) => this.valueChanged("repository", o && o.value || ""),
1171
+ options: repositoryOptions,
1172
+ required: true,
1173
+ isLoading: imagesLoading
1174
+ })))));
1175
+ const Tag = lookupType === "tag" ? specifyTagByRegex ? /* @__PURE__ */ React.createElement("div", {
1176
+ className: "sp-formItem"
1177
+ }, /* @__PURE__ */ React.createElement("div", {
1178
+ className: "sp-formItem__left"
1179
+ }, /* @__PURE__ */ React.createElement("div", {
1180
+ className: "sp-formLabel"
1181
+ }, "Tag")), /* @__PURE__ */ React.createElement("div", {
1182
+ className: "sp-formItem__right"
1183
+ }, /* @__PURE__ */ React.createElement("div", {
1184
+ className: "sp-form"
1185
+ }, /* @__PURE__ */ React.createElement("span", {
1186
+ className: "field"
1187
+ }, /* @__PURE__ */ React.createElement("input", {
1188
+ type: "text",
1189
+ className: "form-control input-sm",
1190
+ value: tag || "",
1191
+ disabled: imagesLoading || !repository,
1192
+ onChange: (e) => this.valueChanged("tag", e.target.value)
1193
+ }))), /* @__PURE__ */ React.createElement(HelpField, {
1194
+ id: "pipeline.config.docker.trigger.tag",
1195
+ expand: true
1196
+ }))) : /* @__PURE__ */ React.createElement("div", {
1197
+ className: "sp-formItem"
1198
+ }, /* @__PURE__ */ React.createElement("div", {
1199
+ className: "sp-formItem__left"
1200
+ }, /* @__PURE__ */ React.createElement("div", {
1201
+ className: "sp-formLabel"
1202
+ }, "Tag")), /* @__PURE__ */ React.createElement("div", {
1203
+ className: "sp-formItem__right"
1204
+ }, /* @__PURE__ */ React.createElement("div", {
1205
+ className: "sp-form"
1206
+ }, /* @__PURE__ */ React.createElement("span", {
1207
+ className: "field"
1208
+ }, tag && tag.includes("${") ? /* @__PURE__ */ React.createElement("input", {
1209
+ className: "form-control input-sm",
1210
+ disabled: imagesLoading,
1211
+ value: tag || "",
1212
+ onChange: (e) => this.valueChanged("tag", e.target.value),
1213
+ required: true
1214
+ }) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Select, {
1215
+ value: tag || "",
1216
+ disabled: imagesLoading || !repository,
1217
+ isLoading: imagesLoading,
1218
+ onChange: (o) => this.valueChanged("tag", o ? o.value : void 0),
1219
+ options: tagOptions,
1220
+ placeholder: "No tag",
1221
+ required: true
1222
+ }), /* @__PURE__ */ React.createElement(HelpField, {
1223
+ id: "pipeline.config.docker.trigger.tag.additionalInfo",
1224
+ expand: true
1225
+ })))))) : null;
1226
+ const Digest = lookupType === "digest" ? /* @__PURE__ */ React.createElement("div", {
1227
+ className: "sp-formItem"
1228
+ }, /* @__PURE__ */ React.createElement("div", {
1229
+ className: "sp-formItem__left"
1230
+ }, /* @__PURE__ */ React.createElement("div", {
1231
+ className: "sp-formLabel"
1232
+ }, "Digest ", /* @__PURE__ */ React.createElement(HelpField, {
1233
+ id: "pipeline.config.docker.trigger.digest"
1234
+ }))), /* @__PURE__ */ React.createElement("div", {
1235
+ className: "sp-formItem__right"
1236
+ }, /* @__PURE__ */ React.createElement("div", {
1237
+ className: "sp-form"
1238
+ }, /* @__PURE__ */ React.createElement("span", {
1239
+ className: "field"
1240
+ }, /* @__PURE__ */ React.createElement("input", {
1241
+ className: "form-control input-sm",
1242
+ placeholder: "sha256:abc123",
1243
+ value: digest || parsedImageId.digest || "",
1244
+ onChange: (e) => this.valueChanged("digest", e.target.value),
1245
+ required: true
1246
+ }))))) : null;
1247
+ const LookupTypeSelector = showDigest ? /* @__PURE__ */ React.createElement("div", {
1248
+ className: "sp-formItem"
1249
+ }, /* @__PURE__ */ React.createElement("div", {
1250
+ className: "sp-formItem__left"
1251
+ }, /* @__PURE__ */ React.createElement("div", {
1252
+ className: "sp-formLabel"
1253
+ }, "Type")), /* @__PURE__ */ React.createElement("div", {
1254
+ className: "sp-formItem__right"
1255
+ }, /* @__PURE__ */ React.createElement("div", {
1256
+ className: "sp-form"
1257
+ }, /* @__PURE__ */ React.createElement("span", {
1258
+ className: "field"
1259
+ }, /* @__PURE__ */ React.createElement(Select, {
1260
+ clearable: false,
1261
+ value: lookupType,
1262
+ options: [
1263
+ { value: "digest", label: "Digest" },
1264
+ { value: "tag", label: "Tag" }
1265
+ ],
1266
+ onChange: this.lookupTypeChanged
1267
+ }))))) : null;
1268
+ return /* @__PURE__ */ React.createElement("div", {
1269
+ className: "sp-formGroup"
1270
+ }, manualInputToggle, Registry, Organization, Image, LookupTypeSelector, Digest, Tag);
1271
+ }
1272
+ }
1273
+ DockerChartAndTagSelector.defaultProps = {
1274
+ organization: "",
1275
+ registry: "",
1276
+ repository: "",
1277
+ showDigest: true,
1278
+ allowManualDefinition: true
1279
+ };
1280
+
1281
+ function DockerHelmOciTriggerConfig(props) {
1282
+ const { formik } = props;
1283
+ const trigger = formik.values;
1284
+ const dockerChanged = (changes) => {
1285
+ const { imageId, ...rest } = changes;
1286
+ props.triggerUpdated(rest);
1287
+ };
1288
+ return /* @__PURE__ */ React.createElement("div", {
1289
+ className: "form-horizontal"
1290
+ }, /* @__PURE__ */ React.createElement(DockerChartAndTagSelector, {
1291
+ allowManualDefinition: false,
1292
+ specifyTagByRegex: true,
1293
+ account: trigger.account,
1294
+ organization: trigger.organization,
1295
+ registry: trigger.registry,
1296
+ repository: trigger.repository,
1297
+ tag: trigger.tag,
1298
+ showRegistry: true,
1299
+ onChange: dockerChanged,
1300
+ showDigest: false
1301
+ }));
1302
+ }
1303
+
1304
+ function DockerTriggerConfig(props) {
1305
+ const { formik } = props;
1306
+ const trigger = formik.values;
1307
+ const dockerChanged = (changes) => {
1308
+ const { imageId, ...rest } = changes;
1309
+ props.triggerUpdated(rest);
1310
+ };
1311
+ return /* @__PURE__ */ React.createElement("div", {
1312
+ className: "form-horizontal"
1313
+ }, /* @__PURE__ */ React.createElement(DockerImageAndTagSelector, {
1314
+ allowManualDefinition: false,
1315
+ specifyTagByRegex: true,
1316
+ account: trigger.account,
1317
+ organization: trigger.organization,
1318
+ registry: trigger.registry,
1319
+ repository: trigger.repository,
1320
+ tag: trigger.tag,
1321
+ showRegistry: true,
1322
+ onChange: dockerChanged,
1323
+ showDigest: false
1324
+ }));
1325
+ }
1326
+
1327
+ const lookupTypeOptions = [
1328
+ { value: "digest", label: "Digest" },
1329
+ { value: "tag", label: "Tag" }
1330
+ ];
1331
+ class DockerTriggerTemplate extends React.Component {
1332
+ constructor(props) {
1333
+ super(props);
1334
+ this.queryStream = new Subject();
1335
+ this.handleQuery = () => {
1336
+ const trigger = this.props.command.trigger;
1337
+ if (trigger.type === "helm/oci") {
1338
+ return from(DockerChartImageReader.findTags({
1339
+ provider: "dockerRegistry",
1340
+ account: trigger.account,
1341
+ repository: trigger.repository
1342
+ }));
1343
+ } else {
1344
+ return from(DockerImageReader.findTags({
1345
+ provider: "dockerRegistry",
1346
+ account: trigger.account,
1347
+ repository: trigger.repository
1348
+ }));
1349
+ }
1350
+ };
1351
+ this.lookupTypeChanged = (o) => {
1352
+ const newType = o.value;
1353
+ this.updateArtifact(this.props.command, newType === "tag" ? this.state.selectedTag : this.state.digest);
1354
+ this.setState({ lookupType: newType });
1355
+ };
1356
+ this.updateSelectedTag = (tag) => {
1357
+ this.updateArtifact(this.props.command, tag);
1358
+ this.setState({ selectedTag: tag });
1359
+ this.props.command.triggerInvalid = false;
1360
+ };
1361
+ this.updateDigest = (digest) => {
1362
+ this.updateArtifact(this.props.command, digest);
1363
+ this.setState({ digest });
1364
+ };
1365
+ this.tagLoadSuccess = (tags) => {
1366
+ const { command } = this.props;
1367
+ const trigger = command.trigger;
1368
+ const newState = {};
1369
+ newState.tags = tags || [];
1370
+ const defaultSelection = newState.tags.find((t) => t === trigger.tag);
1371
+ if (defaultSelection) {
1372
+ newState.selectedTag = defaultSelection;
1373
+ this.updateSelectedTag(defaultSelection);
1374
+ }
1375
+ newState.tagsLoading = false;
1376
+ this.setState(newState);
1377
+ };
1378
+ this.tagLoadFailure = () => {
1379
+ this.setState({
1380
+ tagsLoading: false,
1381
+ loadError: true
1382
+ });
1383
+ };
1384
+ this.initialize = () => {
1385
+ const { command } = this.props;
1386
+ this.props.updateCommand("triggerInvalid", true);
1387
+ this.props.updateCommand("extraFields", {
1388
+ tag: get(command, "extraFields.tag", ""),
1389
+ artifacts: get(command, "extraFields.artifacts", "")
1390
+ });
1391
+ if (this.subscription) {
1392
+ this.subscription.unsubscribe();
1393
+ }
1394
+ if (command.trigger.type !== "docker" && command.trigger.type !== "helm/oci") {
1395
+ return;
1396
+ }
1397
+ this.subscription = this.queryStream.pipe(debounceTime(250), switchMap(this.handleQuery)).subscribe(this.tagLoadSuccess, this.tagLoadFailure);
1398
+ this.searchTags();
1399
+ };
1400
+ this.searchTags = (query = "") => {
1401
+ this.setState({ tags: [`<span>Finding tags${query && ` matching ${query}`}...</span>`] });
1402
+ this.queryStream.next();
1403
+ };
1404
+ this.state = {
1405
+ digest: "",
1406
+ tags: [],
1407
+ tagsLoading: true,
1408
+ loadError: false,
1409
+ lookupType: "tag",
1410
+ selectedTag: ""
1411
+ };
1412
+ }
1413
+ static formatLabel(trigger) {
1414
+ return $q.when(`(Docker Registry) ${trigger.account ? trigger.account + ":" : ""} ${trigger.repository || ""}`);
1415
+ }
1416
+ updateArtifact(command, tagOrDigest) {
1417
+ this.props.updateCommand("extraFields.tag", tagOrDigest);
1418
+ const trigger = command.trigger;
1419
+ if (trigger && trigger.repository) {
1420
+ let imageName = "";
1421
+ if (trigger.registry) {
1422
+ imageName += trigger.registry + "/";
1423
+ }
1424
+ imageName += trigger.repository;
1425
+ let imageReference = "";
1426
+ if (this.state.lookupType === "digest") {
1427
+ imageReference = `${imageName}@${tagOrDigest}`;
1428
+ } else {
1429
+ imageReference = `${imageName}:${tagOrDigest}`;
1430
+ }
1431
+ const artifactType = trigger.type === "docker" ? "docker/image" : "helm/image";
1432
+ this.props.updateCommand("extraFields.artifacts", [
1433
+ {
1434
+ type: artifactType,
1435
+ name: imageName,
1436
+ version: tagOrDigest,
1437
+ reference: imageReference
1438
+ }
1439
+ ]);
1440
+ }
1441
+ }
1442
+ componentDidMount() {
1443
+ this.initialize();
1444
+ }
1445
+ componentWillUnmount() {
1446
+ if (this.subscription) {
1447
+ this.subscription.unsubscribe();
1448
+ }
1449
+ }
1450
+ render() {
1451
+ const { digest, tags, tagsLoading, loadError, selectedTag, lookupType } = this.state;
1452
+ const options = tags.map((tag) => {
1453
+ return { value: tag };
1454
+ });
1455
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
1456
+ className: "form-group"
1457
+ }, /* @__PURE__ */ React.createElement("div", {
1458
+ className: "sm-label-right col-md-4"
1459
+ }, "Type"), /* @__PURE__ */ React.createElement("div", {
1460
+ className: "col-md-3"
1461
+ }, /* @__PURE__ */ React.createElement(TetheredSelect, {
895
1462
  clearable: false,
896
1463
  value: lookupType,
897
1464
  options: lookupTypeOptions,
@@ -968,9 +1535,36 @@ Registry.pipeline.registerTrigger({
968
1535
  }
969
1536
  ]
970
1537
  });
1538
+ Registry.pipeline.registerTrigger({
1539
+ label: "Helm Docker Registry",
1540
+ description: "Executes the pipeline on an helm/image update",
1541
+ key: "helm/oci",
1542
+ component: DockerHelmOciTriggerConfig,
1543
+ manualExecutionComponent: DockerTriggerTemplate,
1544
+ executionStatusComponent: DockerTriggerExecutionStatus,
1545
+ executionTriggerLabel: () => "Helm Docker Registry",
1546
+ validators: [
1547
+ {
1548
+ type: "requiredField",
1549
+ fieldName: "account",
1550
+ message: "<strong>Registry</strong> is a required field for Docker Registry triggers."
1551
+ },
1552
+ {
1553
+ type: "requiredField",
1554
+ fieldName: "repository",
1555
+ message: "<strong>Image</strong> is a required field for Docker Registry triggers."
1556
+ },
1557
+ {
1558
+ type: "serviceAccountAccess",
1559
+ preventSave: true,
1560
+ message: `You do not have access to the service account configured in this pipeline's Docker Registry trigger.
1561
+ You will not be able to save your edits to this pipeline.`
1562
+ }
1563
+ ]
1564
+ });
971
1565
 
972
1566
  const DOCKER_MODULE = "spinnaker.docker";
973
1567
  module(DOCKER_MODULE, [DOCKER_PIPELINE_STAGES_BAKE_DOCKERBAKESTAGE]);
974
1568
 
975
- export { DOCKER_MODULE, DockerImageAndTagSelector, DockerImageReader, DockerImageUtils };
1569
+ export { DOCKER_MODULE, DockerChartImageReader, DockerImageAndTagSelector, DockerImageReader, DockerImageUtils };
976
1570
  //# sourceMappingURL=index.js.map