Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ci/pipeline_details')
-rw-r--r--app/assets/javascripts/ci/pipeline_details/graph/components/job_item.vue4
-rw-r--r--app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipeline.vue4
-rw-r--r--app/assets/javascripts/ci/pipeline_details/header/constants.js9
-rw-r--r--app/assets/javascripts/ci/pipeline_details/header/graphql/fragments/pipeline_header.fragment.graphql4
-rw-r--r--app/assets/javascripts/ci/pipeline_details/header/graphql/queries/get_pipeline_header_data.query.graphql18
-rw-r--r--app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue109
-rw-r--r--app/assets/javascripts/ci/pipeline_details/jobs/components/failed_jobs_table.vue2
-rw-r--r--app/assets/javascripts/ci/pipeline_details/pipeline_details_header.js44
-rw-r--r--app/assets/javascripts/ci/pipeline_details/pipelines_index.js2
-rw-r--r--app/assets/javascripts/ci/pipeline_details/stores/test_reports/utils.js23
-rw-r--r--app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue15
11 files changed, 139 insertions, 95 deletions
diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/job_item.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/job_item.vue
index c6340e6787a..afe66588fb9 100644
--- a/app/assets/javascripts/ci/pipeline_details/graph/components/job_item.vue
+++ b/app/assets/javascripts/ci/pipeline_details/graph/components/job_item.vue
@@ -5,7 +5,7 @@ import delayedJobMixin from '~/ci/mixins/delayed_job_mixin';
import { helpPagePath } from '~/helpers/help_page_helper';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { __, s__, sprintf } from '~/locale';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
import ActionComponent from '../../../common/private/job_action_component.vue';
import JobNameComponent from '../../../common/private/job_name_component.vue';
import { BRIDGE_KIND, RETRY_ACTION_TITLE, SINGLE_JOB, SKIP_RETRY_MODAL_KEY } from '../constants';
@@ -343,7 +343,7 @@ export default {
</div>
<gl-badge
v-if="isBridge"
- class="gl-mt-3"
+ class="gl-mt-3 gl-ml-7"
variant="info"
size="sm"
data-testid="job-bridge-badge"
diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipeline.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipeline.vue
index 26521f87426..76ff662cd3f 100644
--- a/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipeline.vue
+++ b/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipeline.vue
@@ -14,7 +14,7 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { __, sprintf } from '~/locale';
import CancelPipelineMutation from '~/ci/pipeline_details/graphql/mutations/cancel_pipeline.mutation.graphql';
import RetryPipelineMutation from '~/ci/pipeline_details/graphql/mutations/retry_pipeline.mutation.graphql';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
import { reportToSentry } from '~/ci/utils';
import { ACTION_FAILURE, DOWNSTREAM, UPSTREAM } from '../constants';
@@ -287,7 +287,7 @@ export default {
/>
<div v-else :class="$options.styles.actionSizeClasses"></div>
</div>
- <div class="gl-pt-2">
+ <div class="gl-pt-2 gl-ml-7">
<gl-badge size="sm" variant="info" data-testid="downstream-pipeline-label">
{{ label }}
</gl-badge>
diff --git a/app/assets/javascripts/ci/pipeline_details/header/constants.js b/app/assets/javascripts/ci/pipeline_details/header/constants.js
new file mode 100644
index 00000000000..a4aed7b8f46
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_details/header/constants.js
@@ -0,0 +1,9 @@
+export const DELETE_MODAL_ID = 'pipeline-delete-modal';
+
+export const POLL_INTERVAL = 10000;
+
+export const SCHEDULE_SOURCE = 'schedule';
+export const AUTO_DEVOPS_SOURCE = 'AUTO_DEVOPS_SOURCE';
+export const DETACHED_EVENT_TYPE = 'DETACHED';
+export const MERGED_RESULT_EVENT_TYPE = 'MERGED_RESULT';
+export const MERGE_TRAIN_EVENT_TYPE = 'MERGE_TRAIN';
diff --git a/app/assets/javascripts/ci/pipeline_details/header/graphql/fragments/pipeline_header.fragment.graphql b/app/assets/javascripts/ci/pipeline_details/header/graphql/fragments/pipeline_header.fragment.graphql
new file mode 100644
index 00000000000..80fc8b92a47
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_details/header/graphql/fragments/pipeline_header.fragment.graphql
@@ -0,0 +1,4 @@
+fragment PipelineHeaderData on Pipeline {
+ id
+ iid
+}
diff --git a/app/assets/javascripts/ci/pipeline_details/header/graphql/queries/get_pipeline_header_data.query.graphql b/app/assets/javascripts/ci/pipeline_details/header/graphql/queries/get_pipeline_header_data.query.graphql
index eb5643126a2..4ef79aaa03c 100644
--- a/app/assets/javascripts/ci/pipeline_details/header/graphql/queries/get_pipeline_header_data.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_details/header/graphql/queries/get_pipeline_header_data.query.graphql
@@ -1,15 +1,16 @@
+#import "ee_else_ce/ci/pipeline_details/header/graphql/fragments/pipeline_header.fragment.graphql"
+
query getPipelineHeaderData($fullPath: ID!, $iid: ID!) {
project(fullPath: $fullPath) {
id
pipeline(iid: $iid) {
- id
- iid
status
retryable
cancelable
userPermissions {
destroyPipeline
updatePipeline
+ cancelPipeline
}
detailedStatus {
id
@@ -41,6 +42,19 @@ query getPipelineHeaderData($fullPath: ID!, $iid: ID!) {
finishedAt
queuedDuration
duration
+ name
+ totalJobs
+ refText
+ triggeredByPath
+ stuck
+ child
+ complete
+ latest
+ mergeRequestEventType
+ configSource
+ failureReason
+ source
+ ...PipelineHeaderData
}
}
}
diff --git a/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue b/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue
index 651662d6395..1ecc4b2e1c1 100644
--- a/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue
+++ b/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue
@@ -17,7 +17,7 @@ import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility'; // eslint-
import { __, s__, sprintf, formatNumber } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { LOAD_FAILURE, POST_FAILURE, DELETE_FAILURE, DEFAULT } from '../constants';
@@ -26,9 +26,15 @@ import deletePipelineMutation from '../graphql/mutations/delete_pipeline.mutatio
import retryPipelineMutation from '../graphql/mutations/retry_pipeline.mutation.graphql';
import { getQueryHeaders } from '../graph/utils';
import getPipelineQuery from './graphql/queries/get_pipeline_header_data.query.graphql';
-
-const DELETE_MODAL_ID = 'pipeline-delete-modal';
-const POLL_INTERVAL = 10000;
+import {
+ DELETE_MODAL_ID,
+ POLL_INTERVAL,
+ DETACHED_EVENT_TYPE,
+ AUTO_DEVOPS_SOURCE,
+ SCHEDULE_SOURCE,
+ MERGE_TRAIN_EVENT_TYPE,
+ MERGED_RESULT_EVENT_TYPE,
+} from './constants';
export default {
name: 'PipelineDetailsHeader',
@@ -129,40 +135,14 @@ export default {
},
},
props: {
- name: {
- type: String,
- required: false,
- default: '',
- },
- totalJobs: {
- type: String,
- required: false,
- default: '',
- },
- computeMinutes: {
- type: String,
- required: false,
- default: '',
- },
yamlErrors: {
type: String,
required: false,
default: '',
},
- failureReason: {
- type: String,
- required: false,
- default: '',
- },
- refText: {
- type: String,
- required: false,
- default: '',
- },
- badges: {
- type: Object,
- required: false,
- default: () => {},
+ trigger: {
+ type: Boolean,
+ required: true,
},
},
apollo: {
@@ -270,7 +250,7 @@ export default {
},
totalJobsText() {
return sprintf(__('%{jobs} Jobs'), {
- jobs: this.totalJobs,
+ jobs: this.pipeline?.totalJobs || 0,
});
},
triggeredText() {
@@ -312,10 +292,61 @@ export default {
canCancelPipeline() {
const { cancelable, userPermissions } = this.pipeline;
- return cancelable && userPermissions.updatePipeline;
+ return cancelable && userPermissions.cancelPipeline;
+ },
+ computeMinutes() {
+ return this.pipeline?.computeMinutes;
},
showComputeMinutes() {
- return this.isFinished && this.computeMinutes !== '0.0';
+ return this.isFinished && this.computeMinutes;
+ },
+ pipelineName() {
+ return this.pipeline?.name;
+ },
+ refText() {
+ return this.pipeline?.refText;
+ },
+ triggeredByPath() {
+ return this.pipeline?.triggeredByPath;
+ },
+ mergeRequestEventType() {
+ return this.pipeline.mergeRequestEventType;
+ },
+ isMergeTrainPipeline() {
+ return this.mergeRequestEventType === MERGE_TRAIN_EVENT_TYPE;
+ },
+ isMergedResultsPipeline() {
+ return this.mergeRequestEventType === MERGED_RESULT_EVENT_TYPE;
+ },
+ isDetachedPipeline() {
+ return this.mergeRequestEventType === DETACHED_EVENT_TYPE;
+ },
+ isAutoDevopsPipeline() {
+ return this.pipeline.configSource === AUTO_DEVOPS_SOURCE;
+ },
+ isScheduledPipeline() {
+ return this.pipeline.source === SCHEDULE_SOURCE;
+ },
+ isInvalidPipeline() {
+ return Boolean(this.yamlErrors);
+ },
+ failureReason() {
+ return this.pipeline.failureReason;
+ },
+ badges() {
+ return {
+ schedule: this.isScheduledPipeline,
+ trigger: this.trigger,
+ invalid: this.isInvalidPipeline,
+ child: this.pipeline.child,
+ latest: this.pipeline.latest,
+ mergeTrainPipeline: this.isMergeTrainPipeline,
+ mergedResultsPipeline: this.isMergedResultsPipeline,
+ detached: this.isDetachedPipeline,
+ failed: Boolean(this.failureReason),
+ autoDevops: this.isAutoDevopsPipeline,
+ stuck: this.pipeline.stuck,
+ };
},
},
methods: {
@@ -406,7 +437,9 @@ export default {
<gl-loading-icon v-if="loading" class="gl-text-left" size="lg" />
<div v-else class="gl-display-flex gl-justify-content-space-between gl-flex-wrap">
<div>
- <h3 v-if="name" class="gl-mt-0 gl-mb-3" data-testid="pipeline-name">{{ name }}</h3>
+ <h3 v-if="pipelineName" class="gl-mt-0 gl-mb-3" data-testid="pipeline-name">
+ {{ pipelineName }}
+ </h3>
<h3 v-else class="gl-mt-0 gl-mb-3" data-testid="pipeline-commit-title">
{{ commitTitle }}
</h3>
@@ -483,7 +516,7 @@ export default {
>
<gl-sprintf :message="$options.i18n.childBadgeText">
<template #link="{ content }">
- <gl-link :href="paths.triggeredByPath" target="_blank">
+ <gl-link :href="triggeredByPath" target="_blank">
{{ content }}
</gl-link>
</template>
diff --git a/app/assets/javascripts/ci/pipeline_details/jobs/components/failed_jobs_table.vue b/app/assets/javascripts/ci/pipeline_details/jobs/components/failed_jobs_table.vue
index 287f6e045c6..1823908c231 100644
--- a/app/assets/javascripts/ci/pipeline_details/jobs/components/failed_jobs_table.vue
+++ b/app/assets/javascripts/ci/pipeline_details/jobs/components/failed_jobs_table.vue
@@ -5,7 +5,7 @@ import { __, s__ } from '~/locale';
import { createAlert } from '~/alert';
import Tracking from '~/tracking';
import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
import { TRACKING_CATEGORIES } from '~/ci/constants';
import RetryFailedJobMutation from '../graphql/mutations/retry_failed_job.mutation.graphql';
import { DEFAULT_FIELDS } from '../../constants';
diff --git a/app/assets/javascripts/ci/pipeline_details/pipeline_details_header.js b/app/assets/javascripts/ci/pipeline_details/pipeline_details_header.js
index 4966b657887..0430bc83dd7 100644
--- a/app/assets/javascripts/ci/pipeline_details/pipeline_details_header.js
+++ b/app/assets/javascripts/ci/pipeline_details/pipeline_details_header.js
@@ -12,29 +12,7 @@ export const createPipelineDetailsHeaderApp = (elSelector, apolloProvider, graph
return;
}
- const {
- fullPath,
- pipelineIid,
- pipelinesPath,
- name,
- totalJobs,
- computeMinutes,
- yamlErrors,
- failureReason,
- triggeredByPath,
- schedule,
- trigger,
- child,
- latest,
- mergeTrainPipeline,
- mergedResultsPipeline,
- invalid,
- failed,
- autoDevops,
- detached,
- stuck,
- refText,
- } = el.dataset;
+ const { fullPath, pipelineIid, pipelinesPath, yamlErrors, trigger } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
@@ -46,32 +24,14 @@ export const createPipelineDetailsHeaderApp = (elSelector, apolloProvider, graph
fullProject: fullPath,
graphqlResourceEtag,
pipelinesPath,
- triggeredByPath,
},
pipelineIid,
},
render(createElement) {
return createElement(PipelineDetailsHeader, {
props: {
- name,
- totalJobs,
- computeMinutes,
yamlErrors,
- failureReason,
- refText,
- badges: {
- schedule: parseBoolean(schedule),
- trigger: parseBoolean(trigger),
- child: parseBoolean(child),
- latest: parseBoolean(latest),
- mergeTrainPipeline: parseBoolean(mergeTrainPipeline),
- mergedResultsPipeline: parseBoolean(mergedResultsPipeline),
- invalid: parseBoolean(invalid),
- failed: parseBoolean(failed),
- autoDevops: parseBoolean(autoDevops),
- detached: parseBoolean(detached),
- stuck: parseBoolean(stuck),
- },
+ trigger: parseBoolean(trigger),
},
});
},
diff --git a/app/assets/javascripts/ci/pipeline_details/pipelines_index.js b/app/assets/javascripts/ci/pipeline_details/pipelines_index.js
index ea2875713a9..b4528ab895d 100644
--- a/app/assets/javascripts/ci/pipeline_details/pipelines_index.js
+++ b/app/assets/javascripts/ci/pipeline_details/pipelines_index.js
@@ -44,6 +44,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
params,
fullPath,
visibilityPipelineIdType,
+ showJenkinsCiPrompt,
} = el.dataset;
return new Vue({
@@ -57,6 +58,7 @@ export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
pipelineEditorPath,
pipelineSchedulesPath,
suggestedCiTemplates: JSON.parse(suggestedCiTemplates),
+ showJenkinsCiPrompt: parseBoolean(showJenkinsCiPrompt),
},
data() {
return {
diff --git a/app/assets/javascripts/ci/pipeline_details/stores/test_reports/utils.js b/app/assets/javascripts/ci/pipeline_details/stores/test_reports/utils.js
index 6b616601bc5..e3984685094 100644
--- a/app/assets/javascripts/ci/pipeline_details/stores/test_reports/utils.js
+++ b/app/assets/javascripts/ci/pipeline_details/stores/test_reports/utils.js
@@ -1,4 +1,5 @@
import { __, sprintf } from '~/locale';
+import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import { TestStatus } from '../../constants';
/**
@@ -25,15 +26,27 @@ export function iconForTestStatus(status) {
return 'status_notfound';
}
}
-
export const formattedTime = (seconds = 0) => {
if (seconds < 1) {
- const milliseconds = seconds * 1000;
- return sprintf(__('%{milliseconds}ms'), { milliseconds: milliseconds.toFixed(2) });
+ return sprintf(__('%{milliseconds}ms'), {
+ milliseconds: (seconds * 1000).toFixed(2),
+ });
+ }
+ if (seconds < 60) {
+ return sprintf(__('%{seconds}s'), {
+ seconds: (seconds % 60).toFixed(2),
+ });
}
- return sprintf(__('%{seconds}s'), { seconds: seconds.toFixed(2) });
-};
+ const hoursAndMinutes = stringifyTime(parseSeconds(seconds));
+ const remainingSeconds =
+ seconds % 60 >= 1
+ ? sprintf(__('%{seconds}s'), {
+ seconds: Math.floor(seconds % 60),
+ })
+ : '';
+ return `${hoursAndMinutes} ${remainingSeconds}`.trim();
+};
export const addIconStatus = (testCase) => ({
...testCase,
icon: iconForTestStatus(testCase.status),
diff --git a/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue b/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue
index a7737d33285..6e9a705c046 100644
--- a/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue
+++ b/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue
@@ -2,6 +2,7 @@
import { GlLoadingIcon } from '@gitlab/ui';
// eslint-disable-next-line no-restricted-imports
import { mapActions, mapGetters, mapState } from 'vuex';
+import { getParameterValues } from '~/lib/utils/url_utility';
import EmptyState from './empty_state.vue';
import TestSuiteTable from './test_suite_table.vue';
import TestSummary from './test_summary.vue';
@@ -19,7 +20,7 @@ export default {
inject: ['blobPath', 'summaryEndpoint', 'suiteEndpoint'],
computed: {
...mapState('testReports', ['isLoading', 'selectedSuiteIndex', 'testReports']),
- ...mapGetters('testReports', ['getSelectedSuite']),
+ ...mapGetters('testReports', ['getSelectedSuite', 'getTestSuites']),
showSuite() {
return this.selectedSuiteIndex !== null;
},
@@ -28,8 +29,16 @@ export default {
return testSuites.length > 0;
},
},
- created() {
- this.fetchSummary();
+ async created() {
+ await this.fetchSummary();
+ const jobName = getParameterValues('job_name')[0] || '';
+ if (jobName.length > 0) {
+ // get the index from the job name
+ const indexToSelect = this.getTestSuites.findIndex((test) => test.name === jobName);
+
+ this.setSelectedSuiteIndex(indexToSelect);
+ this.fetchTestSuite(indexToSelect);
+ }
},
methods: {
...mapActions('testReports', [