From 7a698cdc7a9f86c8953710c2e6881815e3546b8c Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Mon, 24 Nov 2025 12:08:39 +0000 Subject: [PATCH 01/17] Trigger error only when sifekiq retries have been exhausted --- app/models/ci/pipeline_creation/requests.rb | 15 ++++++++ .../merge_requests/create_pipeline_service.rb | 2 +- .../merge_requests/create_pipeline_worker.rb | 25 ++++++++++++- .../merge_requests/create_pipeline_service.rb | 6 ++++ .../create_pipeline_worker_spec.rb | 35 +++++++++++++++++++ 5 files changed, 81 insertions(+), 2 deletions(-) diff --git a/app/models/ci/pipeline_creation/requests.rb b/app/models/ci/pipeline_creation/requests.rb index a062a67daa2f38..5d1418180b9f31 100644 --- a/app/models/ci/pipeline_creation/requests.rb +++ b/app/models/ci/pipeline_creation/requests.rb @@ -97,6 +97,21 @@ def merge_request_key(merge_request) format(MERGE_REQUEST_REDIS_KEY, project_id: merge_request.project_id, mr_id: merge_request.id) end + # Extracts merge request ID from Redis key and returns the MergeRequest object + # + # @param key [String] Redis key in MERGE_REQUEST_REDIS_KEY format + # @return [MergeRequest, nil] The MergeRequest object or nil if not found + def merge_request_from_key(key) + match = key.match(/mrs:\{(?\d+)\}/) + return unless match + + mr_id = match[:mr_id].to_i + + MergeRequest.find(mr_id) + rescue ActiveRecord::RecordNotFound + nil + end + def hset(request, status, pipeline_id: nil, error: nil) Gitlab::Redis::SharedState.with do |redis| redis.multi do |transaction| diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index 6851924d651280..21951f22692398 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -102,7 +102,7 @@ def cannot_create_pipeline_error ::Ci::PipelineCreation::Requests.failed(params[:pipeline_creation_request], error_message) - ServiceResponse.error(message: error_message, payload: nil) + ServiceResponse.error(message: error_message, payload: nil, reason: :retriable_error) end end end diff --git a/app/workers/merge_requests/create_pipeline_worker.rb b/app/workers/merge_requests/create_pipeline_worker.rb index d2d10dedcb278b..ed6083da452d65 100644 --- a/app/workers/merge_requests/create_pipeline_worker.rb +++ b/app/workers/merge_requests/create_pipeline_worker.rb @@ -15,6 +15,20 @@ class CreatePipelineWorker worker_resource_boundary :cpu idempotent! + sidekiq_retries_exhausted do |job, _exception| + pipeline_creation_request = job['args'][3]&.dig('pipeline_creation_request') + next unless pipeline_creation_request + + merge_request = ::Ci::PipelineCreation::Requests.merge_request_from_key(pipeline_creation_request&.dig('key')) + + error_message = 'Cannot create a pipeline for this merge request after multiple retries.' + Gitlab::AppJsonLogger.info( + message: "pipeline_service sidekiq_retries_exhausted" + ) + ::Ci::PipelineCreation::Requests.failed(pipeline_creation_request, error_message) + GraphqlTriggers.ci_pipeline_creation_requests_updated(merge_request) + end + def perform(project_id, user_id, merge_request_id, params = {}) Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/464679') @@ -32,7 +46,7 @@ def perform(project_id, user_id, merge_request_id, params = {}) push_options = params.with_indifferent_access[:push_options] gitaly_context = params.with_indifferent_access[:gitaly_context] - MergeRequests::CreatePipelineService + result = MergeRequests::CreatePipelineService .new( project: project, current_user: user, @@ -44,6 +58,15 @@ def perform(project_id, user_id, merge_request_id, params = {}) } ).execute(merge_request) + # If the service returned a retriable error, raise to trigger Sidekiq retry + # The request will only be marked as FAILED when retries are exhausted (see sidekiq_retries_exhausted hook) + Gitlab::AppJsonLogger.info( + message: "pipeline_service error", + error: result.error?, + reason: result.reason + ) + raise StandardError, result.message if result.error? && result.reason == :retriable_error + merge_request.update_head_pipeline after_perform(merge_request) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 8d3b3ce1f53af3..6c5a72e7fee358 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -21,6 +21,9 @@ def allowed?(merge_request) private def create_merged_result_pipeline_for(merge_request) + # Gitlab::AppJsonLogger.info( + # message: "pipeline_service create_merged_result_pipeline_for", + # ) return cannot_create_pipeline_error unless can_create_merged_result_pipeline_for?(merge_request) result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute(recheck: true) @@ -38,6 +41,9 @@ def create_merged_result_pipeline_for(merge_request) push_options: params[:push_options]) .execute(:merge_request_event, merge_request: merge_request) else + # Gitlab::AppJsonLogger.info( + # message: "pipeline_service create_merged_result_pipeline_for else", + # ) cannot_create_pipeline_error end end diff --git a/spec/workers/merge_requests/create_pipeline_worker_spec.rb b/spec/workers/merge_requests/create_pipeline_worker_spec.rb index 5752fe987599a8..0f77ca9467ea9b 100644 --- a/spec/workers/merge_requests/create_pipeline_worker_spec.rb +++ b/spec/workers/merge_requests/create_pipeline_worker_spec.rb @@ -104,4 +104,39 @@ it_behaves_like 'when object does not exist' end end + + describe 'retry behavior' do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:merge_request) { create(:merge_request) } + let(:worker) { described_class.new } + let(:pipeline_creation_request) { Ci::PipelineCreation::Requests.start_for_merge_request(merge_request) } + let(:params) do + { + 'pipeline_creation_request' => pipeline_creation_request, + 'gitaly_context' => {} + } + end + + subject { worker.perform(project.id, user.id, merge_request.id, params) } + + context 'when service raises a retriable error' do + before do + allow_next_instance_of(MergeRequests::CreatePipelineService) do |service| + allow(service).to receive(:execute).and_raise(StandardError, 'Temporary failure') + end + end + + it 'raises the error to trigger Sidekiq retry' do + expect { subject }.to raise_error(StandardError, 'Temporary failure') + end + + it 'keeps status as IN_PROGRESS during retries', :clean_gitlab_redis_shared_state do + expect { subject }.to raise_error(StandardError) + + result = Ci::PipelineCreation::Requests.hget(pipeline_creation_request) + expect(result['status']).to eq('in_progress') + end + end + end end -- GitLab From b7daac217b793fd77b5b655a2ae9d791436b83d2 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Tue, 25 Nov 2025 13:34:01 +0000 Subject: [PATCH 02/17] Remove duplicate failed status update --- app/services/merge_requests/create_pipeline_service.rb | 4 ---- app/workers/merge_requests/create_pipeline_worker.rb | 10 ++-------- .../ee/merge_requests/create_pipeline_service.rb | 6 ------ 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index 21951f22692398..1f94fdfbbdc6a1 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -98,10 +98,6 @@ def can_update_source_branch_in_target_project?(merge_request) end def cannot_create_pipeline_error - error_message = 'Cannot create a pipeline for this merge request.' - - ::Ci::PipelineCreation::Requests.failed(params[:pipeline_creation_request], error_message) - ServiceResponse.error(message: error_message, payload: nil, reason: :retriable_error) end end diff --git a/app/workers/merge_requests/create_pipeline_worker.rb b/app/workers/merge_requests/create_pipeline_worker.rb index ed6083da452d65..f7c970be40eba8 100644 --- a/app/workers/merge_requests/create_pipeline_worker.rb +++ b/app/workers/merge_requests/create_pipeline_worker.rb @@ -22,9 +22,7 @@ class CreatePipelineWorker merge_request = ::Ci::PipelineCreation::Requests.merge_request_from_key(pipeline_creation_request&.dig('key')) error_message = 'Cannot create a pipeline for this merge request after multiple retries.' - Gitlab::AppJsonLogger.info( - message: "pipeline_service sidekiq_retries_exhausted" - ) + ::Ci::PipelineCreation::Requests.failed(pipeline_creation_request, error_message) GraphqlTriggers.ci_pipeline_creation_requests_updated(merge_request) end @@ -60,11 +58,7 @@ def perform(project_id, user_id, merge_request_id, params = {}) # If the service returned a retriable error, raise to trigger Sidekiq retry # The request will only be marked as FAILED when retries are exhausted (see sidekiq_retries_exhausted hook) - Gitlab::AppJsonLogger.info( - message: "pipeline_service error", - error: result.error?, - reason: result.reason - ) + raise StandardError, result.message if result.error? && result.reason == :retriable_error merge_request.update_head_pipeline diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 6c5a72e7fee358..8d3b3ce1f53af3 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -21,9 +21,6 @@ def allowed?(merge_request) private def create_merged_result_pipeline_for(merge_request) - # Gitlab::AppJsonLogger.info( - # message: "pipeline_service create_merged_result_pipeline_for", - # ) return cannot_create_pipeline_error unless can_create_merged_result_pipeline_for?(merge_request) result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute(recheck: true) @@ -41,9 +38,6 @@ def create_merged_result_pipeline_for(merge_request) push_options: params[:push_options]) .execute(:merge_request_event, merge_request: merge_request) else - # Gitlab::AppJsonLogger.info( - # message: "pipeline_service create_merged_result_pipeline_for else", - # ) cannot_create_pipeline_error end end -- GitLab From a8c82bdda0cec5e536c8e0dea5872e302d5e0766 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 4 Dec 2025 09:37:26 +0000 Subject: [PATCH 03/17] Differentiate between temporary & permanent errors --- .../merge_requests/create_pipeline_service.rb | 24 ++++++++++++++++--- .../merge_requests/create_pipeline_service.rb | 14 ++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index 1f94fdfbbdc6a1..697492f2391a52 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -3,7 +3,13 @@ module MergeRequests class CreatePipelineService < MergeRequests::BaseService def execute(merge_request) - return cannot_create_pipeline_error unless can_create_pipeline_for?(merge_request) + # Check for permanent validation errors first + return permanent_validation_error('no commits to build') if merge_request.has_no_commits? + + # Check for retriable race conditions + if !allow_duplicate && merge_request.find_diff_head_pipeline&.merge_request? + return retriable_validation_error('duplicate pipeline detected') + end create_merge_request_pipeline(merge_request) end @@ -97,8 +103,20 @@ def can_update_source_branch_in_target_project?(merge_request) .can_update_branch?(merge_request.source_branch) end - def cannot_create_pipeline_error - ServiceResponse.error(message: error_message, payload: nil, reason: :retriable_error) + def permanent_validation_error(reason) + message = "Cannot create a pipeline for this merge request: #{reason}." + + # Mark as failed ASAP - no need to retry + ::Ci::PipelineCreation::Requests.failed(params[:pipeline_creation_request], message) + + ServiceResponse.error(message: message, payload: nil) + end + + def retriable_validation_error(reason) + message = "Cannot create a pipeline for this merge request: #{reason}." + + # now THIS we retry + ServiceResponse.error(message: message, payload: nil, reason: :retriable_error) end end end diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 8d3b3ce1f53af3..53f806fbd4eef2 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -21,7 +21,13 @@ def allowed?(merge_request) private def create_merged_result_pipeline_for(merge_request) - return cannot_create_pipeline_error unless can_create_merged_result_pipeline_for?(merge_request) + unless can_create_merged_result_pipeline_for?(merge_request) + return permanent_validation_error('merged result pipeline not allowed') + end + + if !allow_duplicate && merge_request.find_diff_head_pipeline&.merge_request? + return retriable_validation_error('duplicate pipeline detected') + end result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute(recheck: true) @@ -38,7 +44,7 @@ def create_merged_result_pipeline_for(merge_request) push_options: params[:push_options]) .execute(:merge_request_event, merge_request: merge_request) else - cannot_create_pipeline_error + permanent_validation_error('mergeability check failed') end end @@ -46,7 +52,9 @@ def can_create_merged_result_pipeline_for?(merge_request) return false unless merge_request.project.merge_pipelines_enabled? return false unless can_create_pipeline_in_target_project?(merge_request) - can_create_pipeline_for?(merge_request) + return false if merge_request.has_no_commits? + + true end def merge_status_race?(merge_request, result) -- GitLab From 793b1ded9c6625713ffa96ad501f7e3a9772f9b0 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 4 Dec 2025 09:37:50 +0000 Subject: [PATCH 04/17] Decrease retry exponential backoff duration --- app/workers/merge_requests/create_pipeline_worker.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/workers/merge_requests/create_pipeline_worker.rb b/app/workers/merge_requests/create_pipeline_worker.rb index f7c970be40eba8..051355a96f1592 100644 --- a/app/workers/merge_requests/create_pipeline_worker.rb +++ b/app/workers/merge_requests/create_pipeline_worker.rb @@ -6,7 +6,10 @@ class CreatePipelineWorker data_consistency :sticky - sidekiq_options retry: 3 + sidekiq_options retry: 2 + sidekiq_retry_in do |_count| + 10 + end include PipelineQueue queue_namespace :pipeline_creation -- GitLab From cc3a67742fe3ca172d0b2a70f0b072be4e854bd4 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 4 Dec 2025 15:47:55 +0000 Subject: [PATCH 05/17] Check pipeline status before retrying duplicate errors --- app/services/merge_requests/create_pipeline_service.rb | 8 ++++++-- .../services/ee/merge_requests/create_pipeline_service.rb | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index 697492f2391a52..d5e0ad1bd0eb7b 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -7,8 +7,12 @@ def execute(merge_request) return permanent_validation_error('no commits to build') if merge_request.has_no_commits? # Check for retriable race conditions - if !allow_duplicate && merge_request.find_diff_head_pipeline&.merge_request? - return retriable_validation_error('duplicate pipeline detected') + unless allow_duplicate + existing_pipeline = merge_request.find_diff_head_pipeline + + if existing_pipeline&.merge_request? && existing_pipeline.running_or_pending? + return retriable_validation_error('duplicate pipeline still in progress') + end end create_merge_request_pipeline(merge_request) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 53f806fbd4eef2..ae22aea6b34acc 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -25,8 +25,12 @@ def create_merged_result_pipeline_for(merge_request) return permanent_validation_error('merged result pipeline not allowed') end - if !allow_duplicate && merge_request.find_diff_head_pipeline&.merge_request? - return retriable_validation_error('duplicate pipeline detected') + unless allow_duplicate + existing_pipeline = merge_request.find_diff_head_pipeline + + if existing_pipeline&.merge_request? && existing_pipeline.running_or_pending? + return retriable_validation_error('duplicate pipeline still in progress') + end end result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute(recheck: true) -- GitLab From d3784640421439d07af9e858eb3970e838726f36 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Fri, 5 Dec 2025 10:23:01 +0000 Subject: [PATCH 06/17] Make merge pipelines enabled check retriable --- ee/app/services/ee/merge_requests/create_pipeline_service.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index ae22aea6b34acc..bdc72bb8ef5659 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -21,6 +21,10 @@ def allowed?(merge_request) private def create_merged_result_pipeline_for(merge_request) + unless merge_request.project.merge_pipelines_enabled? + return retriable_validation_error('merge pipeline not enabled') + end + unless can_create_merged_result_pipeline_for?(merge_request) return permanent_validation_error('merged result pipeline not allowed') end @@ -53,7 +57,6 @@ def create_merged_result_pipeline_for(merge_request) end def can_create_merged_result_pipeline_for?(merge_request) - return false unless merge_request.project.merge_pipelines_enabled? return false unless can_create_pipeline_in_target_project?(merge_request) return false if merge_request.has_no_commits? -- GitLab From 240933b3426b13aa5112cc8286d666271204e3a8 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Fri, 5 Dec 2025 11:11:18 +0000 Subject: [PATCH 07/17] Increase retry count --- app/workers/merge_requests/create_pipeline_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/merge_requests/create_pipeline_worker.rb b/app/workers/merge_requests/create_pipeline_worker.rb index 051355a96f1592..8042174b2fd0a0 100644 --- a/app/workers/merge_requests/create_pipeline_worker.rb +++ b/app/workers/merge_requests/create_pipeline_worker.rb @@ -6,7 +6,7 @@ class CreatePipelineWorker data_consistency :sticky - sidekiq_options retry: 2 + sidekiq_options retry: 3 sidekiq_retry_in do |_count| 10 end -- GitLab From aa3c73df595ef9c4b36eb70cc6f9d85f632e18b5 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Tue, 9 Dec 2025 11:42:50 +0000 Subject: [PATCH 08/17] Add tests and cleanup code --- .../merge_requests/create_pipeline_service.rb | 27 ++---- .../merge_requests/create_pipeline_worker.rb | 5 +- .../merge_requests/create_pipeline_service.rb | 10 +- .../create_pipeline_service_spec.rb | 94 ++++++++++++++++++- 4 files changed, 108 insertions(+), 28 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index d5e0ad1bd0eb7b..c7709968f9a595 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -3,15 +3,13 @@ module MergeRequests class CreatePipelineService < MergeRequests::BaseService def execute(merge_request) - # Check for permanent validation errors first - return permanent_validation_error('no commits to build') if merge_request.has_no_commits? + return cannot_create_pipeline_error('no commits to build') if merge_request.has_no_commits? - # Check for retriable race conditions unless allow_duplicate existing_pipeline = merge_request.find_diff_head_pipeline - if existing_pipeline&.merge_request? && existing_pipeline.running_or_pending? - return retriable_validation_error('duplicate pipeline still in progress') + if existing_pipeline&.merge_request? && existing_pipeline.active? + return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) end end @@ -107,20 +105,15 @@ def can_update_source_branch_in_target_project?(merge_request) .can_update_branch?(merge_request.source_branch) end - def permanent_validation_error(reason) + def cannot_create_pipeline_error(reason, retriable: false) message = "Cannot create a pipeline for this merge request: #{reason}." - # Mark as failed ASAP - no need to retry - ::Ci::PipelineCreation::Requests.failed(params[:pipeline_creation_request], message) - - ServiceResponse.error(message: message, payload: nil) - end - - def retriable_validation_error(reason) - message = "Cannot create a pipeline for this merge request: #{reason}." - - # now THIS we retry - ServiceResponse.error(message: message, payload: nil, reason: :retriable_error) + if retriable + ServiceResponse.error(message: message, payload: nil, reason: :retriable_error) + else + ::Ci::PipelineCreation::Requests.failed(params[:pipeline_creation_request], message) + ServiceResponse.error(message: message, payload: nil) + end end end end diff --git a/app/workers/merge_requests/create_pipeline_worker.rb b/app/workers/merge_requests/create_pipeline_worker.rb index 8042174b2fd0a0..0f05128df1f73f 100644 --- a/app/workers/merge_requests/create_pipeline_worker.rb +++ b/app/workers/merge_requests/create_pipeline_worker.rb @@ -59,10 +59,7 @@ def perform(project_id, user_id, merge_request_id, params = {}) } ).execute(merge_request) - # If the service returned a retriable error, raise to trigger Sidekiq retry - # The request will only be marked as FAILED when retries are exhausted (see sidekiq_retries_exhausted hook) - - raise StandardError, result.message if result.error? && result.reason == :retriable_error + raise StandardError, result.message if result&.error? && result.reason == :retriable_error merge_request.update_head_pipeline diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index bdc72bb8ef5659..a3ab046d758361 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -22,18 +22,18 @@ def allowed?(merge_request) def create_merged_result_pipeline_for(merge_request) unless merge_request.project.merge_pipelines_enabled? - return retriable_validation_error('merge pipeline not enabled') + return cannot_create_pipeline_error('merge pipeline not enabled', retriable: true) end unless can_create_merged_result_pipeline_for?(merge_request) - return permanent_validation_error('merged result pipeline not allowed') + return cannot_create_pipeline_error('merged result pipeline not allowed') end unless allow_duplicate existing_pipeline = merge_request.find_diff_head_pipeline - if existing_pipeline&.merge_request? && existing_pipeline.running_or_pending? - return retriable_validation_error('duplicate pipeline still in progress') + if existing_pipeline&.merge_request? && existing_pipeline.active? + return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) end end @@ -52,7 +52,7 @@ def create_merged_result_pipeline_for(merge_request) push_options: params[:push_options]) .execute(:merge_request_event, merge_request: merge_request) else - permanent_validation_error('mergeability check failed') + cannot_create_pipeline_error('mergeability check failed') end end diff --git a/spec/services/merge_requests/create_pipeline_service_spec.rb b/spec/services/merge_requests/create_pipeline_service_spec.rb index 19d71d8a90d11e..804bc9e5c54df3 100644 --- a/spec/services/merge_requests/create_pipeline_service_spec.rb +++ b/spec/services/merge_requests/create_pipeline_service_spec.rb @@ -173,7 +173,11 @@ context 'when service is called multiple times' do it 'creates a pipeline once' do expect do + first_pipeline = service.execute(merge_request) + first_pipeline.payload.update!(status: :running) service.execute(merge_request) + + allow(merge_request).to receive(:find_diff_head_pipeline).and_call_original service.execute(merge_request) end.to change { Ci::Pipeline.count }.by(1) end @@ -266,12 +270,37 @@ expect { response }.not_to change { Ci::Pipeline.count } expect(response).to be_error - expect(response.message).to eq('Cannot create a pipeline for this merge request.') + expect(response.message).to eq('Cannot create a pipeline for this merge request: no commits to build.') expect(response.payload).to be_nil failed_creation = ::Ci::PipelineCreation::Requests.hget(request) expect(failed_creation['status']).to eq(::Ci::PipelineCreation::Requests::FAILED) - expect(failed_creation['error']).to eq('Cannot create a pipeline for this merge request.') + expect(failed_creation['error']).to eq('Cannot create a pipeline for this merge request: no commits to build.') + end + end + + context 'when duplicate pipeline is still in progress' do + let(:request) { ::Ci::PipelineCreation::Requests.start_for_merge_request(merge_request) } + let(:params) { { pipeline_creation_request: request } } + + before do + existing_pipeline = create(:ci_pipeline, merge_requests_as_head_pipeline: [merge_request]) + allow(existing_pipeline).to receive_messages(merge_request?: true, running_or_pending?: true) + allow(merge_request).to receive_messages(has_no_commits?: false, find_diff_head_pipeline: existing_pipeline) + end + + it 'calls cannot_create_pipeline_error with retriable flag' do + response = service.execute(merge_request) + + expect(response).to be_error + expect(response.message).to include('duplicate pipeline still in progress') + end + + it 'keeps pipeline creation in progress for retry' do + service.execute(merge_request) + + request_data = ::Ci::PipelineCreation::Requests.hget(params[:pipeline_creation_request]) + expect(request_data['status']).to eq(::Ci::PipelineCreation::Requests::IN_PROGRESS) end end @@ -394,4 +423,65 @@ it { is_expected.to be_falsey } end end + + describe '#cannot_create_pipeline_error' do + let(:service) { described_class.new(project: project, current_user: user, params: params) } + let(:params) { { pipeline_creation_request: { 'key' => '123', 'id' => '456' } } } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + + context 'when called with retriable_error: false (default)' do + it 'returns an error response' do + response = service.send(:cannot_create_pipeline_error, 'test reason') + + expect(response).to be_error + expect(response.message).to eq('Cannot create a pipeline for this merge request: test reason.') + end + + it 'marks the pipeline creation request as failed' do + service.send(:cannot_create_pipeline_error, 'test reason') + + failed_request = ::Ci::PipelineCreation::Requests.hget(params[:pipeline_creation_request]) + expect(failed_request['status']).to eq(::Ci::PipelineCreation::Requests::FAILED) + expect(failed_request['error']).to eq('Cannot create a pipeline for this merge request: test reason.') + end + + it 'includes the reason in the error message' do + response = service.send(:cannot_create_pipeline_error, 'custom failure reason') + + expect(response.message).to include('custom failure reason') + end + end + end + + describe '#cannot_create_pipeline_error with retriable_error flag' do + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let(:request) { ::Ci::PipelineCreation::Requests.start_for_merge_request(merge_request) } + let(:service) { described_class.new(project: project, current_user: user, params: params) } + let(:params) { { pipeline_creation_request: request } } + + context 'when called with retriable: true' do + it 'returns an error response' do + response = service.send(:cannot_create_pipeline_error, 'test reason', retriable: true) + + expect(response).to be_error + expect(response.message).to eq('Cannot create a pipeline for this merge request: test reason.') + end + + it 'does NOT mark the pipeline creation request as failed' do + service.send(:cannot_create_pipeline_error, 'test reason', retriable: true) + + # The request should still be in progress, not marked as failed + request_data = ::Ci::PipelineCreation::Requests.hget(params[:pipeline_creation_request]) + expect(request_data['status']).to eq(::Ci::PipelineCreation::Requests::IN_PROGRESS) + expect(request_data['error']).to be_nil + end + + it 'allows the worker to retry the job' do + response = service.send(:cannot_create_pipeline_error, 'duplicate pipeline still in progress', retriable: true) + + expect(response).to be_error + expect(response.reason).to eq(:retriable_error) + end + end + end end -- GitLab From 7ef339ceee1b069a698f01f1dc605c4a71fe97bc Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Tue, 9 Dec 2025 11:57:16 +0000 Subject: [PATCH 09/17] Fixes duplicate pipeline tests --- .../ee/merge_requests/create_pipeline_service_spec.rb | 6 +++++- .../services/merge_requests/create_pipeline_service_spec.rb | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb index fdf4894d440ebd..be1d9b08f9ed4d 100644 --- a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb +++ b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb @@ -153,7 +153,11 @@ context 'when the CreateService is retried' do it 'does not create a merge request pipeline twice' do expect do - 2.times { service.execute(merge_request) } + first_pipeline = service.execute(merge_request) + first_pipeline.payload.update!(status: :running) + + allow(merge_request).to receive(:find_diff_head_pipeline).and_call_original + service.execute(merge_request) end.to change { Ci::Pipeline.count }.by(1) end end diff --git a/spec/services/merge_requests/create_pipeline_service_spec.rb b/spec/services/merge_requests/create_pipeline_service_spec.rb index 804bc9e5c54df3..809ec7acfc28df 100644 --- a/spec/services/merge_requests/create_pipeline_service_spec.rb +++ b/spec/services/merge_requests/create_pipeline_service_spec.rb @@ -175,7 +175,6 @@ expect do first_pipeline = service.execute(merge_request) first_pipeline.payload.update!(status: :running) - service.execute(merge_request) allow(merge_request).to receive(:find_diff_head_pipeline).and_call_original service.execute(merge_request) @@ -187,7 +186,10 @@ it 'creates pipelines multiple times' do expect do - service.execute(merge_request) + first_pipeline = service.execute(merge_request) + first_pipeline.payload.update!(status: :running) + + allow(merge_request).to receive(:find_diff_head_pipeline).and_call_original service.execute(merge_request) end.to change { Ci::Pipeline.count }.by(2) end -- GitLab From b7ad3a21f297bcfcb9b9d550da0130ab8afd6441 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Wed, 10 Dec 2025 02:21:19 +0000 Subject: [PATCH 10/17] Fix #allowed tests --- .../services/ee/merge_requests/create_pipeline_service.rb | 6 +++++- .../ee/merge_requests/create_pipeline_service_spec.rb | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index a3ab046d758361..61654c074cbace 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -15,7 +15,11 @@ def execute(merge_request) end def allowed?(merge_request) - (can_create_merged_result_pipeline_for?(merge_request) && user_can_run_pipeline?(merge_request)) || super + ( + merge_request.project.merge_pipelines_enabled? && + can_create_merged_result_pipeline_for?(merge_request) && + user_can_run_pipeline?(merge_request) + ) || super end private diff --git a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb index be1d9b08f9ed4d..d38c1b2031688a 100644 --- a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb +++ b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb @@ -218,6 +218,7 @@ subject(:allowed) { service.allowed?(merge_request) } + let(:merge_pipelines_enabled) { true } let(:user_without_permissions) { create(:user) } where(:merged_result_pipeline_supported, :detached_mr_pipeline_supported, :user_can_run_pipeline, :result) do @@ -244,6 +245,9 @@ .and_return(detached_mr_pipeline_supported) project.add_developer(user) if user_can_run_pipeline + + source_project.merge_pipelines_enabled = merge_pipelines_enabled + stub_licensed_features(merge_pipelines: true) end it 'matches the expected result' do -- GitLab From a608f9a069045579fe503fb081b6aae29888d947 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Wed, 10 Dec 2025 14:52:43 +0000 Subject: [PATCH 11/17] Fix failing invalid gitlab yml file test --- app/services/merge_requests/create_pipeline_service.rb | 2 +- ee/app/services/ee/merge_requests/create_pipeline_service.rb | 2 +- .../ee/merge_requests/create_pipeline_service_spec.rb | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index c7709968f9a595..c5560005a1cf66 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -8,7 +8,7 @@ def execute(merge_request) unless allow_duplicate existing_pipeline = merge_request.find_diff_head_pipeline - if existing_pipeline&.merge_request? && existing_pipeline.active? + if existing_pipeline&.merge_request? return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) end end diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 61654c074cbace..6bcc58ee43303d 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -36,7 +36,7 @@ def create_merged_result_pipeline_for(merge_request) unless allow_duplicate existing_pipeline = merge_request.find_diff_head_pipeline - if existing_pipeline&.merge_request? && existing_pipeline.active? + if existing_pipeline&.merge_request? return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) end end diff --git a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb index d38c1b2031688a..0225890a983acd 100644 --- a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb +++ b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb @@ -208,7 +208,9 @@ it 'responds with error', :aggregate_failures do expect(subject).to be_error - expect(subject.message).to eq('Cannot create a pipeline for this merge request.') + expect(subject.message).to eq( + 'Cannot create a pipeline for this merge request: duplicate pipeline still in progress.' + ) end end end -- GitLab From 744994680d4c74205a10275b39c58f78408be6d1 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Wed, 10 Dec 2025 15:34:11 +0000 Subject: [PATCH 12/17] Fix invalid gitlab-ci.yml spec by checking for failed pipelines --- app/services/merge_requests/create_pipeline_service.rb | 6 +++++- .../ee/merge_requests/create_pipeline_service_spec.rb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index c5560005a1cf66..9041ac29cd73be 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -9,7 +9,11 @@ def execute(merge_request) existing_pipeline = merge_request.find_diff_head_pipeline if existing_pipeline&.merge_request? - return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) + if existing_pipeline.running? || existing_pipeline.pending? + return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) + end + + return cannot_create_pipeline_error('duplicate pipeline') if existing_pipeline.failed? end end diff --git a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb index 0225890a983acd..9387e7c92fe52c 100644 --- a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb +++ b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb @@ -209,7 +209,7 @@ it 'responds with error', :aggregate_failures do expect(subject).to be_error expect(subject.message).to eq( - 'Cannot create a pipeline for this merge request: duplicate pipeline still in progress.' + 'Cannot create a pipeline for this merge request: duplicate pipeline.' ) end end -- GitLab From 4746c8454d9bc90eee90bd71b317446a3685aa2c Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Thu, 11 Dec 2025 18:52:44 +0000 Subject: [PATCH 13/17] Add failed pipeline check --- .../ee/merge_requests/create_pipeline_service.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 6bcc58ee43303d..793f1c673f8bdc 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -17,8 +17,8 @@ def execute(merge_request) def allowed?(merge_request) ( merge_request.project.merge_pipelines_enabled? && - can_create_merged_result_pipeline_for?(merge_request) && - user_can_run_pipeline?(merge_request) + can_create_merged_result_pipeline_for?(merge_request) && + user_can_run_pipeline?(merge_request) ) || super end @@ -37,7 +37,11 @@ def create_merged_result_pipeline_for(merge_request) existing_pipeline = merge_request.find_diff_head_pipeline if existing_pipeline&.merge_request? - return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) + if existing_pipeline.running? || existing_pipeline.pending? + return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) + end + + return cannot_create_pipeline_error('duplicate pipeline') if existing_pipeline.failed? end end -- GitLab From 024c3673dc997a34ac81c232094973ab1c1b7296 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Mon, 15 Dec 2025 13:13:06 +0000 Subject: [PATCH 14/17] Create a common helper function for duplicate check --- .../merge_requests/create_pipeline_service.rb | 29 ++++++++++++------- .../merge_requests/create_pipeline_worker.rb | 7 +++-- .../merge_requests/create_pipeline_service.rb | 13 ++------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index 9041ac29cd73be..e3651d765582da 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -5,17 +5,8 @@ class CreatePipelineService < MergeRequests::BaseService def execute(merge_request) return cannot_create_pipeline_error('no commits to build') if merge_request.has_no_commits? - unless allow_duplicate - existing_pipeline = merge_request.find_diff_head_pipeline - - if existing_pipeline&.merge_request? - if existing_pipeline.running? || existing_pipeline.pending? - return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) - end - - return cannot_create_pipeline_error('duplicate pipeline') if existing_pipeline.failed? - end - end + duplicate_error = check_duplicate_pipeline(merge_request) + return duplicate_error if duplicate_error create_merge_request_pipeline(merge_request) end @@ -109,6 +100,22 @@ def can_update_source_branch_in_target_project?(merge_request) .can_update_branch?(merge_request.source_branch) end + def check_duplicate_pipeline(merge_request) + return if allow_duplicate + + existing_pipeline = merge_request.find_diff_head_pipeline + + if existing_pipeline&.merge_request? + if existing_pipeline.running? || existing_pipeline.pending? + return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) + end + + return cannot_create_pipeline_error('duplicate pipeline') if existing_pipeline.failed? + end + + nil + end + def cannot_create_pipeline_error(reason, retriable: false) message = "Cannot create a pipeline for this merge request: #{reason}." diff --git a/app/workers/merge_requests/create_pipeline_worker.rb b/app/workers/merge_requests/create_pipeline_worker.rb index 0f05128df1f73f..41abce49ed26e1 100644 --- a/app/workers/merge_requests/create_pipeline_worker.rb +++ b/app/workers/merge_requests/create_pipeline_worker.rb @@ -22,12 +22,13 @@ class CreatePipelineWorker pipeline_creation_request = job['args'][3]&.dig('pipeline_creation_request') next unless pipeline_creation_request - merge_request = ::Ci::PipelineCreation::Requests.merge_request_from_key(pipeline_creation_request&.dig('key')) - error_message = 'Cannot create a pipeline for this merge request after multiple retries.' ::Ci::PipelineCreation::Requests.failed(pipeline_creation_request, error_message) - GraphqlTriggers.ci_pipeline_creation_requests_updated(merge_request) + + merge_request = ::Ci::PipelineCreation::Requests.merge_request_from_key(pipeline_creation_request&.dig('key')) + + GraphqlTriggers.ci_pipeline_creation_requests_updated(merge_request) if merge_request end def perform(project_id, user_id, merge_request_id, params = {}) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 793f1c673f8bdc..80409a69308217 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -33,17 +33,8 @@ def create_merged_result_pipeline_for(merge_request) return cannot_create_pipeline_error('merged result pipeline not allowed') end - unless allow_duplicate - existing_pipeline = merge_request.find_diff_head_pipeline - - if existing_pipeline&.merge_request? - if existing_pipeline.running? || existing_pipeline.pending? - return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) - end - - return cannot_create_pipeline_error('duplicate pipeline') if existing_pipeline.failed? - end - end + duplicate_error = check_duplicate_pipeline(merge_request) + return duplicate_error if duplicate_error result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute(recheck: true) -- GitLab From 9a37a422107e5aedba19354f29ee0e33d4aba681 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Mon, 15 Dec 2025 17:17:07 +0000 Subject: [PATCH 15/17] Directly return service error when merge requests are not enabled --- ee/app/services/ee/merge_requests/create_pipeline_service.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ee/app/services/ee/merge_requests/create_pipeline_service.rb b/ee/app/services/ee/merge_requests/create_pipeline_service.rb index 80409a69308217..e963db3bc3e0e2 100644 --- a/ee/app/services/ee/merge_requests/create_pipeline_service.rb +++ b/ee/app/services/ee/merge_requests/create_pipeline_service.rb @@ -26,7 +26,9 @@ def allowed?(merge_request) def create_merged_result_pipeline_for(merge_request) unless merge_request.project.merge_pipelines_enabled? - return cannot_create_pipeline_error('merge pipeline not enabled', retriable: true) + return ServiceResponse.error( + message: 'Cannot create a pipeline for this merge request: merge pipelines not enabled' + ) end unless can_create_merged_result_pipeline_for?(merge_request) -- GitLab From 4f1d92f928f46f2d6606571e7e13adf241c1725d Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Tue, 16 Dec 2025 12:07:47 +0000 Subject: [PATCH 16/17] Ensure that the duplicate pipeline check only runs for the same commit --- app/services/merge_requests/create_pipeline_service.rb | 4 ++-- .../ee/merge_requests/create_pipeline_service_spec.rb | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index e3651d765582da..d067bc4e6ef4b0 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -105,12 +105,12 @@ def check_duplicate_pipeline(merge_request) existing_pipeline = merge_request.find_diff_head_pipeline - if existing_pipeline&.merge_request? + if existing_pipeline&.merge_request? && existing_pipeline&.sha == merge_request.diff_head_sha if existing_pipeline.running? || existing_pipeline.pending? return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) end - return cannot_create_pipeline_error('duplicate pipeline') if existing_pipeline.failed? + return cannot_create_pipeline_error('duplicate pipeline') end nil diff --git a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb index 9387e7c92fe52c..b5f97a9d97ea28 100644 --- a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb +++ b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb @@ -152,11 +152,9 @@ context 'when the CreateService is retried' do it 'does not create a merge request pipeline twice' do + service.execute(merge_request) + merge_request.reload expect do - first_pipeline = service.execute(merge_request) - first_pipeline.payload.update!(status: :running) - - allow(merge_request).to receive(:find_diff_head_pipeline).and_call_original service.execute(merge_request) end.to change { Ci::Pipeline.count }.by(1) end -- GitLab From 220ea44b1f2311c3b57309ae33e0fc6372c2d7f5 Mon Sep 17 00:00:00 2001 From: Sahil Sharma Date: Tue, 16 Dec 2025 15:51:49 +0000 Subject: [PATCH 17/17] Using pipeline `merge_request_diff_sha` instead of `sha` for comparing same commit --- app/models/ci/pipeline.rb | 12 ++++++------ .../merge_requests/create_pipeline_service.rb | 2 +- .../merge_requests/create_pipeline_service_spec.rb | 4 +--- .../merge_requests/create_pipeline_service_spec.rb | 6 +++++- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 1fb1cf4132c30a..7a90dfee8e1e93 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -1656,12 +1656,6 @@ def cancel_async_on_job_failure end end - private - - def add_message(severity, content) - messages.build(severity: severity, content: content, project_id: project_id) - end - def merge_request_diff_sha return unless merge_request? @@ -1672,6 +1666,12 @@ def merge_request_diff_sha end end + private + + def add_message(severity, content) + messages.build(severity: severity, content: content, project_id: project_id) + end + def push_details strong_memoize(:push_details) do Gitlab::Git::Push.new(project, before_sha, sha, git_ref) diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb index d067bc4e6ef4b0..d2fa8b9030f9a8 100644 --- a/app/services/merge_requests/create_pipeline_service.rb +++ b/app/services/merge_requests/create_pipeline_service.rb @@ -105,7 +105,7 @@ def check_duplicate_pipeline(merge_request) existing_pipeline = merge_request.find_diff_head_pipeline - if existing_pipeline&.merge_request? && existing_pipeline&.sha == merge_request.diff_head_sha + if existing_pipeline&.merge_request? && existing_pipeline&.merge_request_diff_sha == merge_request.diff_head_sha if existing_pipeline.running? || existing_pipeline.pending? return cannot_create_pipeline_error('duplicate pipeline still in progress', retriable: true) end diff --git a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb index b5f97a9d97ea28..bd5a82c6ed9f6b 100644 --- a/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb +++ b/ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb @@ -152,10 +152,8 @@ context 'when the CreateService is retried' do it 'does not create a merge request pipeline twice' do - service.execute(merge_request) - merge_request.reload expect do - service.execute(merge_request) + 2.times { service.execute(merge_request) } end.to change { Ci::Pipeline.count }.by(1) end end diff --git a/spec/services/merge_requests/create_pipeline_service_spec.rb b/spec/services/merge_requests/create_pipeline_service_spec.rb index 809ec7acfc28df..485ba9987a75e1 100644 --- a/spec/services/merge_requests/create_pipeline_service_spec.rb +++ b/spec/services/merge_requests/create_pipeline_service_spec.rb @@ -287,7 +287,11 @@ before do existing_pipeline = create(:ci_pipeline, merge_requests_as_head_pipeline: [merge_request]) - allow(existing_pipeline).to receive_messages(merge_request?: true, running_or_pending?: true) + allow(existing_pipeline).to receive_messages( + merge_request?: true, + running_or_pending?: true, + merge_request_diff_sha: merge_request.diff_head_sha + ) allow(merge_request).to receive_messages(has_no_commits?: false, find_diff_head_pipeline: existing_pipeline) end -- GitLab