From 3368b000beb38edc5beef8fbc554e465ce2a886d Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 25 Sep 2025 17:54:27 +0100 Subject: [PATCH 01/34] Creates getPipelineCreationRequests graphql query (cherry picked from commit 5254ee366355637b413b6860730e061599f12246) --- ...t_pipeline_creation_requests.query.graphql | 13 +++++++++ .../legacy_pipelines_table_wrapper.vue | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql diff --git a/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql b/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql new file mode 100644 index 00000000000000..39803d89cde9ae --- /dev/null +++ b/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql @@ -0,0 +1,13 @@ +query getPipelineCreationRequests($fullPath: ID!, $mergeRequestIid: String!) { + project(fullPath: $fullPath) { + mergeRequest(iid: $mergeRequestIid) { + id + iid + pipelineCreationRequests { + status + pipelineId + error + } + } + } +} \ No newline at end of file diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index a74d7f705fa25a..b3c3cce201eaad 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -10,6 +10,7 @@ import PipelinesService from '~/ci/pipelines_page/services/pipelines_service'; import PipelineStore from '~/ci/pipeline_details/stores/pipelines_store'; import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue'; import { s__, __ } from '~/locale'; +import getPipelineCreationRequests from '~/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql'; export default { components: { @@ -82,9 +83,34 @@ export default { page: getParameterByName('page') || '1', requestData: {}, modalId: 'create-pipeline-for-fork-merge-request-modal', + pipelineCreationRequests: [], }; }, + apollo: { + pipelineCreationRequests: { + query: getPipelineCreationRequests, + variables() { + return { + fullPath: this.targetProjectFullPath, + mergeRequestIid: this.mergeRequestId.toString(), + }; + }, + skip() { + return !this.isMergeRequestTable || !this.mergeRequestId || !this.targetProjectFullPath; + }, + pollInterval() { + return this.inProgressRequests.length > 0 ? 1000 : 0; + }, + update(data) { + return data.project?.mergeRequest?.pipelineCreationRequests || []; + }, + error(error) { + console.error('Failed to fetch pipeline creation requests:', error); + }, + }, + }, + computed: { shouldRenderTable() { return !this.isLoading && this.state.pipelines.length > 0 && !this.hasError; @@ -135,6 +161,9 @@ export default { (latest.flags.detached_merge_request_pipeline || latest.flags.merge_request_pipeline) ); }, + inProgressRequests() { + return this.pipelineCreationRequests.filter(req => req.status === 'IN_PROGRESS'); + }, }, created() { this.service = new PipelinesService(this.endpoint); -- GitLab From 64b61592e6b4dafb55a6b0dcc8e8ee04ecad9893 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 25 Sep 2025 17:56:28 +0100 Subject: [PATCH 02/34] Left hook fixes (cherry picked from commit 81df8f777a801c21b4fddf0ee56378c3833ba335) --- .../queries/get_pipeline_creation_requests.query.graphql | 2 +- .../commit/pipelines/legacy_pipelines_table_wrapper.vue | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql b/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql index 39803d89cde9ae..ecd2982043983e 100644 --- a/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql +++ b/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql @@ -10,4 +10,4 @@ query getPipelineCreationRequests($fullPath: ID!, $mergeRequestIid: String!) { } } } -} \ No newline at end of file +} diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index b3c3cce201eaad..272031a426d5ad 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -90,7 +90,7 @@ export default { apollo: { pipelineCreationRequests: { query: getPipelineCreationRequests, - variables() { + variables() { return { fullPath: this.targetProjectFullPath, mergeRequestIid: this.mergeRequestId.toString(), @@ -105,9 +105,6 @@ export default { update(data) { return data.project?.mergeRequest?.pipelineCreationRequests || []; }, - error(error) { - console.error('Failed to fetch pipeline creation requests:', error); - }, }, }, @@ -162,7 +159,7 @@ export default { ); }, inProgressRequests() { - return this.pipelineCreationRequests.filter(req => req.status === 'IN_PROGRESS'); + return this.pipelineCreationRequests.filter((req) => req.status === 'IN_PROGRESS'); }, }, created() { -- GitLab From 0479b03a87da94d5476da795f2f1568f1702754e Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 2 Oct 2025 03:56:10 +0530 Subject: [PATCH 03/34] Add GraphQL subscription for pipeline creation - Add ciPipelineCreationRequestsUpdated subscription to track real-time pipeline creation status - Integrate subscription in legacy_pipelines_table_wrapper.vue to replace polling - Add GraphQL triggers in merge request pipeline creation service - Update MergeRequest type to include pipelineCreationRequests field --- ...tion_requests_updated.subscription.graphql | 11 ++++++ .../legacy_pipelines_table_wrapper.vue | 37 +++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/ci/merge_requests/graphql/subscriptions/pipeline_creation_requests_updated.subscription.graphql diff --git a/app/assets/javascripts/ci/merge_requests/graphql/subscriptions/pipeline_creation_requests_updated.subscription.graphql b/app/assets/javascripts/ci/merge_requests/graphql/subscriptions/pipeline_creation_requests_updated.subscription.graphql new file mode 100644 index 00000000000000..8a8f9055b184ed --- /dev/null +++ b/app/assets/javascripts/ci/merge_requests/graphql/subscriptions/pipeline_creation_requests_updated.subscription.graphql @@ -0,0 +1,11 @@ +subscription ciPipelineCreationRequestsUpdated($mergeRequestId: MergeRequestID!) { + ciPipelineCreationRequestsUpdated(mergeRequestId: $mergeRequestId) { + id + iid + pipelineCreationRequests { + status + pipelineId + error + } + } +} diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index 272031a426d5ad..1884e0ce940f71 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -11,6 +11,7 @@ import PipelineStore from '~/ci/pipeline_details/stores/pipelines_store'; import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue'; import { s__, __ } from '~/locale'; import getPipelineCreationRequests from '~/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql'; +import pipelineCreationRequestsUpdatedSubscription from '~/ci/merge_requests/graphql/subscriptions/pipeline_creation_requests_updated.subscription.graphql'; export default { components: { @@ -82,6 +83,7 @@ export default { state: store.state, page: getParameterByName('page') || '1', requestData: {}, + mergeRequestGlobalId: null, modalId: 'create-pipeline-for-fork-merge-request-modal', pipelineCreationRequests: [], }; @@ -100,11 +102,40 @@ export default { return !this.isMergeRequestTable || !this.mergeRequestId || !this.targetProjectFullPath; }, pollInterval() { - return this.inProgressRequests.length > 0 ? 1000 : 0; + return this.hasInProgressCreationRequests ? 10000 : 0; }, update(data) { + if (data.project?.mergeRequest?.id) { + this.mergeRequestGlobalId = data.project.mergeRequest.id; + } return data.project?.mergeRequest?.pipelineCreationRequests || []; }, + subscribeToMore: { + document: pipelineCreationRequestsUpdatedSubscription, + variables() { + return { + mergeRequestId: this.mergeRequestGlobalId, + }; + }, + skip() { + return !this.isMergeRequestTable || !this.mergeRequestGlobalId; + }, + updateQuery: (previousResult, { subscriptionData }) => { + if (!subscriptionData.data?.ciPipelineCreationRequestsUpdated) return previousResult; + + const updated = subscriptionData.data.ciPipelineCreationRequestsUpdated; + return { + ...previousResult, + project: { + ...previousResult.project, + mergeRequest: { + ...previousResult.project.mergeRequest, + pipelineCreationRequests: updated.pipelineCreationRequests, + }, + }, + }; + }, + }, }, }, @@ -158,8 +189,8 @@ export default { (latest.flags.detached_merge_request_pipeline || latest.flags.merge_request_pipeline) ); }, - inProgressRequests() { - return this.pipelineCreationRequests.filter((req) => req.status === 'IN_PROGRESS'); + hasInProgressCreationRequests() { + return this.pipelineCreationRequests.some((req) => req.status === 'IN_PROGRESS'); }, }, created() { -- GitLab From de950ed9ee151746cbdf86848a990b1718f0b6b7 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 2 Oct 2025 05:48:17 +0530 Subject: [PATCH 04/34] Update loader state based on subscription response --- .../legacy_pipelines_table_wrapper.vue | 130 ++++++++++-------- 1 file changed, 75 insertions(+), 55 deletions(-) diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index 1884e0ce940f71..e527ec2c75a6d2 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -89,56 +89,6 @@ export default { }; }, - apollo: { - pipelineCreationRequests: { - query: getPipelineCreationRequests, - variables() { - return { - fullPath: this.targetProjectFullPath, - mergeRequestIid: this.mergeRequestId.toString(), - }; - }, - skip() { - return !this.isMergeRequestTable || !this.mergeRequestId || !this.targetProjectFullPath; - }, - pollInterval() { - return this.hasInProgressCreationRequests ? 10000 : 0; - }, - update(data) { - if (data.project?.mergeRequest?.id) { - this.mergeRequestGlobalId = data.project.mergeRequest.id; - } - return data.project?.mergeRequest?.pipelineCreationRequests || []; - }, - subscribeToMore: { - document: pipelineCreationRequestsUpdatedSubscription, - variables() { - return { - mergeRequestId: this.mergeRequestGlobalId, - }; - }, - skip() { - return !this.isMergeRequestTable || !this.mergeRequestGlobalId; - }, - updateQuery: (previousResult, { subscriptionData }) => { - if (!subscriptionData.data?.ciPipelineCreationRequestsUpdated) return previousResult; - - const updated = subscriptionData.data.ciPipelineCreationRequestsUpdated; - return { - ...previousResult, - project: { - ...previousResult.project, - mergeRequest: { - ...previousResult.project.mergeRequest, - pipelineCreationRequests: updated.pipelineCreationRequests, - }, - }, - }; - }, - }, - }, - }, - computed: { shouldRenderTable() { return !this.isLoading && this.state.pipelines.length > 0 && !this.hasError; @@ -190,7 +140,19 @@ export default { ); }, hasInProgressCreationRequests() { - return this.pipelineCreationRequests.some((req) => req.status === 'IN_PROGRESS'); + return ( + this.pipelineCreationRequests?.some((request) => request.status === 'IN_PROGRESS') ?? false + ); + }, + }, + watch: { + pipelineCreationRequests: { + handler(newRequests, oldRequests) { + if (this.hasInProgressRequestsCompleted(oldRequests, newRequests)) { + this.onRefreshPipelinesTable(); + } + }, + deep: true, }, }, created() { @@ -241,6 +203,61 @@ export default { this.$refs.modal.show(); } }, + hasInProgressRequestsCompleted(previousRequests = [], currentRequests = []) { + const prevInProgressCount = this.countRequestsByStatus(previousRequests, 'IN_PROGRESS'); + const currInProgressCount = this.countRequestsByStatus(currentRequests, 'IN_PROGRESS'); + + return currInProgressCount < prevInProgressCount; + }, + countRequestsByStatus(requests, status) { + return requests.filter((request) => request.status === status).length; + }, + }, + apollo: { + pipelineCreationRequests: { + query: getPipelineCreationRequests, + variables() { + return { + fullPath: this.targetProjectFullPath, + mergeRequestIid: this.mergeRequestId.toString(), + }; + }, + skip() { + return !this.isMergeRequestTable || !this.mergeRequestId || !this.targetProjectFullPath; + }, + update(data) { + if (data.project?.mergeRequest?.id) { + this.mergeRequestGlobalId = data.project.mergeRequest.id; + } + return data.project?.mergeRequest?.pipelineCreationRequests || []; + }, + subscribeToMore: { + document: pipelineCreationRequestsUpdatedSubscription, + variables() { + return { + mergeRequestId: this.mergeRequestGlobalId, + }; + }, + skip() { + return !this.isMergeRequestTable || !this.mergeRequestGlobalId; + }, + updateQuery: (previousResult, { subscriptionData }) => { + if (!subscriptionData.data?.ciPipelineCreationRequestsUpdated) return previousResult; + + const updated = subscriptionData.data.ciPipelineCreationRequestsUpdated; + return { + ...previousResult, + project: { + ...previousResult.project, + mergeRequest: { + ...previousResult.project.mergeRequest, + pipelineCreationRequests: updated.pipelineCreationRequests, + }, + }, + }; + }, + }, + }, }, pipelineIdKey: PIPELINE_ID_KEY, modal: { @@ -330,7 +347,8 @@ export default {
@@ -349,7 +367,8 @@ export default { {{ $options.i18n.runPipelineText }} @@ -357,7 +376,7 @@ export default {
{{ $options.i18n.runPipelineText }} -- GitLab From f16e2f9b646a80e81eb7dd0825f8fc7d06c3b5bf Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 2 Oct 2025 06:37:15 +0530 Subject: [PATCH 05/34] Remove duplicate pipelines.json call --- .../javascripts/ci/pipeline_details/mixins/pipelines_mixin.js | 3 +-- .../commit/pipelines/legacy_pipelines_table_wrapper.vue | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js b/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js index 074e5b49759000..ab812eb653f88d 100644 --- a/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js +++ b/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js @@ -201,9 +201,8 @@ export default { .then(() => { if (!options.isAsync) { this.$toast.show(TOAST_MESSAGE); + this.updateTable(); } - - this.updateTable(); }) .catch((e) => { const unauthorized = e.response.status === HTTP_STATUS_UNAUTHORIZED; diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index e527ec2c75a6d2..81c23b4a876d7f 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -140,9 +140,7 @@ export default { ); }, hasInProgressCreationRequests() { - return ( - this.pipelineCreationRequests?.some((request) => request.status === 'IN_PROGRESS') ?? false - ); + return this.countRequestsByStatus(this.pipelineCreationRequests, 'IN_PROGRESS') > 0; }, }, watch: { -- GitLab From 1cd9b828cb0672679434f0e1b250fe4285e7ee4e Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Fri, 3 Oct 2025 05:40:38 +0530 Subject: [PATCH 06/34] Add failed alert & remove skeleton loader --- .../javascripts/ci/common/pipelines_table.vue | 9 ----- ...t_pipeline_creation_requests.query.graphql | 1 + .../legacy_pipelines_table_wrapper.vue | 39 +++++++++++++++---- locale/gitlab.pot | 3 ++ 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/ci/common/pipelines_table.vue b/app/assets/javascripts/ci/common/pipelines_table.vue index 8fa59609f26be8..ccc83ea4d06658 100644 --- a/app/assets/javascripts/ci/common/pipelines_table.vue +++ b/app/assets/javascripts/ci/common/pipelines_table.vue @@ -59,11 +59,6 @@ export default { }, }, props: { - isCreatingPipeline: { - type: Boolean, - required: false, - default: false, - }, pipelines: { type: Array, required: true, @@ -122,10 +117,6 @@ export default { pipelinesWithDetails() { let { pipelines } = this; - if (this.isCreatingPipeline) { - pipelines = [{ isLoading: true }, ...this.pipelines]; - } - if (this.useFailedJobsWidget) { pipelines = pipelines.map((p) => { return p.failed_builds_count > 0 ? { ...p, _showDetails: true } : p; diff --git a/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql b/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql index ecd2982043983e..4d706f7339d50a 100644 --- a/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql +++ b/app/assets/javascripts/ci/merge_requests/graphql/queries/get_pipeline_creation_requests.query.graphql @@ -1,5 +1,6 @@ query getPipelineCreationRequests($fullPath: ID!, $mergeRequestIid: String!) { project(fullPath: $fullPath) { + id mergeRequest(iid: $mergeRequestIid) { id iid diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index 81c23b4a876d7f..f469746564d02d 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -1,5 +1,13 @@