Commit f586dc07 authored by Felipe Artur's avatar Felipe Artur

Check if head_pipeline is correct before merging

parent 8f78ba55
...@@ -283,15 +283,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -283,15 +283,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_pipeline_succeeds].present? if params[:merge_when_pipeline_succeeds].present?
return :failed unless @merge_request.head_pipeline return :failed unless @merge_request.current_head_pipeline
if @merge_request.head_pipeline.active? if @merge_request.current_head_pipeline.active?
::MergeRequests::MergeWhenPipelineSucceedsService ::MergeRequests::MergeWhenPipelineSucceedsService
.new(@project, current_user, merge_params) .new(@project, current_user, merge_params)
.execute(@merge_request) .execute(@merge_request)
:merge_when_pipeline_succeeds :merge_when_pipeline_succeeds
elsif @merge_request.head_pipeline.success? elsif @merge_request.current_head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while # This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time # the tests finish at about the same time
@merge_request.merge_async(current_user.id, params) @merge_request.merge_async(current_user.id, params)
......
...@@ -145,8 +145,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -145,8 +145,11 @@ class MergeRequest < ActiveRecord::Base
'!' '!'
end end
def head_pipeline # Use this method whenever you need to make sure the head_pipeline is synced with the
super&.sha == diff_head_sha ? super : nil # branch head commit, for example checking if a merge request can be merged.
# For more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/40004
def current_head_pipeline
head_pipeline&.sha == diff_head_sha ? head_pipeline : nil
end end
# Pattern used to extract `!123` merge request references from text # Pattern used to extract `!123` merge request references from text
...@@ -826,8 +829,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -826,8 +829,9 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state? def mergeable_ci_state?
return true unless project.only_allow_merge_if_pipeline_succeeds? return true unless project.only_allow_merge_if_pipeline_succeeds?
return true unless head_pipeline
!head_pipeline || head_pipeline.success? || head_pipeline.skipped? current_head_pipeline&.success? || current_head_pipeline&.skipped?
end end
def environments_for(current_user) def environments_for(current_user)
...@@ -1001,7 +1005,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -1001,7 +1005,7 @@ class MergeRequest < ActiveRecord::Base
return true if autocomplete_precheck return true if autocomplete_precheck
return false unless mergeable?(skip_ci_check: true) return false unless mergeable?(skip_ci_check: true)
return false if head_pipeline && !(head_pipeline.success? || head_pipeline.active?) return false if current_head_pipeline && !(current_head_pipeline.success? || current_head_pipeline.active?)
return false if last_diff_sha != diff_head_sha return false if last_diff_sha != diff_head_sha
true true
......
...@@ -324,12 +324,12 @@ describe Projects::MergeRequestsController do ...@@ -324,12 +324,12 @@ describe Projects::MergeRequestsController do
end end
context 'when the pipeline succeeds is passed' do context 'when the pipeline succeeds is passed' do
def merge_when_pipeline_succeeds let!(:head_pipeline) do
post :merge, base_params.merge(sha: merge_request.diff_head_sha, merge_when_pipeline_succeeds: '1') create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch, head_pipeline_of: merge_request)
end end
before do def merge_when_pipeline_succeeds
create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch, head_pipeline_of: merge_request) post :merge, base_params.merge(sha: merge_request.diff_head_sha, merge_when_pipeline_succeeds: '1')
end end
it 'returns :merge_when_pipeline_succeeds' do it 'returns :merge_when_pipeline_succeeds' do
...@@ -354,6 +354,18 @@ describe Projects::MergeRequestsController do ...@@ -354,6 +354,18 @@ describe Projects::MergeRequestsController do
project.update_column(:only_allow_merge_if_pipeline_succeeds, true) project.update_column(:only_allow_merge_if_pipeline_succeeds, true)
end end
context 'and head pipeline is not the current one' do
before do
head_pipeline.update(sha: 'not_current_sha')
end
it 'returns :failed' do
merge_when_pipeline_succeeds
expect(json_response).to eq('status' => 'failed')
end
end
it 'returns :merge_when_pipeline_succeeds' do it 'returns :merge_when_pipeline_succeeds' do
merge_when_pipeline_succeeds merge_when_pipeline_succeeds
......
...@@ -827,29 +827,34 @@ describe MergeRequest do ...@@ -827,29 +827,34 @@ describe MergeRequest do
end end
end end
describe '#head_pipeline' do context 'head pipeline' do
before do before do
allow(subject).to receive(:diff_head_sha).and_return('lastsha') allow(subject).to receive(:diff_head_sha).and_return('lastsha')
end end
describe '#head_pipeline' do
it 'returns nil for MR without head_pipeline_id' do it 'returns nil for MR without head_pipeline_id' do
subject.update_attribute(:head_pipeline_id, nil) subject.update_attribute(:head_pipeline_id, nil)
expect(subject.head_pipeline).to be_nil expect(subject.head_pipeline).to be_nil
end end
end
describe '#current_head_pipeline' do
it 'returns nil for MR with old pipeline' do it 'returns nil for MR with old pipeline' do
pipeline = create(:ci_empty_pipeline, sha: 'notlatestsha') pipeline = create(:ci_empty_pipeline, sha: 'notlatestsha')
subject.update_attribute(:head_pipeline_id, pipeline.id) subject.update_attribute(:head_pipeline_id, pipeline.id)
expect(subject.head_pipeline).to be_nil expect(subject.current_head_pipeline).to be_nil
end end
it 'returns the pipeline for MR with recent pipeline' do it 'returns the pipeline for MR with recent pipeline' do
pipeline = create(:ci_empty_pipeline, sha: 'lastsha') pipeline = create(:ci_empty_pipeline, sha: 'lastsha')
subject.update_attribute(:head_pipeline_id, pipeline.id) subject.update_attribute(:head_pipeline_id, pipeline.id)
expect(subject.head_pipeline).to eq(pipeline) expect(subject.current_head_pipeline).to eq(subject.head_pipeline)
expect(subject.current_head_pipeline).to eq(pipeline)
end
end end
end end
...@@ -1187,7 +1192,7 @@ describe MergeRequest do ...@@ -1187,7 +1192,7 @@ describe MergeRequest do
context 'when it is only allowed to merge when build is green' do context 'when it is only allowed to merge when build is green' do
context 'and a failed pipeline is associated' do context 'and a failed pipeline is associated' do
before do before do
pipeline.update(status: 'failed') pipeline.update(status: 'failed', sha: subject.diff_head_sha)
allow(subject).to receive(:head_pipeline) { pipeline } allow(subject).to receive(:head_pipeline) { pipeline }
end end
...@@ -1196,7 +1201,7 @@ describe MergeRequest do ...@@ -1196,7 +1201,7 @@ describe MergeRequest do
context 'and a successful pipeline is associated' do context 'and a successful pipeline is associated' do
before do before do
pipeline.update(status: 'success') pipeline.update(status: 'success', sha: subject.diff_head_sha)
allow(subject).to receive(:head_pipeline) { pipeline } allow(subject).to receive(:head_pipeline) { pipeline }
end end
...@@ -1205,7 +1210,7 @@ describe MergeRequest do ...@@ -1205,7 +1210,7 @@ describe MergeRequest do
context 'and a skipped pipeline is associated' do context 'and a skipped pipeline is associated' do
before do before do
pipeline.update(status: 'skipped') pipeline.update(status: 'skipped', sha: subject.diff_head_sha)
allow(subject).to receive(:head_pipeline) { pipeline } allow(subject).to receive(:head_pipeline) { pipeline }
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment