diff --git a/.gitignore b/.gitignore
index 421b03c5822..a4d1d77c9c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ test/functional/cypress/screenshots/**/*
# vite local files
**/.env.local
+settings-my.js
diff --git a/Dockerfile.slim b/Dockerfile.slim
index 939870813f6..8504ad2ce0f 100644
--- a/Dockerfile.slim
+++ b/Dockerfile.slim
@@ -2,11 +2,11 @@ FROM debian:stable-slim
LABEL maintainer="sig-platform@spinnaker.io"
WORKDIR /opt/deck
-COPY docker /opt/deck/docker
+RUN chown www-data:www-data .
+COPY --chown=www-data:www-data docker /opt/deck/docker
RUN docker/setup-apache2.sh
-COPY build/webpack /opt/deck/html
-RUN chown -R www-data:www-data /opt/deck
+COPY --chown=www-data:www-data build/webpack /opt/deck/html
USER www-data
CMD docker/run-apache2.sh
diff --git a/packages/core/src/artifact/artifactTab.less b/packages/core/src/artifact/artifactTab.less
index 112f67646f9..6278a2532ce 100644
--- a/packages/core/src/artifact/artifactTab.less
+++ b/packages/core/src/artifact/artifactTab.less
@@ -14,6 +14,12 @@
}
}
+.artifact-list-row {
+ display: flex;
+ flex-direction: row;
+ align-items: baseline;
+}
+
.artifact-list-item {
height: 28px;
position: relative;
diff --git a/packages/core/src/artifact/react/ArtifactIconList.tsx b/packages/core/src/artifact/react/ArtifactIconList.tsx
index 0bbc3e6e71f..76f18981a9f 100644
--- a/packages/core/src/artifact/react/ArtifactIconList.tsx
+++ b/packages/core/src/artifact/react/ArtifactIconList.tsx
@@ -4,33 +4,45 @@ import React from 'react';
import { ArtifactIconService } from '../ArtifactIconService';
import { ArtifactTypePatterns } from '../ArtifactTypes';
import type { IArtifact } from '../../domain';
+import { CopyToClipboard } from '../../utils';
export interface IArtifactIconListProps {
artifacts: IArtifact[];
}
+function artifactDelimiter(artifact: IArtifact): string {
+ switch (artifact.type) {
+ case 'docker/image':
+ return ':';
+ default:
+ return ' - ';
+ }
+}
export const ArtifactIconList = (props: IArtifactIconListProps): any => {
return props.artifacts.map((artifact, i) => {
const { location, reference, type } = artifact;
let { name } = artifact;
const iconPath = ArtifactIconService.getPath(type);
const key = `${location || ''}${type || ''}${reference || ''}` || String(i);
- let title = type;
+ let artifactName = `${name || reference}${artifact.version ? artifactDelimiter(artifact) + artifact.version : ''}`;
+ let title = artifactName;
+ const copyToClipboardText = artifactDelimiter(artifact) === ':' ? artifactName : '';
if (isString(type) && type.length > 0) {
const k8sKindMatch = type.match(ArtifactTypePatterns.KUBERNETES);
if (k8sKindMatch != null && k8sKindMatch[1].length > 0) {
const kind = k8sKindMatch[1].substr(0, 1).toUpperCase() + k8sKindMatch[1].substr(1);
name = `${kind} ${name}`;
title = name;
+ artifactName = `${name || reference}${artifact.version ? artifactDelimiter(artifact) + artifact.version : ''}`;
}
}
return (
-
- {iconPath &&
}
-
- {name}
- {artifact.version && - {artifact.version}}
-
+
+
+ {iconPath &&
}
+
{artifactName}
+
+ {copyToClipboardText &&
}
);
});
diff --git a/packages/core/src/cluster/rollups.less b/packages/core/src/cluster/rollups.less
index ac65549fe0f..dc6fdcf5d7a 100644
--- a/packages/core/src/cluster/rollups.less
+++ b/packages/core/src/cluster/rollups.less
@@ -563,6 +563,10 @@ running-tasks-tag {
margin-left: 3px;
}
+ .padded-images-span {
+ padding-left: 35px;
+ }
+
.server-group-title,
.subgroup-title {
padding: 5px 10px 2px;
diff --git a/packages/core/src/pipeline/config/actions/delete/DeletePipelineModal.tsx b/packages/core/src/pipeline/config/actions/delete/DeletePipelineModal.tsx
index 24de7f7f9cc..194e2a606b1 100644
--- a/packages/core/src/pipeline/config/actions/delete/DeletePipelineModal.tsx
+++ b/packages/core/src/pipeline/config/actions/delete/DeletePipelineModal.tsx
@@ -1,4 +1,4 @@
-import { get, isEmpty, set } from 'lodash';
+import { get } from 'lodash';
import { $log } from 'ngimport';
import React from 'react';
import { Modal } from 'react-bootstrap';
@@ -27,22 +27,13 @@ export function DeletePipelineModal(props: IDeletePipelineModalProps) {
PipelineConfigService.deletePipeline(application.name, pipeline, pipeline.name).then(
() => {
- const idsToUpdatedIndices = {};
+ // const idsToUpdatedIndices = {};
const isPipelineStrategy = pipeline.strategy === true;
const data = isPipelineStrategy ? application.strategyConfigs.data : application.pipelineConfigs.data;
data.splice(
data.findIndex((p: any) => p.id === pipeline.id),
1,
);
- data.forEach((p: IPipeline, index: number) => {
- if (p.index !== index) {
- p.index = index;
- set(idsToUpdatedIndices, p.id, index);
- }
- });
- if (!isEmpty(idsToUpdatedIndices)) {
- PipelineConfigService.reorderPipelines(application.name, idsToUpdatedIndices, isPipelineStrategy);
- }
ReactInjector.$state.go('^.executions', null, { location: 'replace' });
closeModal();
},
diff --git a/packages/core/src/pipeline/config/services/PipelineConfigService.ts b/packages/core/src/pipeline/config/services/PipelineConfigService.ts
index 0d32a9943ae..7bbfb814951 100644
--- a/packages/core/src/pipeline/config/services/PipelineConfigService.ts
+++ b/packages/core/src/pipeline/config/services/PipelineConfigService.ts
@@ -67,7 +67,9 @@ export class PipelineConfigService {
}
const endpoint = pipeline.strategy ? 'strategies' : 'pipelines';
- return REST(endpoint).query({ staleCheck: true }).post(pipeline);
+ // return REST(endpoint).query({ staleCheck: true }).post(pipeline);
+ // temp turn off stale check as it causes gate to throw 400 for unknown reason
+ return REST(endpoint).post(pipeline);
}
public static reorderPipelines(
@@ -170,22 +172,8 @@ export class PipelineConfigService {
return uniq(upstreamStages);
}
- private static sortPipelines(pipelines: IPipeline[]): PromiseLike
{
+ static sortPipelines(pipelines: IPipeline[]): PromiseLike {
const sorted = sortBy(pipelines, ['index', 'name']);
-
- // if there are pipelines with a bad index, fix that
- const toReindex: Array> = [];
- if (sorted && sorted.length) {
- sorted.forEach((pipeline, index) => {
- if (pipeline.index !== index) {
- pipeline.index = index;
- toReindex.push(this.savePipeline(pipeline));
- }
- });
- if (toReindex.length) {
- return $q.all(toReindex).then(() => sorted);
- }
- }
return $q.resolve(sorted);
}
}
diff --git a/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts b/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts
index 19c2f7f3dcc..7da6c597894 100644
--- a/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts
+++ b/packages/core/src/pipeline/config/validation/PipelineConfigValidator.ts
@@ -191,7 +191,7 @@ export class PipelineConfigValidator {
if (pipeline.strategy && !pipeline.stages.some((stage) => stage.type === 'deploy')) {
messages.push('To be able to create new server groups, a custom strategy should contain a Deploy stage.');
}
- if ((pipeline.expectedArtifacts || []).some((a) => !a.matchArtifact || (a.matchArtifact as any) === {})) {
+ if ((pipeline.expectedArtifacts || []).some((a) => !a.matchArtifact || (a.matchArtifact as any) == {})) {
messages.push('Every expected artifact must specify an artifact to match against.');
}
return messages;
diff --git a/packages/core/src/pipeline/filter/ExecutionFilters.tsx b/packages/core/src/pipeline/filter/ExecutionFilters.tsx
index 89e9e53a5b0..358e5ba8974 100644
--- a/packages/core/src/pipeline/filter/ExecutionFilters.tsx
+++ b/packages/core/src/pipeline/filter/ExecutionFilters.tsx
@@ -208,17 +208,67 @@ export class ExecutionFilters extends React.Component {
+ if (sortEnd.oldIndex === sortEnd.newIndex) {
+ return;
+ }
const pipelineNames = arrayMove(this.state.pipelineNames, sortEnd.oldIndex, sortEnd.newIndex);
- this.applyNewPipelineSortOrder(pipelineNames);
+ this.setState({ pipelineNames: pipelineNames });
+ this.sortAsVisuallySeen(pipelineNames);
};
private sortAlphabetically = () => {
const pipelineNames = this.state.pipelineNames.slice().sort();
- this.applyNewPipelineSortOrder(pipelineNames);
+ this.setState({ pipelineNames: pipelineNames });
+ this.sortAsVisuallySeen(pipelineNames);
};
+ private sortAsVisuallySeen(pipelineNames: string[]) {
+ const idsToUpdatedIndices: { [key: string]: number } = {};
+ let i = 0;
+ const indices: number[] = [];
+ this.props.application.pipelineConfigs.data.forEach((pipeline: IPipeline) => {
+ indices.push(pipeline.index);
+ });
+ for (const pName of pipelineNames) {
+ const pipeline = this.getPipelineByName(pName);
+ const toIndex = indices[i];
+ if (pipeline.index !== toIndex) {
+ idsToUpdatedIndices[pipeline.id] = toIndex;
+ this.setPipelineIndex(pipeline.id, toIndex);
+ }
+ i = i + 1;
+ }
+ if (!isEmpty(idsToUpdatedIndices)) {
+ this.updatePipelines(idsToUpdatedIndices).then(() => {
+ this.refreshPipelines();
+ });
+ }
+ }
+
+ // @ts-ignore
+ // noinspection JSUnusedLocalSymbols
private applyNewPipelineSortOrder = (pipelineNames: string[]): void => {
const { application } = this.props;
logger.log({ category: 'Pipelines', action: 'Reordered pipeline' });
@@ -232,7 +282,7 @@ export class ExecutionFilters extends React.Component {});
this.refreshPipelines();
}
};
diff --git a/packages/core/src/pipeline/status/Artifact.tsx b/packages/core/src/pipeline/status/Artifact.tsx
index 0051fe4c1c0..a337293b181 100644
--- a/packages/core/src/pipeline/status/Artifact.tsx
+++ b/packages/core/src/pipeline/status/Artifact.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { ArtifactIconService } from '../../artifact';
import type { IArtifact } from '../../domain';
+import { CopyToClipboard } from '../../utils';
import './artifact.less';
@@ -32,9 +33,20 @@ export class Artifact extends React.Component {
return tooltipEntries.join('\n');
}
+ private artifactDelimiter(artifact: IArtifact): string {
+ switch (artifact.type) {
+ case 'docker/image':
+ return ':';
+ default:
+ return ' - ';
+ }
+ }
+
public render() {
const { artifact, isDefault } = this.props;
const { name, reference, version, type } = artifact;
+ const artifactName = `${name || reference}${this.artifactDelimiter(artifact)}${version || 'latest'}`;
+ const copyToClipboardText = this.artifactDelimiter(artifact) === ':' ? artifactName : '';
return (
@@ -42,14 +54,22 @@ export class Artifact extends React.Component
{
{ArtifactIconService.getPath(type) ? (
-
+
) : (
[{type}]
)}
- {name || reference}
- {version && - {version}
}
+
+ {artifactName}
+ {copyToClipboardText && }
+
diff --git a/packages/core/src/pipeline/status/artifact.less b/packages/core/src/pipeline/status/artifact.less
index a0d951c21b2..ee3546ef82b 100644
--- a/packages/core/src/pipeline/status/artifact.less
+++ b/packages/core/src/pipeline/status/artifact.less
@@ -12,6 +12,7 @@
.artifact-detail {
display: inline-flex;
padding: 0.1px;
+ align-items: baseline;
.artifact-icon {
padding-right: 4px;
diff --git a/packages/core/src/presentation/main.less b/packages/core/src/presentation/main.less
index 68a3c2ec1f3..0f0a458ee65 100644
--- a/packages/core/src/presentation/main.less
+++ b/packages/core/src/presentation/main.less
@@ -1464,6 +1464,8 @@ ul.checkmap {
.break-word {
overflow-wrap: break-word;
+ text-wrap: avoid;
+ white-space: nowrap;
}
.horizontal-rule {
diff --git a/packages/core/src/serverGroup/ServerGroupHeader.tsx b/packages/core/src/serverGroup/ServerGroupHeader.tsx
index 3322f3510b0..8dfafc971cd 100644
--- a/packages/core/src/serverGroup/ServerGroupHeader.tsx
+++ b/packages/core/src/serverGroup/ServerGroupHeader.tsx
@@ -13,6 +13,7 @@ import { LoadBalancersTagWrapper } from '../loadBalancer';
import { NameUtils } from '../naming';
import { Overridable } from '../overrideRegistry';
import { RunningTasksTag } from './pod/RunningTasksTag';
+import { CopyToClipboard } from '../utils';
export interface IServerGroupHeaderProps {
application: Application;
@@ -83,7 +84,10 @@ export class ImageList extends React.Component
{collapsed && (
<>
- {images[0]}
+
+ {images[0]}
+
+
{images.length > 1 && (