summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/pipelines
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/pipelines')
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue40
-rw-r--r--app/assets/javascripts/pipelines/components/graph_shared/drawing_utils.js6
-rw-r--r--app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/parsing_utils.js17
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue28
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue51
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue (renamed from app/assets/javascripts/pipelines/components/pipeline_graph/stage_pill.vue)17
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue64
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue23
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_ci_templates.vue46
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue2
-rw-r--r--app/assets/javascripts/pipelines/constants.js3
-rw-r--r--app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js14
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js10
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_mediator.js6
-rw-r--r--app/assets/javascripts/pipelines/pipeline_shared_client.js1
-rw-r--r--app/assets/javascripts/pipelines/pipelines_index.js4
27 files changed, 162 insertions, 210 deletions
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 71ec81b8969..ea45b5e3ec7 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -101,9 +101,6 @@ export default {
showJobLinks() {
return !this.isStageView && this.showLinks;
},
- shouldShowStageName() {
- return !this.isStageView;
- },
// The show downstream check prevents showing redundant linked columns
showDownstreamPipelines() {
return (
@@ -165,8 +162,10 @@ export default {
<div class="js-pipeline-graph">
<div
ref="mainPipelineContainer"
- class="gl-display-flex gl-position-relative gl-bg-gray-10 gl-white-space-nowrap gl-border-t-solid gl-border-t-1 gl-border-gray-100"
- :class="{ 'gl-pipeline-min-h gl-py-5 gl-overflow-auto': !isLinkedPipeline }"
+ class="gl-display-flex gl-position-relative gl-bg-gray-10 gl-white-space-nowrap"
+ :class="{
+ 'gl-pipeline-min-h gl-py-5 gl-overflow-auto gl-border-t-solid gl-border-t-1 gl-border-gray-100': !isLinkedPipeline,
+ }"
>
<linked-graph-wrapper>
<template #upstream>
@@ -202,11 +201,12 @@ export default {
:groups="column.groups"
:action="column.status.action"
:highlighted-jobs="highlightedJobs"
- :show-stage-name="shouldShowStageName"
+ :is-stage-view="isStageView"
:job-hovered="hoveredJobName"
:source-job-hovered="hoveredSourceJobName"
:pipeline-expanded="pipelineExpanded"
:pipeline-id="pipeline.id"
+ :user-permissions="pipeline.userPermissions"
@refreshPipelineGraph="$emit('refreshPipelineGraph')"
@jobHover="setJob"
@updateMeasurements="getMeasurements"
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index fb45738f8d1..a948a57c144 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -105,7 +105,7 @@ export default {
return this.pipeline;
}
- return unwrapPipelineData(this.pipelineProjectPath, data);
+ return unwrapPipelineData(this.pipelineProjectPath, JSON.parse(JSON.stringify(data)));
},
error(err) {
this.reportFailure({ type: LOAD_FAILURE, skipSentry: true });
@@ -114,7 +114,7 @@ export default {
this.$options.name,
`| type: ${LOAD_FAILURE} , info: ${serializeLoadErrors(err)}`,
{
- projectPath: this.projectPath,
+ projectPath: this.pipelineProjectPath,
pipelineIid: this.pipelineIid,
pipelineStages: this.pipeline?.stages?.length || 0,
nbOfDownstreams: this.pipeline?.downstream?.length || 0,
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue
index b3c5af5418f..dd8a354511a 100644
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue
@@ -161,7 +161,7 @@ export default {
:size="24"
css-classes="gl-top-0 gl-pr-2"
/>
- <div v-else class="gl-pr-2"><gl-loading-icon inline /></div>
+ <div v-else class="gl-pr-2"><gl-loading-icon size="sm" inline /></div>
<div class="gl-display-flex gl-flex-direction-column gl-w-13">
<span class="gl-text-truncate" data-testid="downstream-title">
{{ downstreamTitle }}
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
index 45113ecff41..52ee40bd982 100644
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
@@ -118,7 +118,7 @@ export default {
return this.currentPipeline;
}
- return unwrapPipelineData(projectPath, data);
+ return unwrapPipelineData(projectPath, JSON.parse(JSON.stringify(data)));
},
result() {
this.loadingPipelineId = null;
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
index 81d59f1ef65..d34ae8036ed 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -40,6 +40,11 @@ export default {
required: false,
default: () => [],
},
+ isStageView: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
jobHovered: {
type: String,
required: false,
@@ -50,16 +55,15 @@ export default {
required: false,
default: () => ({}),
},
- showStageName: {
- type: Boolean,
- required: false,
- default: false,
- },
sourceJobHovered: {
type: String,
required: false,
default: '',
},
+ userPermissions: {
+ type: Object,
+ required: true,
+ },
},
titleClasses: [
'gl-font-weight-bold',
@@ -69,20 +73,11 @@ export default {
'gl-pl-3',
],
computed: {
- /*
- currentGroups and filteredGroups are part of
- a test to hunt down a bug
- (see: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57142).
-
- They should be removed when the bug is rectified.
- */
- currentGroups() {
- return this.glFeatures.pipelineFilterJobs ? this.filteredGroups : this.groups;
+ canUpdatePipeline() {
+ return this.userPermissions.updatePipeline;
},
- filteredGroups() {
- return this.groups.map((group) => {
- return { ...group, jobs: group.jobs.filter(Boolean) };
- });
+ columnSpacingClass() {
+ return this.isStageView ? 'gl-px-6' : 'gl-px-9';
},
formattedTitle() {
return capitalize(escape(this.name));
@@ -90,6 +85,9 @@ export default {
hasAction() {
return !isEmpty(this.action);
},
+ showStageName() {
+ return !this.isStageView;
+ },
},
errorCaptured(err, _vm, info) {
reportToSentry('stage_column_component', `error: ${err}, info: ${info}`);
@@ -123,7 +121,7 @@ export default {
};
</script>
<template>
- <main-graph-wrapper class="gl-px-6" data-testid="stage-column">
+ <main-graph-wrapper :class="columnSpacingClass" data-testid="stage-column">
<template #stages>
<div
data-testid="stage-column-title"
@@ -132,7 +130,7 @@ export default {
>
<div>{{ formattedTitle }}</div>
<action-component
- v-if="hasAction"
+ v-if="hasAction && canUpdatePipeline"
:action-icon="action.icon"
:tooltip-text="action.title"
:link="action.path"
@@ -143,7 +141,7 @@ export default {
</template>
<template #jobs>
<div
- v-for="group in currentGroups"
+ v-for="group in groups"
:id="groupId(group)"
:key="getGroupId(group)"
data-testid="stage-column-group"
diff --git a/app/assets/javascripts/pipelines/components/graph_shared/drawing_utils.js b/app/assets/javascripts/pipelines/components/graph_shared/drawing_utils.js
index 7c62acbe8de..83f2466f0bf 100644
--- a/app/assets/javascripts/pipelines/components/graph_shared/drawing_utils.js
+++ b/app/assets/javascripts/pipelines/components/graph_shared/drawing_utils.js
@@ -75,11 +75,11 @@ export const generateLinksData = ({ links }, containerID, modifier = '') => {
// until we can safely draw the bezier to look nice.
// The adjustment number here is a magic number to make things
// look nice and should change if the padding changes. This goes well
- // with gl-px-6. gl-px-8 is more like 100.
- const straightLineDestinationX = targetNodeX - 60;
+ // with gl-px-9 which we translate with 100px here.
+ const straightLineDestinationX = targetNodeX - 100;
const controlPointX = straightLineDestinationX + (targetNodeX - straightLineDestinationX) / 2;
- if (straightLineDestinationX > 0) {
+ if (straightLineDestinationX > firstPointCoordinateX) {
path.lineTo(straightLineDestinationX, sourceNodeY);
}
diff --git a/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue b/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue
index d19215e7895..efad43ddd4f 100644
--- a/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue
@@ -99,7 +99,7 @@ export default {
class="js-ci-action gl-ci-action-icon-container ci-action-icon-container ci-action-icon-wrapper gl-display-flex gl-align-items-center gl-justify-content-center"
@click.stop="onClickAction"
>
- <gl-loading-icon v-if="isLoading" class="js-action-icon-loading" />
+ <gl-loading-icon v-if="isLoading" size="sm" class="js-action-icon-loading" />
<gl-icon v-else :name="actionIcon" class="gl-mr-0!" :aria-label="actionIcon" />
</gl-button>
</template>
diff --git a/app/assets/javascripts/pipelines/components/parsing_utils.js b/app/assets/javascripts/pipelines/components/parsing_utils.js
index f1d9ced807b..b36c9c0d049 100644
--- a/app/assets/javascripts/pipelines/components/parsing_utils.js
+++ b/app/assets/javascripts/pipelines/components/parsing_utils.js
@@ -1,4 +1,4 @@
-import { isEqual, memoize, uniqWith } from 'lodash';
+import { memoize } from 'lodash';
import { createSankey } from './dag/drawing_utils';
/*
@@ -113,11 +113,24 @@ export const filterByAncestors = (links, nodeDict) =>
return !allAncestors.includes(source);
});
+/*
+ A peformant alternative to lodash's isEqual. Because findIndex always finds
+ the first instance of a match, if the found index is not the first, we know
+ it is in fact a duplicate.
+*/
+const deduplicate = (item, itemIndex, arr) => {
+ const foundIdx = arr.findIndex((test) => {
+ return test.source === item.source && test.target === item.target;
+ });
+
+ return foundIdx === itemIndex;
+};
+
export const parseData = (nodes) => {
const nodeDict = createNodeDict(nodes);
const allLinks = makeLinksFromNodes(nodes, nodeDict);
const filteredLinks = filterByAncestors(allLinks, nodeDict);
- const links = uniqWith(filteredLinks, isEqual);
+ const links = filteredLinks.filter(deduplicate);
return { nodes, links };
};
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
index 01baf0a42d5..836333c8bde 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
@@ -14,7 +14,7 @@ export default {
type: Number,
required: true,
},
- isHighlighted: {
+ isHovered: {
type: Boolean,
required: false,
default: false,
@@ -42,7 +42,7 @@ export default {
jobPillClasses() {
return [
{ 'gl-opacity-3': this.isFadedOut },
- this.isHighlighted ? 'gl-shadow-blue-200-x0-y0-b4-s2' : 'gl-inset-border-2-green-400',
+ { 'gl-bg-gray-50 gl-inset-border-1-gray-200': this.isHovered },
];
},
},
@@ -57,15 +57,17 @@ export default {
};
</script>
<template>
- <tooltip-on-truncate :title="jobName" truncate-target="child" placement="top">
- <div
- :id="id"
- class="gl-w-15 gl-bg-white gl-text-center gl-text-truncate gl-rounded-pill gl-mb-3 gl-px-5 gl-py-2 gl-relative gl-z-index-1 gl-transition-duration-slow gl-transition-timing-function-ease"
- :class="jobPillClasses"
- @mouseover="onMouseEnter"
- @mouseleave="onMouseLeave"
- >
- {{ jobName }}
- </div>
- </tooltip-on-truncate>
+ <div class="gl-w-full">
+ <tooltip-on-truncate :title="jobName" truncate-target="child" placement="top">
+ <div
+ :id="id"
+ class="gl-bg-white gl-inset-border-1-gray-100 gl-text-center gl-text-truncate gl-rounded-6 gl-mb-3 gl-px-5 gl-py-3 gl-relative gl-z-index-1 gl-transition-duration-slow gl-transition-timing-function-ease"
+ :class="jobPillClasses"
+ @mouseover="onMouseEnter"
+ @mouseleave="onMouseLeave"
+ >
+ {{ jobName }}
+ </div>
+ </tooltip-on-truncate>
+ </div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue
index 3ba0d7d0120..78771b6a072 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue
@@ -4,14 +4,14 @@ import { __ } from '~/locale';
import { DRAW_FAILURE, DEFAULT } from '../../constants';
import LinksLayer from '../graph_shared/links_layer.vue';
import JobPill from './job_pill.vue';
-import StagePill from './stage_pill.vue';
+import StageName from './stage_name.vue';
export default {
components: {
GlAlert,
JobPill,
LinksLayer,
- StagePill,
+ StageName,
},
CONTAINER_REF: 'PIPELINE_GRAPH_CONTAINER_REF',
BASE_CONTAINER_ID: 'pipeline-graph-container',
@@ -21,6 +21,11 @@ export default {
[DRAW_FAILURE]: __('Could not draw the lines for job relationships'),
[DEFAULT]: __('An unknown error occurred.'),
},
+ // The combination of gl-w-full gl-min-w-full and gl-max-w-15 is necessary.
+ // The max width and the width make sure the ellipsis to work and the min width
+ // is for when there is less text than the stage column width (which the width 100% does not fix)
+ jobWrapperClasses:
+ 'gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full gl-px-8 gl-min-w-full gl-max-w-15',
props: {
pipelineData: {
required: true,
@@ -85,23 +90,8 @@ export default {
height: this.$refs[this.$options.CONTAINER_REF].scrollHeight,
};
},
- getStageBackgroundClasses(index) {
- const { length } = this.pipelineStages;
- // It's possible for a graph to have only one stage, in which
- // case we concatenate both the left and right rounding classes
- if (length === 1) {
- return 'gl-rounded-bottom-left-6 gl-rounded-top-left-6 gl-rounded-bottom-right-6 gl-rounded-top-right-6';
- }
-
- if (index === 0) {
- return 'gl-rounded-bottom-left-6 gl-rounded-top-left-6';
- }
-
- if (index === length - 1) {
- return 'gl-rounded-bottom-right-6 gl-rounded-top-right-6';
- }
-
- return '';
+ isFadedOut(jobName) {
+ return this.highlightedJobs.length > 1 && !this.isJobHighlighted(jobName);
},
isJobHighlighted(jobName) {
return this.highlightedJobs.includes(jobName);
@@ -137,7 +127,12 @@ export default {
>
{{ failure.text }}
</gl-alert>
- <div :id="containerId" :ref="$options.CONTAINER_REF" data-testid="graph-container">
+ <div
+ :id="containerId"
+ :ref="$options.CONTAINER_REF"
+ class="gl-bg-gray-10 gl-overflow-auto"
+ data-testid="graph-container"
+ >
<links-layer
:pipeline-data="pipelineStages"
:pipeline-id="$options.PIPELINE_ID"
@@ -152,23 +147,17 @@ export default {
:key="`${stage.name}-${index}`"
class="gl-flex-direction-column"
>
- <div
- class="gl-display-flex gl-align-items-center gl-bg-white gl-w-full gl-px-8 gl-py-4 gl-mb-5"
- :class="getStageBackgroundClasses(index)"
- data-testid="stage-background"
- >
- <stage-pill :stage-name="stage.name" :is-empty="stage.groups.length === 0" />
+ <div class="gl-display-flex gl-align-items-center gl-w-full gl-px-9 gl-py-4 gl-mb-5">
+ <stage-name :stage-name="stage.name" />
</div>
- <div
- class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full gl-px-8"
- >
+ <div :class="$options.jobWrapperClasses">
<job-pill
v-for="group in stage.groups"
:key="group.name"
:job-name="group.name"
:pipeline-id="$options.PIPELINE_ID"
- :is-highlighted="hasHighlightedJob && isJobHighlighted(group.name)"
- :is-faded-out="hasHighlightedJob && !isJobHighlighted(group.name)"
+ :is-hovered="highlightedJob === group.name"
+ :is-faded-out="isFadedOut(group.name)"
@on-mouse-enter="setHoveredJob"
@on-mouse-leave="removeHoveredJob"
/>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_pill.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
index df48426f24e..367a18af248 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_pill.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
@@ -1,4 +1,5 @@
<script>
+import { capitalize, escape } from 'lodash';
import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
export default {
@@ -10,26 +11,18 @@ export default {
type: String,
required: true,
},
- isEmpty: {
- type: Boolean,
- required: false,
- default: false,
- },
},
computed: {
- emptyClass() {
- return this.isEmpty ? 'gl-bg-gray-200' : 'gl-bg-gray-600';
+ formattedTitle() {
+ return capitalize(escape(this.stageName));
},
},
};
</script>
<template>
<tooltip-on-truncate :title="stageName" truncate-target="child" placement="top">
- <div
- class="gl-px-5 gl-py-2 gl-text-white gl-text-center gl-text-truncate gl-rounded-pill gl-w-20"
- :class="emptyClass"
- >
- {{ stageName }}
+ <div class="gl-py-2 gl-text-truncate gl-font-weight-bold gl-w-20">
+ {{ formattedTitle }}
</div>
</tooltip-on-truncate>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue
index 104a3caab4c..1ce6654e0e9 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue
@@ -16,7 +16,6 @@ export default {
consuming tasks, so you can spend more time creating.`),
aboutRunnersBtnText: s__('Pipelines|Learn about Runners'),
installRunnersBtnText: s__('Pipelines|Install GitLab Runners'),
- getStartedBtnText: s__('Pipelines|Get started with CI/CD'),
codeQualityTitle: s__('Pipelines|Improve code quality with GitLab CI/CD'),
codeQualityDescription: s__(`Pipelines|To keep your codebase simple,
readable, and accessible to contributors, use GitLab CI/CD
@@ -55,9 +54,6 @@ export default {
ciHelpPagePath() {
return helpPagePath('ci/quick_start/index.md');
},
- isPipelineEmptyStateTemplatesExperimentActive() {
- return this.canSetCi && Boolean(getExperimentData('pipeline_empty_state_templates'));
- },
isCodeQualityExperimentActive() {
return this.canSetCi && Boolean(getExperimentData('code_quality_walkthrough'));
},
@@ -81,37 +77,8 @@ export default {
</script>
<template>
<div>
- <gitlab-experiment
- v-if="isPipelineEmptyStateTemplatesExperimentActive"
- name="pipeline_empty_state_templates"
- >
- <template #control>
- <gl-empty-state
- :title="$options.i18n.title"
- :svg-path="emptyStateSvgPath"
- :description="$options.i18n.description"
- :primary-button-text="$options.i18n.getStartedBtnText"
- :primary-button-link="ciHelpPagePath"
- />
- </template>
- <template #candidate>
- <pipelines-ci-templates />
- </template>
- </gitlab-experiment>
- <gitlab-experiment v-else-if="isCodeQualityExperimentActive" name="code_quality_walkthrough">
- <template #control>
- <gl-empty-state
- :title="$options.i18n.title"
- :svg-path="emptyStateSvgPath"
- :description="$options.i18n.description"
- >
- <template #actions>
- <gl-button :href="ciHelpPagePath" variant="confirm" @click="trackClick()">
- {{ $options.i18n.getStartedBtnText }}
- </gl-button>
- </template>
- </gl-empty-state>
- </template>
+ <gitlab-experiment v-if="isCodeQualityExperimentActive" name="code_quality_walkthrough">
+ <template #control><pipelines-ci-templates /></template>
<template #candidate>
<gl-empty-state
:title="$options.i18n.codeQualityTitle"
@@ -127,23 +94,7 @@ export default {
</template>
</gitlab-experiment>
<gitlab-experiment v-else-if="isCiRunnerTemplatesExperimentActive" name="ci_runner_templates">
- <template #control>
- <gl-empty-state
- :title="$options.i18n.title"
- :svg-path="emptyStateSvgPath"
- :description="$options.i18n.description"
- >
- <template #actions>
- <gl-button
- :href="ciHelpPagePath"
- variant="confirm"
- @click="trackCiRunnerTemplatesClick('get_started_button_clicked')"
- >
- {{ $options.i18n.getStartedBtnText }}
- </gl-button>
- </template>
- </gl-empty-state>
- </template>
+ <template #control><pipelines-ci-templates /></template>
<template #candidate>
<gl-empty-state
:title="$options.i18n.title"
@@ -169,14 +120,7 @@ export default {
</gl-empty-state>
</template>
</gitlab-experiment>
- <gl-empty-state
- v-else-if="canSetCi"
- :title="$options.i18n.title"
- :svg-path="emptyStateSvgPath"
- :description="$options.i18n.description"
- :primary-button-text="$options.i18n.getStartedBtnText"
- :primary-button-link="ciHelpPagePath"
- />
+ <pipelines-ci-templates v-else-if="canSetCi" />
<gl-empty-state
v-else
title=""
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue
index d7bd2d731b1..5e18f636b52 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue
@@ -97,7 +97,7 @@ export default {
{{ $options.i18n.artifactsFetchErrorMessage }}
</gl-alert>
- <gl-loading-icon v-if="isLoading" />
+ <gl-loading-icon v-if="isLoading" size="sm" />
<gl-dropdown-item
v-for="(artifact, i) in artifacts"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
index bf992b84387..7552ddb61dc 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
@@ -13,7 +13,7 @@
*/
import { GlDropdown, GlLoadingIcon, GlTooltipDirective, GlIcon } from '@gitlab/ui';
-import { deprecatedCreateFlash as Flash } from '~/flash';
+import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import eventHub from '../../event_hub';
@@ -83,7 +83,9 @@ export default {
this.$refs.dropdown.hide();
this.isLoading = false;
- Flash(__('Something went wrong on our end.'));
+ createFlash({
+ message: __('Something went wrong on our end.'),
+ });
});
},
isDropdownOpen() {
@@ -118,7 +120,7 @@ export default {
<gl-icon :name="borderlessIcon" />
</span>
</template>
- <gl-loading-icon v-if="isLoading" />
+ <gl-loading-icon v-if="isLoading" size="sm" />
<ul
v-else
class="js-builds-dropdown-list scrollable-menu"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
index 52c8ef2cf26..fc8f31c5b7e 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
@@ -60,19 +60,20 @@ export default {
data-testid="pipeline-url-link"
data-qa-selector="pipeline_url_link"
>
- <span class="pipeline-id">#{{ pipeline.id }}</span>
+ #{{ pipeline.id }}
</gl-link>
<div class="label-container">
- <gl-link v-if="isScheduled" :href="pipelineScheduleUrl" target="__blank">
- <gl-badge
- v-gl-tooltip
- :title="__('This pipeline was triggered by a schedule.')"
- variant="info"
- size="sm"
- data-testid="pipeline-url-scheduled"
- >{{ __('Scheduled') }}</gl-badge
- >
- </gl-link>
+ <gl-badge
+ v-if="isScheduled"
+ v-gl-tooltip
+ :href="pipelineScheduleUrl"
+ target="__blank"
+ :title="__('This pipeline was triggered by a schedule.')"
+ variant="info"
+ size="sm"
+ data-testid="pipeline-url-scheduled"
+ >{{ __('Scheduled') }}</gl-badge
+ >
<gl-badge
v-if="pipeline.flags.latest"
v-gl-tooltip
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index 8bb2657c161..e3373178239 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -2,7 +2,7 @@
import { GlEmptyState, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { isEqual } from 'lodash';
import createFlash from '~/flash';
-import { getParameterByName } from '~/lib/utils/common_utils';
+import { getParameterByName } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue
index 147fff52101..36629d9f1f1 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_artifacts.vue
@@ -96,7 +96,7 @@ export default {
{{ $options.i18n.artifactsFetchErrorMessage }}
</gl-alert>
- <gl-loading-icon v-if="isLoading" />
+ <gl-loading-icon v-if="isLoading" size="sm" />
<gl-alert v-else-if="!hasArtifacts" variant="info" :dismissible="false">
{{ $options.i18n.noArtifacts }}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_ci_templates.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_ci_templates.vue
index c2ec8c57fd7..c6c81d5253b 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_ci_templates.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_ci_templates.vue
@@ -1,17 +1,19 @@
<script>
-import { GlButton, GlCard, GlSprintf } from '@gitlab/ui';
-import ExperimentTracking from '~/experimentation/experiment_tracking';
+import { GlAvatar, GlButton, GlCard, GlSprintf } from '@gitlab/ui';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { s__, sprintf } from '~/locale';
-import { HELLO_WORLD_TEMPLATE_KEY } from '../../constants';
+import { STARTER_TEMPLATE_NAME } from '~/pipeline_editor/constants';
+import Tracking from '~/tracking';
export default {
components: {
+ GlAvatar,
GlButton,
GlCard,
GlSprintf,
},
- HELLO_WORLD_TEMPLATE_KEY,
+ mixins: [Tracking.mixin()],
+ STARTER_TEMPLATE_NAME,
i18n: {
cta: s__('Pipelines|Use template'),
testTemplates: {
@@ -19,10 +21,10 @@ export default {
subtitle: s__(
'Pipelines|Use a sample %{codeStart}.gitlab-ci.yml%{codeEnd} template file to explore how CI/CD works.',
),
- helloWorld: {
- title: s__('Pipelines|“Hello world” with GitLab CI/CD'),
+ gettingStarted: {
+ title: s__('Pipelines|Get started with GitLab CI/CD'),
description: s__(
- 'Pipelines|Get familiar with GitLab CI/CD syntax by starting with a simple pipeline that runs a “Hello world” script.',
+ 'Pipelines|Get familiar with GitLab CI/CD syntax by starting with a basic 3 stage CI/CD pipeline.',
),
},
},
@@ -34,31 +36,30 @@ export default {
description: s__('Pipelines|CI/CD template to test and deploy your %{name} project.'),
},
},
- inject: ['addCiYmlPath', 'suggestedCiTemplates'],
+ inject: ['pipelineEditorPath', 'suggestedCiTemplates'],
data() {
const templates = this.suggestedCiTemplates.map(({ name, logo }) => {
return {
name,
logo,
- link: mergeUrlParams({ template: name }, this.addCiYmlPath),
+ link: mergeUrlParams({ template: name }, this.pipelineEditorPath),
description: sprintf(this.$options.i18n.templates.description, { name }),
};
});
return {
templates,
- helloWorldTemplateUrl: mergeUrlParams(
- { template: HELLO_WORLD_TEMPLATE_KEY },
- this.addCiYmlPath,
+ gettingStartedTemplateUrl: mergeUrlParams(
+ { template: STARTER_TEMPLATE_NAME },
+ this.pipelineEditorPath,
),
};
},
methods: {
trackEvent(template) {
- const tracking = new ExperimentTracking('pipeline_empty_state_templates', {
+ this.track('template_clicked', {
label: template,
});
- tracking.event('template_clicked');
},
},
};
@@ -81,18 +82,18 @@ export default {
<div class="gl-py-5"><gl-emoji class="gl-font-size-h2-xl" data-name="wave" /></div>
<div class="gl-mb-3">
<strong class="gl-text-gray-800 gl-mb-2">{{
- $options.i18n.testTemplates.helloWorld.title
+ $options.i18n.testTemplates.gettingStarted.title
}}</strong>
</div>
- <p class="gl-font-sm">{{ $options.i18n.testTemplates.helloWorld.description }}</p>
+ <p class="gl-font-sm">{{ $options.i18n.testTemplates.gettingStarted.description }}</p>
</div>
<gl-button
category="primary"
variant="confirm"
- :href="helloWorldTemplateUrl"
+ :href="gettingStartedTemplateUrl"
data-testid="test-template-link"
- @click="trackEvent($options.HELLO_WORLD_TEMPLATE_KEY)"
+ @click="trackEvent($options.STARTER_TEMPLATE_NAME)"
>
{{ $options.i18n.cta }}
</gl-button>
@@ -109,11 +110,12 @@ export default {
class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-pb-3 gl-pt-3"
>
<div class="gl-display-flex gl-flex-direction-row gl-align-items-center">
- <img
- width="64"
- height="64"
+ <gl-avatar
:src="template.logo"
- class="gl-mr-6"
+ :size="64"
+ class="gl-mr-6 gl-bg-white dark-mode-override"
+ shape="rect"
+ :alt="template.name"
data-testid="template-logo"
/>
<div class="gl-flex-direction-row">
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue
index 15ff7da35e1..5409e68cdc4 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue
@@ -60,7 +60,7 @@ export default {
@input="searchBranches"
>
<template #suggestions>
- <gl-loading-icon v-if="loading" />
+ <gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion
v-for="(branch, index) in branches"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue
index af62c492748..afcdd63b664 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue
@@ -55,7 +55,7 @@ export default {
<template>
<gl-filtered-search-token v-bind="{ ...$props, ...$attrs }" v-on="$listeners" @input="searchTags">
<template #suggestions>
- <gl-loading-icon v-if="loading" />
+ <gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion v-for="(tag, index) in tags" :key="index" :value="tag">
{{ tag }}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue
index bc661f37493..33115d72b9c 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue
@@ -98,7 +98,7 @@ export default {
}}</gl-filtered-search-suggestion>
<gl-dropdown-divider />
- <gl-loading-icon v-if="loading" />
+ <gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion
v-for="user in users"
diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js
index 01705e7726f..21b114825a6 100644
--- a/app/assets/javascripts/pipelines/constants.js
+++ b/app/assets/javascripts/pipelines/constants.js
@@ -35,6 +35,3 @@ export const POST_FAILURE = 'post_failure';
export const UNSUPPORTED_DATA = 'unsupported_data';
export const CHILD_VIEW = 'child';
-
-// The key of the template is the same as the filename
-export const HELLO_WORLD_TEMPLATE_KEY = 'Hello-World';
diff --git a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js
index 9f15b6c4ae3..5c34f4e4f7e 100644
--- a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js
+++ b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js
@@ -1,4 +1,4 @@
-import { deprecatedCreateFlash as flash } from '~/flash';
+import createFlash from '~/flash';
import { __ } from '~/locale';
export default {
@@ -13,7 +13,9 @@ export default {
})
.catch(() => {
this.mediator.store.toggleLoading(pipeline);
- flash(__('An error occurred while fetching the pipeline.'));
+ createFlash({
+ message: __('An error occurred while fetching the pipeline.'),
+ });
});
},
/**
@@ -53,9 +55,11 @@ export default {
requestRefreshPipelineGraph() {
// When an action is clicked
// (whether in the dropdown or in the main nodes, we refresh the big graph)
- this.mediator
- .refreshPipeline()
- .catch(() => flash(__('An error occurred while making the request.')));
+ this.mediator.refreshPipeline().catch(() =>
+ createFlash({
+ message: __('An error occurred while making the request.'),
+ }),
+ );
},
},
};
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index 9ab4753fec8..e8d5ed175ba 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { deprecatedCreateFlash as Flash } from '~/flash';
+import createFlash from '~/flash';
import { parseBoolean } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import Translate from '~/vue_shared/translate';
@@ -96,14 +96,18 @@ export default async function initPipelineDetailsBundle() {
try {
createPipelineHeaderApp(SELECTORS.PIPELINE_HEADER, apolloProvider, dataset.graphqlResourceEtag);
} catch {
- Flash(__('An error occurred while loading a section of this page.'));
+ createFlash({
+ message: __('An error occurred while loading a section of this page.'),
+ });
}
if (canShowNewPipelineDetails) {
try {
createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, apolloProvider, dataset);
} catch {
- Flash(__('An error occurred while loading the pipeline.'));
+ createFlash({
+ message: __('An error occurred while loading the pipeline.'),
+ });
}
} else {
const { default: PipelinesMediator } = await import(
diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediator.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
index 09637c25654..72c4fedc64c 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_mediator.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
@@ -1,5 +1,5 @@
import Visibility from 'visibilityjs';
-import { deprecatedCreateFlash as Flash } from '../flash';
+import createFlash from '~/flash';
import Poll from '../lib/utils/poll';
import { __ } from '../locale';
import PipelineService from './services/pipeline_service';
@@ -47,7 +47,9 @@ export default class pipelinesMediator {
errorCallback() {
this.state.isLoading = false;
- Flash(__('An error occurred while fetching the pipeline.'));
+ createFlash({
+ message: __('An error occurred while fetching the pipeline.'),
+ });
}
refreshPipeline() {
diff --git a/app/assets/javascripts/pipelines/pipeline_shared_client.js b/app/assets/javascripts/pipelines/pipeline_shared_client.js
index c3be487caae..7a922acd0b3 100644
--- a/app/assets/javascripts/pipelines/pipeline_shared_client.js
+++ b/app/assets/javascripts/pipelines/pipeline_shared_client.js
@@ -5,6 +5,7 @@ export const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
+ assumeImmutableResults: true,
useGet: true,
},
),
diff --git a/app/assets/javascripts/pipelines/pipelines_index.js b/app/assets/javascripts/pipelines/pipelines_index.js
index 925a96ea1aa..c4c2b5f2927 100644
--- a/app/assets/javascripts/pipelines/pipelines_index.js
+++ b/app/assets/javascripts/pipelines/pipelines_index.js
@@ -29,7 +29,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
errorStateSvgPath,
noPipelinesSvgPath,
newPipelinePath,
- addCiYmlPath,
+ pipelineEditorPath,
suggestedCiTemplates,
canCreatePipeline,
hasGitlabCi,
@@ -44,7 +44,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
return new Vue({
el,
provide: {
- addCiYmlPath,
+ pipelineEditorPath,
artifactsEndpoint,
artifactsEndpointPlaceholder,
suggestedCiTemplates: JSON.parse(suggestedCiTemplates),