diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
commit | 43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch) | |
tree | dceebdc68925362117480a5d672bcff122fb625b /app/assets/javascripts/pipelines/components | |
parent | 20c84b99005abd1c82101dfeff264ac50d2df211 (diff) |
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'app/assets/javascripts/pipelines/components')
26 files changed, 185 insertions, 274 deletions
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 8f76d7535f1..83cd64c17ed 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue @@ -255,7 +255,7 @@ export default { this.canRefetchHeaderPipeline = true; this.$apollo.queries.headerPipeline.refetch(); }, - /* eslint-disable @gitlab/require-i18n-strings */ + // eslint-disable-next-line @gitlab/require-i18n-strings reportFailure({ type, err = 'No error string passed.', skipSentry = false }) { this.showAlert = true; this.alertType = type; @@ -263,7 +263,6 @@ export default { reportToSentry(this.$options.name, `type: ${type}, info: ${err}`); } }, - /* eslint-enable @gitlab/require-i18n-strings */ updateShowLinksState(val) { this.showLinks = val; }, diff --git a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue index 6d8c35f4482..73143c981ed 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue @@ -62,6 +62,7 @@ export default { }, showTip() { return ( + this.showLinksToggle && this.showLinks && this.showLinksActive && !this.tipPreviouslyDismissed && diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue index 992e3d2f552..22895a31082 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_item.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue @@ -39,6 +39,9 @@ export default { confirmationModalDocLink: helpPagePath('/ci/pipelines/downstream_pipelines'), i18n: { bridgeBadgeText: __('Trigger job'), + bridgeRetryText: s__( + 'PipelineGraph|Downstream pipeline might not display in the graph while the new downstream pipeline is being created.', + ), unauthorizedTooltip: __('You are not authorized to run this manual job'), confirmationModal: { title: s__('PipelineGraph|Are you sure you want to retry %{jobName}?'), @@ -288,6 +291,10 @@ export default { }, pipelineActionRequestComplete() { this.$emit('pipelineActionRequestComplete'); + + if (this.isBridge) { + this.$toast.show(this.$options.i18n.bridgeRetryText); + } }, executePendingAction() { this.shouldTriggerActionClick = true; diff --git a/app/assets/javascripts/pipelines/components/graph/utils.js b/app/assets/javascripts/pipelines/components/graph/utils.js index 3da792cb9df..c888c8a5537 100644 --- a/app/assets/javascripts/pipelines/components/graph/utils.js +++ b/app/assets/javascripts/pipelines/components/graph/utils.js @@ -1,11 +1,12 @@ import { isEmpty } from 'lodash'; -import Visibility from 'visibilityjs'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { getIdFromGraphQLId, etagQueryHeaders } from '~/graphql_shared/utils'; import { reportToSentry } from '../../utils'; import { listByLayers } from '../parsing_utils'; import { unwrapStagesWithNeedsAndLookup } from '../unwrapping_utils'; import { beginPerfMeasure, finishPerfMeasureAndSend } from './perf_utils'; +export { toggleQueryPollingByVisibility } from '~/graphql_shared/utils'; + const addMulti = (mainPipelineProjectPath, linkedPipeline) => { return { ...linkedPipeline, @@ -35,23 +36,13 @@ const calculatePipelineLayersInfo = (pipeline, componentName, metricsPath) => { return layers; }; -/* eslint-disable @gitlab/require-i18n-strings */ -const getQueryHeaders = (etagResource) => { - return { - fetchOptions: { - method: 'GET', - }, - headers: { - 'X-GITLAB-GRAPHQL-FEATURE-CORRELATION': 'verify/ci/pipeline-graph', - 'X-GITLAB-GRAPHQL-RESOURCE-ETAG': etagResource, - 'X-Requested-With': 'XMLHttpRequest', - }, - }; -}; +const getQueryHeaders = (etagResource) => + etagQueryHeaders('verify/ci/pipeline-graph', etagResource); const serializeGqlErr = (gqlError) => { const { locations = [], message = '', path = [] } = gqlError; + // eslint-disable-next-line @gitlab/require-i18n-strings return ` ${message}. Locations: ${locations @@ -74,27 +65,12 @@ const serializeLoadErrors = (errors) => { } if (!isEmpty(networkError)) { - return `Network error: ${networkError.message}`; + return `Network error: ${networkError.message}`; // eslint-disable-line @gitlab/require-i18n-strings } return message; }; -/* eslint-enable @gitlab/require-i18n-strings */ - -const toggleQueryPollingByVisibility = (queryRef, interval = 10000) => { - const stopStartQuery = (query) => { - if (!Visibility.hidden()) { - query.startPolling(interval); - } else { - query.stopPolling(); - } - }; - - stopStartQuery(queryRef); - Visibility.change(stopStartQuery.bind(null, queryRef)); -}; - const transformId = (linkedPipeline) => { return { ...linkedPipeline, id: getIdFromGraphQLId(linkedPipeline.id) }; }; @@ -135,7 +111,6 @@ export { getQueryHeaders, serializeGqlErr, serializeLoadErrors, - toggleQueryPollingByVisibility, unwrapPipelineData, validateConfigPaths, }; diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index a36d5d9b58f..27119419060 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -7,7 +7,7 @@ import { GlModalDirective, GlTooltipDirective, } from '@gitlab/ui'; -import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility'; +import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated import { __ } from '~/locale'; import CiHeader from '~/vue_shared/components/header_ci_component.vue'; import { @@ -16,6 +16,7 @@ import { DELETE_FAILURE, DEFAULT, BUTTON_TOOLTIP_RETRY, + BUTTON_TOOLTIP_CANCEL, } from '../constants'; import cancelPipelineMutation from '../graphql/mutations/cancel_pipeline.mutation.graphql'; import deletePipelineMutation from '../graphql/mutations/delete_pipeline.mutation.graphql'; @@ -29,6 +30,7 @@ const POLL_INTERVAL = 10000; export default { name: 'PipelineHeaderSection', BUTTON_TOOLTIP_RETRY, + BUTTON_TOOLTIP_CANCEL, pipelineCancel: 'pipelineCancel', pipelineRetry: 'pipelineRetry', finishedStatuses: ['FAILED', 'SUCCESS', 'CANCELED'], @@ -231,7 +233,7 @@ export default { this.reportFailure(DELETE_FAILURE, errors); this.isDeleting = false; } else { - redirectTo(setUrlFragment(this.paths.pipelinesPath, 'delete_success')); + redirectTo(setUrlFragment(this.paths.pipelinesPath, 'delete_success')); // eslint-disable-line import/no-deprecated } } catch { this.$apollo.queries.pipeline.startPolling(POLL_INTERVAL); @@ -275,6 +277,9 @@ export default { <gl-button v-if="canCancelPipeline" + v-gl-tooltip + :aria-label="$options.BUTTON_TOOLTIP_CANCEL" + :title="$options.BUTTON_TOOLTIP_CANCEL" :loading="isCanceling" :disabled="isCanceling" class="gl-ml-3" @@ -282,7 +287,7 @@ export default { data-testid="cancelPipeline" @click="cancelPipeline()" > - {{ __('Cancel running') }} + {{ __('Cancel pipeline') }} </gl-button> <gl-button diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue index 605d40eddee..c24862f828b 100644 --- a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue +++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue @@ -1,10 +1,8 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; import { s__ } from '~/locale'; -import { createAlert } from '~/flash'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { createAlert } from '~/alert'; import GetFailedJobsQuery from '../../graphql/queries/get_failed_jobs.query.graphql'; -import { prepareFailedJobs } from './utils'; import FailedJobsTable from './failed_jobs_table.vue'; export default { @@ -13,38 +11,33 @@ export default { FailedJobsTable, }, inject: { - fullPath: { + projectPath: { default: '', }, pipelineIid: { default: '', }, }, - props: { - failedJobsSummary: { - type: Array, - required: true, - }, - }, apollo: { failedJobs: { query: GetFailedJobsQuery, variables() { return { - fullPath: this.fullPath, + fullPath: this.projectPath, pipelineIid: this.pipelineIid, }; }, update({ project }) { - if (project?.pipeline?.jobs?.nodes) { - return project.pipeline.jobs.nodes.map((job) => { - return { normalizedId: getIdFromGraphQLId(job.id), ...job }; - }); - } - return []; - }, - result() { - this.preparedFailedJobs = prepareFailedJobs(this.failedJobs, this.failedJobsSummary); + const jobNodes = project?.pipeline?.jobs?.nodes || []; + + return jobNodes.map((job) => { + return { + ...job, + // this field is needed for the slot row-details + // on the failed_jobs_table.vue component + _showDetails: true, + }; + }); }, error() { createAlert({ message: s__('Jobs|There was a problem fetching the failed jobs.') }); @@ -54,7 +47,6 @@ export default { data() { return { failedJobs: [], - preparedFailedJobs: [], }; }, computed: { @@ -68,6 +60,6 @@ export default { <template> <div> <gl-loading-icon v-if="loading" size="lg" class="gl-mt-4" /> - <failed-jobs-table v-else :failed-jobs="preparedFailedJobs" /> + <failed-jobs-table v-else :failed-jobs="failedJobs" /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue index 041b62e02ec..ec7000120f1 100644 --- a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue +++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue @@ -2,8 +2,8 @@ import { GlButton, GlLink, GlTableLite } from '@gitlab/ui'; import SafeHtml from '~/vue_shared/directives/safe_html'; import { __, s__ } from '~/locale'; -import { createAlert } from '~/flash'; -import { redirectTo } from '~/lib/utils/url_utility'; +import { createAlert } from '~/alert'; +import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; import RetryFailedJobMutation from '../../graphql/mutations/retry_failed_job.mutation.graphql'; import { DEFAULT_FIELDS } from '../../constants'; @@ -40,7 +40,7 @@ export default { if (errors.length > 0) { this.showErrorMessage(); } else { - redirectTo(job.detailedStatus.detailsPath); + redirectTo(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated } } catch { this.showErrorMessage(); @@ -52,6 +52,9 @@ export default { showErrorMessage() { createAlert({ message: s__('Job|There was a problem retrying the failed job.') }); }, + failureSummary(trace) { + return trace ? trace.htmlSummary : s__('Job|No job log'); + }, }, }; </script> @@ -90,8 +93,8 @@ export default { </div> </template> - <template #cell(failure)="{ item }"> - <span>{{ item.failure }}</span> + <template #cell(failureMessage)="{ item }"> + <span data-testid="job-failure-message">{{ item.failureMessage }}</span> </template> <template #cell(actions)="{ item }"> @@ -110,7 +113,7 @@ export default { class="gl-w-full gl-text-left gl-border-none" data-testid="job-log" > - <code v-safe-html="item.failureSummary" class="gl-reset-bg gl-p-0" > + <code v-safe-html="failureSummary(item.trace)" class="gl-reset-bg gl-p-0" data-testid="job-trace-summary"> </code> </pre> </template> diff --git a/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue b/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue index f1ad312dcaa..61748860983 100644 --- a/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue +++ b/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue @@ -1,7 +1,7 @@ <script> import { GlIntersectionObserver, GlLoadingIcon, GlSkeletonLoader } from '@gitlab/ui'; import produce from 'immer'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { __ } from '~/locale'; import eventHub from '~/jobs/components/table/event_hub'; import JobsTable from '~/jobs/components/table/jobs_table.vue'; @@ -17,7 +17,7 @@ export default { JobsTable, }, inject: { - fullPath: { + projectPath: { default: '', }, pipelineIid: { @@ -56,7 +56,7 @@ export default { computed: { queryVariables() { return { - fullPath: this.fullPath, + fullPath: this.projectPath, iid: this.pipelineIid, }; }, diff --git a/app/assets/javascripts/pipelines/components/jobs/utils.js b/app/assets/javascripts/pipelines/components/jobs/utils.js deleted file mode 100644 index c8414d44d14..00000000000 --- a/app/assets/javascripts/pipelines/components/jobs/utils.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - We get the failure and failure summary from Rails which has - a summary failure log. Here we combine that data with the data - from GraphQL to display the log. - - failedJobs is from GraphQL - failedJobsSummary is from Rails - */ - -export const prepareFailedJobs = (failedJobs = [], failedJobsSummary = []) => { - const combinedJobs = []; - - if (failedJobs.length > 0 && failedJobsSummary.length > 0) { - failedJobs.forEach((failedJob) => { - const foundJob = failedJobsSummary.find( - (failedJobSummary) => failedJob.normalizedId === failedJobSummary.id, - ); - - if (foundJob) { - combinedJobs.push({ - ...failedJob, - failure: foundJob?.failure, - failureSummary: foundJob?.failure_summary, - // this field is needed for the slot row-details - // on the failed_jobs_table.vue component - _showDetails: true, - }); - } - }); - } - - return combinedJobs; -}; 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 7020bfc1e65..ffb6ab71b22 100644 --- a/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue +++ b/app/assets/javascripts/pipelines/components/jobs_shared/action_component.vue @@ -1,6 +1,6 @@ <script> import { GlTooltipDirective, GlButton, GlLoadingIcon, GlIcon } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import { dasherize } from '~/lib/utils/text_utility'; diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue index ec42b738e03..936cd6f0be5 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue @@ -14,7 +14,7 @@ import { GlDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { __, sprintf } from '~/locale'; import eventHub from '../../event_hub'; diff --git a/app/assets/javascripts/pipelines/components/pipeline_tabs.vue b/app/assets/javascripts/pipelines/components/pipeline_tabs.vue index 3798863ae60..d2ec3c352fe 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_tabs.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_tabs.vue @@ -31,13 +31,7 @@ export default { GlTab, GlTabs, }, - inject: [ - 'defaultTabValue', - 'failedJobsCount', - 'failedJobsSummary', - 'totalJobCount', - 'testsCount', - ], + inject: ['defaultTabValue', 'failedJobsCount', 'totalJobCount', 'testsCount'], data() { return { activeTab: this.defaultTabValue, @@ -110,7 +104,7 @@ export default { <span class="gl-mr-2">{{ $options.i18n.tabs.failedJobsTitle }}</span> <gl-badge size="sm" data-testid="failed-builds-counter">{{ failedJobsCount }}</gl-badge> </template> - <router-view :failed-jobs-summary="failedJobsSummary" /> + <router-view /> </gl-tab> <gl-tab :active="isActive($options.tabNames.tests)" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue index 03a2eac89e4..a6297213402 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue @@ -1,19 +1,8 @@ <script> -import { GlButton, GlCard, GlSprintf, GlIcon, GlLink } from '@gitlab/ui'; +import { GlButton, GlCard, GlSprintf } from '@gitlab/ui'; import { mergeUrlParams } from '~/lib/utils/url_utility'; -import { - STARTER_TEMPLATE_NAME, - RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME, - RUNNERS_SETTINGS_LINK_CLICKED_EVENT, - RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT, - RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT, - I18N, -} from '~/ci/pipeline_editor/constants'; +import { STARTER_TEMPLATE_NAME, I18N } from '~/ci/pipeline_editor/constants'; import Tracking from '~/tracking'; -import { helpPagePath } from '~/helpers/help_page_helper'; -import { isExperimentVariant } from '~/experimentation/utils'; -import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue'; -import ExperimentTracking from '~/experimentation/experiment_tracking'; import CiTemplates from './ci_templates.vue'; export default { @@ -21,19 +10,12 @@ export default { GlButton, GlCard, GlSprintf, - GlIcon, - GlLink, - GitlabExperiment, CiTemplates, }, mixins: [Tracking.mixin()], STARTER_TEMPLATE_NAME, - RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME, - RUNNERS_SETTINGS_LINK_CLICKED_EVENT, - RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT, - RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT, I18N, - inject: ['anyRunnersAvailable', 'pipelineEditorPath', 'ciRunnerSettingsPath'], + inject: ['pipelineEditorPath'], data() { return { gettingStartedTemplateUrl: mergeUrlParams( @@ -43,26 +25,12 @@ export default { tracker: null, }; }, - computed: { - sharedRunnersHelpPagePath() { - return helpPagePath('ci/runners/runners_scope', { anchor: 'shared-runners' }); - }, - runnersAvailabilitySectionExperimentEnabled() { - return isExperimentVariant(RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME); - }, - }, - created() { - this.tracker = new ExperimentTracking(RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME); - }, methods: { trackEvent(template) { this.track('template_clicked', { label: template, }); }, - trackExperimentEvent(action) { - this.tracker.event(action); - }, }, }; </script> @@ -70,92 +38,42 @@ export default { <div> <h2 class="gl-font-size-h2 gl-text-gray-900">{{ $options.I18N.title }}</h2> - <gitlab-experiment :name="$options.RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME"> - <template #candidate> - <div v-if="anyRunnersAvailable"> - <h2 class="gl-font-base gl-text-gray-900"> - <gl-icon name="check-circle-filled" class="gl-text-green-500 gl-mr-2" :size="12" /> - {{ $options.I18N.runners.title }} - </h2> - <p class="gl-text-gray-800 gl-mb-6"> - <gl-sprintf :message="$options.I18N.runners.subtitle"> - <template #settingsLink="{ content }"> - <gl-link - data-testid="settings-link" - :href="ciRunnerSettingsPath" - @click="trackExperimentEvent($options.RUNNERS_SETTINGS_LINK_CLICKED_EVENT)" - >{{ content }}</gl-link - > - </template> - <template #docsLink="{ content }"> - <gl-link - data-testid="documentation-link" - :href="sharedRunnersHelpPagePath" - @click="trackExperimentEvent($options.RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT)" - >{{ content }}</gl-link - > - </template> - </gl-sprintf> - </p> - </div> - - <div v-else> - <h2 class="gl-font-base gl-text-gray-900"> - <gl-icon name="warning-solid" class="gl-text-red-600 gl-mr-2" :size="14" /> - {{ $options.I18N.noRunners.title }} - </h2> - <p class="gl-text-gray-800 gl-mb-6">{{ $options.I18N.noRunners.subtitle }}</p> - <gl-button - data-testid="settings-button" - category="primary" - variant="confirm" - :href="ciRunnerSettingsPath" - @click="trackExperimentEvent($options.RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT)" - > - {{ $options.I18N.noRunners.cta }} - </gl-button> - </div> - </template> - </gitlab-experiment> - - <template v-if="!runnersAvailabilitySectionExperimentEnabled || anyRunnersAvailable"> - <h2 class="gl-font-lg gl-text-gray-900">{{ $options.I18N.learnBasics.title }}</h2> - <p class="gl-text-gray-800 gl-mb-6"> - <gl-sprintf :message="$options.I18N.learnBasics.subtitle"> - <template #code="{ content }"> - <code>{{ content }}</code> - </template> - </gl-sprintf> - </p> + <h2 class="gl-font-lg gl-text-gray-900">{{ $options.I18N.learnBasics.title }}</h2> + <p class="gl-text-gray-800 gl-mb-6"> + <gl-sprintf :message="$options.I18N.learnBasics.subtitle"> + <template #code="{ content }"> + <code>{{ content }}</code> + </template> + </gl-sprintf> + </p> - <div class="gl-lg-w-25p gl-lg-pr-5 gl-mb-8"> - <gl-card> - <div class="gl-flex-direction-row"> - <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.learnBasics.gettingStarted.title }} - </strong> - </div> - <p class="gl-font-sm">{{ $options.I18N.learnBasics.gettingStarted.description }}</p> + <div class="gl-lg-w-25p gl-lg-pr-5 gl-mb-8"> + <gl-card> + <div class="gl-flex-direction-row"> + <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.learnBasics.gettingStarted.title }} + </strong> </div> + <p class="gl-font-sm">{{ $options.I18N.learnBasics.gettingStarted.description }}</p> + </div> - <gl-button - category="primary" - variant="confirm" - :href="gettingStartedTemplateUrl" - data-testid="test-template-link" - @click="trackEvent($options.STARTER_TEMPLATE_NAME)" - > - {{ $options.I18N.learnBasics.gettingStarted.cta }} - </gl-button> - </gl-card> - </div> + <gl-button + category="primary" + variant="confirm" + :href="gettingStartedTemplateUrl" + data-testid="test-template-link" + @click="trackEvent($options.STARTER_TEMPLATE_NAME)" + > + {{ $options.I18N.learnBasics.gettingStarted.cta }} + </gl-button> + </gl-card> + </div> - <h2 class="gl-font-lg gl-text-gray-900">{{ $options.I18N.templates.title }}</h2> - <p class="gl-text-gray-800 gl-mb-6">{{ $options.I18N.templates.subtitle }}</p> + <h2 class="gl-font-lg gl-text-gray-900">{{ $options.I18N.templates.title }}</h2> + <p class="gl-text-gray-800 gl-mb-6">{{ $options.I18N.templates.subtitle }}</p> - <ci-templates /> - </template> + <ci-templates /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue index dd62ffb27f7..caeee7edefe 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue @@ -36,12 +36,10 @@ export default { }; }, computed: { - actions() { - if (!this.pipeline || !this.pipeline.details) { - return []; - } - const { details } = this.pipeline; - return [...(details.manual_actions || []), ...(details.scheduled_actions || [])]; + hasActions() { + return ( + this.pipeline?.details?.has_manual_actions || this.pipeline?.details?.has_scheduled_actions + ); }, isCancelling() { return this.cancelingPipeline === this.pipeline.id; @@ -75,7 +73,7 @@ export default { <template> <div class="gl-text-right"> <div class="btn-group"> - <pipelines-manual-actions v-if="actions.length > 0" :actions="actions" /> + <pipelines-manual-actions v-if="hasActions" :iid="pipeline.iid" /> <gl-button v-if="pipeline.flags.retryable" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue index eb70b5fbb7a..9f38be668f2 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue @@ -42,7 +42,7 @@ export default { primaryProps() { return { text: s__('Pipeline|Stop pipeline'), - attributes: [{ variant: 'danger' }], + attributes: { variant: 'danger' }, }; }, cancelProps() { 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 fe2ef2c2d71..7ad12d397e5 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue @@ -144,14 +144,16 @@ export default { <tooltip-on-truncate :title="commitTitle" class="gl-flex-grow-1 gl-text-truncate"> <gl-link :href="commitUrl" - class="commit-row-message gl-text-gray-900" + class="commit-row-message gl-font-weight-bold gl-text-gray-900" data-testid="commit-title" @click="trackClick('click_commit_title')" >{{ commitTitle }}</gl-link > </tooltip-on-truncate> </span> - <span v-else>{{ __("Can't find HEAD commit for this branch") }}</span> + <span v-else class="gl-text-gray-500">{{ + __("Can't find HEAD commit for this branch") + }}</span> </div> <div class="gl-mb-2"> <gl-link diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue index 4111823e0bb..bcbf655a737 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue @@ -1,7 +1,7 @@ <script> import { GlEmptyState, GlIcon, GlLoadingIcon, GlCollapsibleListbox } from '@gitlab/ui'; import { isEqual } from 'lodash'; -import { createAlert, VARIANT_INFO, VARIANT_WARNING } from '~/flash'; +import { createAlert, VARIANT_INFO, VARIANT_WARNING } from '~/alert'; import { getParameterByName } from '~/lib/utils/url_utility'; import { __, s__ } from '~/locale'; import Tracking from '~/tracking'; @@ -189,6 +189,10 @@ export default { ); }, + shouldRenderPagination() { + return !this.isLoading && !this.hasError; + }, + emptyTabMessage() { if (this.scope === this.$options.scopes.finished) { return s__('Pipelines|There are currently no finished pipelines.'); @@ -346,7 +350,7 @@ export default { </div> <div v-if="stateToRender !== $options.stateMap.emptyState" class="gl-display-flex"> - <div class="row-content-block gl-display-flex gl-flex-grow-1"> + <div class="row-content-block gl-display-flex gl-flex-grow-1 gl-border-b-0"> <pipelines-filtered-search class="gl-display-flex gl-flex-grow-1 gl-mr-4" :project-id="projectId" @@ -381,10 +385,8 @@ export default { <gl-empty-state v-else-if="stateToRender === $options.stateMap.error" :svg-path="errorStateSvgPath" - :title=" - s__(`Pipelines|There was an error fetching the pipelines. - Try again in a few moments or contact your support team.`) - " + :title="s__('Pipelines|There was an error fetching the pipelines.')" + :description="s__('Pipelines|Try again in a few moments or contact your support team.')" /> <gl-empty-state diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue index f34b3f56c5b..262e82677a7 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue @@ -1,6 +1,6 @@ <script> -import { GlDropdown, GlDropdownItem, GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import { createAlert } from '~/flash'; +import { GlDropdown, GlDropdownItem, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; +import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; import { s__, __, sprintf } from '~/locale'; @@ -8,8 +8,10 @@ import Tracking from '~/tracking'; import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; import eventHub from '../../event_hub'; import { TRACKING_CATEGORIES } from '../../constants'; +import getPipelineActionsQuery from '../../graphql/queries/get_pipeline_actions.query.graphql'; export default { + name: 'PipelinesManualActions', directives: { GlTooltip: GlTooltipDirective, }, @@ -18,22 +20,52 @@ export default { GlDropdown, GlDropdownItem, GlIcon, + GlLoadingIcon, }, mixins: [Tracking.mixin()], + inject: ['fullPath', 'manualActionsLimit'], props: { - actions: { - type: Array, + iid: { + type: Number, required: true, }, }, + apollo: { + actions: { + query: getPipelineActionsQuery, + variables() { + return { + fullPath: this.fullPath, + iid: this.iid, + limit: this.manualActionsLimit, + }; + }, + skip() { + return !this.hasDropdownBeenShown; + }, + update({ project }) { + return project?.pipeline?.jobs?.nodes || []; + }, + }, + }, data() { return { isLoading: false, + actions: [], + hasDropdownBeenShown: false, }; }, + computed: { + isActionsLoading() { + return this.$apollo.queries.actions.loading; + }, + isDropdownLimitReached() { + return this.actions.length === this.manualActionsLimit; + }, + }, methods: { async onClickAction(action) { - if (action.scheduled_at) { + if (action.scheduledAt) { const confirmationMessage = sprintf( s__( 'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.', @@ -54,12 +86,12 @@ export default { * Ideally, the component would not make an api call directly. * However, in order to use the eventhub and know when to * toggle back the `isLoading` property we'd need an ID - * to track the request with a wacther - since this component + * to track the request with a watcher - since this component * is rendered at least 20 times in the same page, moving the * api call directly here is the most performant solution */ axios - .post(`${action.path}.json`) + .post(`${action.playPath}.json`) .then(() => { this.isLoading = false; eventHub.$emit('updateTable'); @@ -69,12 +101,12 @@ export default { createAlert({ message: __('An error occurred while making the request.') }); }); }, - isActionDisabled(action) { - if (action.playable === undefined) { - return false; - } + fetchActions() { + this.hasDropdownBeenShown = true; + + this.$apollo.queries.actions.refetch(); - return !action.playable; + this.trackClick(); }, trackClick() { this.track('click_manual_actions', { label: TRACKING_CATEGORIES.table }); @@ -91,21 +123,37 @@ export default { right lazy icon="play" - @shown="trackClick" + @shown="fetchActions" > + <gl-dropdown-item v-if="isActionsLoading"> + <div class="gl-display-flex"> + <gl-loading-icon class="mr-2" /> + <span>{{ __('Loading...') }}</span> + </div> + </gl-dropdown-item> + <gl-dropdown-item v-for="action in actions" - :key="action.path" - :disabled="isActionDisabled(action)" + v-else + :key="action.id" + :disabled="!action.canPlayJob" @click="onClickAction(action)" > <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap"> {{ action.name }} - <span v-if="action.scheduled_at"> + <span v-if="action.scheduledAt"> <gl-icon name="clock" /> - <gl-countdown :end-date-string="action.scheduled_at" /> + <gl-countdown :end-date-string="action.scheduledAt" /> </span> </div> </gl-dropdown-item> + + <template #footer> + <gl-dropdown-item v-if="isDropdownLimitReached"> + <span class="gl-font-sm gl-text-gray-300!" data-testid="limit-reached-msg"> + {{ __('Showing first 50 actions.') }} + </span> + </gl-dropdown-item> + </template> </gl-dropdown> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue index 365572f194b..b2da0df17c0 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue @@ -15,7 +15,7 @@ import PipelinesStatusBadge from './pipelines_status_badge.vue'; const DEFAULT_TD_CLASS = 'gl-p-5!'; const HIDE_TD_ON_MOBILE = 'gl-display-none! gl-lg-display-table-cell!'; const DEFAULT_TH_CLASSES = - 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1! gl-font-sm!'; + 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!'; export default { components: { diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue index 960af030421..d068eb16ed4 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue @@ -1,6 +1,6 @@ <script> import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import { durationTimeFormatted } from '~/lib/utils/datetime_utility'; +import { formatTime } from '~/lib/utils/datetime_utility'; import timeagoMixin from '~/vue_shared/mixins/timeago'; export default { @@ -20,7 +20,7 @@ export default { return this.pipeline?.details?.duration; }, durationFormatted() { - return durationTimeFormatted(this.duration); + return formatTime(this.duration * 1000); }, finishedTime() { return this.pipeline?.details?.finished_at; @@ -41,7 +41,7 @@ export default { }; </script> <template> - <div class="gl-display-flex gl-flex-direction-column time-ago"> + <div class="gl-display-flex gl-flex-direction-column gl-font-sm time-ago"> <span v-if="showInProgress" class="gl-display-inline-flex gl-align-items-center" 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 b57d0ac1fd7..81f46d5f2f9 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 @@ -2,7 +2,7 @@ import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui'; import { debounce } from 'lodash'; import Api from '~/api'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { FETCH_BRANCH_ERROR_MESSAGE, FILTER_PIPELINES_SEARCH_DELAY } from '../../../constants'; export default { 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 5846a1f6ed9..b32f5de2d7e 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 @@ -2,7 +2,7 @@ import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui'; import { debounce } from 'lodash'; import Api from '~/api'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { FETCH_TAG_ERROR_MESSAGE, FILTER_PIPELINES_SEARCH_DELAY } from '../../../constants'; export default { 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 73f7d3f52c3..a89354c671a 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 @@ -8,7 +8,7 @@ import { } from '@gitlab/ui'; import { debounce } from 'lodash'; import Api from '~/api'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { ANY_TRIGGER_AUTHOR, FETCH_AUTHOR_ERROR_MESSAGE, diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue b/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue index 2d1f1945e5a..10db3e1c56b 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue @@ -66,7 +66,7 @@ export default { }, modalCloseButton: { text: __('Close'), - attributes: [{ variant: 'confirm' }], + attributes: { variant: 'confirm' }, }, }; </script> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue index 1cd28e027f3..2974bd2dd37 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue @@ -100,7 +100,7 @@ export default { {{ __('Duration') }} </div> <div role="rowheader" class="table-section section-10"> - {{ __('Details'), }} + {{ __('Details') }} </div> </div> @@ -162,7 +162,7 @@ export default { </div> <div class="table-section section-10 section-wrap"> - <div role="rowheader" class="table-mobile-header">{{ __('Details'), }}</div> + <div role="rowheader" class="table-mobile-header">{{ __('Details') }}</div> <div class="table-mobile-content"> <gl-button v-gl-modal-directive="`test-case-details-${index}`">{{ __('View details') diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue index 7ab48da1a9d..2b7b2d78424 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue @@ -50,13 +50,13 @@ export default { {{ __('Failed') }} </div> <div role="rowheader" class="table-section section-10 gl-text-center"> - {{ __('Errors'), }} + {{ __('Errors') }} </div> <div role="rowheader" class="table-section section-10 gl-text-center"> - {{ __('Skipped'), }} + {{ __('Skipped') }} </div> <div role="rowheader" class="table-section section-10 gl-text-center"> - {{ __('Passed'), }} + {{ __('Passed') }} </div> <div role="rowheader" class="table-section section-10 gl-pr-5 gl-text-right"> {{ __('Total') }} |