From c2dde81205965f6e91de1bd97b5a2d37fe90ac2b Mon Sep 17 00:00:00 2001
From: Dylan Griffith <dyl.griffith@gmail.com>
Date: Thu, 2 Dec 2021 17:03:18 +1100
Subject: [PATCH] Ensure ci_pipelines.iid set before transaction in commit
 status API

Prior to this we were using `create!` which triggered the
`ensure_project_iid!` in a callback. This causes a
`CrossDatabaseModificationAcrossUnsupportedTablesError`. To avoid this
we will create the `iid` before saving. This is consistent with all
other places we create pipelines.

This change still doesn't allow us to remove
`spec/requests/api/commit_statuses_spec.rb` from the allowlist as there
are other calls to `project.ci_pipelines.create!` in this file.

We need a more holistic solution for that which is being discussed in
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75316 .
---
 lib/api/commit_statuses.rb                | 5 ++++-
 spec/requests/api/commit_statuses_spec.rb | 4 +++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 1785362656e4..c89abf72e2de 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -78,13 +78,16 @@ class CommitStatuses < ::API::Base
 
         name = params[:name] || params[:context] || 'default'
 
-        pipeline ||= user_project.ci_pipelines.create!(
+        pipeline ||= user_project.ci_pipelines.build(
           source: :external,
           sha: commit.sha,
           ref: ref,
           user: current_user,
           protected: user_project.protected_for?(ref))
 
+        pipeline.ensure_project_iid!
+        pipeline.save!
+
         authorize! :update_pipeline, pipeline
 
         status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 47bc3eb74a64..155d9a36e176 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -131,7 +131,7 @@ def create_status(commit, opts = {})
         %w[pending running success failed canceled].each do |status|
           context "for #{status}" do
             context 'when pipeline for sha does not exists' do
-              it 'creates commit status' do
+              it 'creates commit status and sets pipeline iid' do
                 post api(post_url, developer), params: { state: status }
 
                 expect(response).to have_gitlab_http_status(:created)
@@ -145,6 +145,8 @@ def create_status(commit, opts = {})
                 if status == 'failed'
                   expect(CommitStatus.find(json_response['id'])).to be_api_failure
                 end
+
+                expect(::Ci::Pipeline.last.iid).not_to be_nil
               end
             end
           end
-- 
GitLab