Commit 39c342ba authored by Kamil Trzciński's avatar Kamil Trzciński

Retarget MR only when source branch is removed

This ensures that we have an automation
in erroring condition
parent 296cdaf8
...@@ -35,7 +35,8 @@ module MergeRequests ...@@ -35,7 +35,8 @@ module MergeRequests
return unless Feature.enabled?(:retarget_merge_requests, merge_request.target_project) return unless Feature.enabled?(:retarget_merge_requests, merge_request.target_project)
# we can only retarget MRs that are targeting the same project # we can only retarget MRs that are targeting the same project
return unless merge_request.for_same_project? # and have a remove source branch set
return unless merge_request.for_same_project? && merge_request.remove_source_branch?
# find another merge requests that # find another merge requests that
# - as a target have a current source project and branch # - as a target have a current source project and branch
...@@ -51,7 +52,9 @@ module MergeRequests ...@@ -51,7 +52,9 @@ module MergeRequests
next unless can?(current_user, :update_merge_request, other_merge_request.source_project) next unless can?(current_user, :update_merge_request, other_merge_request.source_project)
::MergeRequests::UpdateService ::MergeRequests::UpdateService
.new(other_merge_request.source_project, current_user, target_branch: merge_request.target_branch) .new(other_merge_request.source_project, current_user,
target_branch: merge_request.target_branch,
target_branch_was_deleted: true)
.execute(other_merge_request) .execute(other_merge_request)
end end
end end
......
...@@ -4,6 +4,12 @@ module MergeRequests ...@@ -4,6 +4,12 @@ module MergeRequests
class UpdateService < MergeRequests::BaseService class UpdateService < MergeRequests::BaseService
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
def initialize(project, user = nil, params = {})
super
@target_branch_was_deleted = @params.delete(:target_branch_was_deleted)
end
def execute(merge_request) def execute(merge_request)
# We don't allow change of source/target projects and source branch # We don't allow change of source/target projects and source branch
# after merge request was created # after merge request was created
...@@ -36,7 +42,9 @@ module MergeRequests ...@@ -36,7 +42,9 @@ module MergeRequests
end end
if merge_request.previous_changes.include?('target_branch') if merge_request.previous_changes.include?('target_branch')
create_branch_change_note(merge_request, 'target', create_branch_change_note(merge_request,
'target',
target_branch_was_deleted ? 'delete' : 'update',
merge_request.previous_changes['target_branch'].first, merge_request.previous_changes['target_branch'].first,
merge_request.target_branch) merge_request.target_branch)
...@@ -130,6 +138,8 @@ module MergeRequests ...@@ -130,6 +138,8 @@ module MergeRequests
private private
attr_reader :target_branch_was_deleted
def handle_milestone_change(merge_request) def handle_milestone_change(merge_request)
return if skip_milestone_email return if skip_milestone_email
...@@ -162,9 +172,9 @@ module MergeRequests ...@@ -162,9 +172,9 @@ module MergeRequests
merge_request_activity_counter.track_users_review_requested(users: new_reviewers) merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
end end
def create_branch_change_note(issuable, branch_type, old_branch, new_branch) def create_branch_change_note(issuable, branch_type, event_type, old_branch, new_branch)
SystemNoteService.change_branch( SystemNoteService.change_branch(
issuable, issuable.project, current_user, branch_type, issuable, issuable.project, current_user, branch_type, event_type,
old_branch, new_branch) old_branch, new_branch)
end end
......
...@@ -168,16 +168,19 @@ module SystemNoteService ...@@ -168,16 +168,19 @@ module SystemNoteService
# project - Project owning noteable # project - Project owning noteable
# author - User performing the change # author - User performing the change
# branch_type - 'source' or 'target' # branch_type - 'source' or 'target'
# event_type - the source of event: 'update' or 'delete'
# old_branch - old branch name # old_branch - old branch name
# new_branch - new branch name # new_branch - new branch name
# #
# Example Note text: # Example Note text is based on event_type:
# #
# "changed target branch from `Old` to `New`" # update: "changed target branch from `Old` to `New`"
# delete: "changed automatically target branch to `New` because `Old` was deleted"
# #
# Returns the created Note object # Returns the created Note object
def change_branch(noteable, project, author, branch_type, old_branch, new_branch) def change_branch(noteable, project, author, branch_type, event_type, old_branch, new_branch)
::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).change_branch(branch_type, old_branch, new_branch) ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author)
.change_branch(branch_type, event_type, old_branch, new_branch)
end end
# Called when a branch in Noteable is added or deleted # Called when a branch in Noteable is added or deleted
......
...@@ -83,16 +83,26 @@ module SystemNotes ...@@ -83,16 +83,26 @@ module SystemNotes
# Called when a branch in Noteable is changed # Called when a branch in Noteable is changed
# #
# branch_type - 'source' or 'target' # branch_type - 'source' or 'target'
# event_type - the source of event: 'update' or 'delete'
# old_branch - old branch name # old_branch - old branch name
# new_branch - new branch name # new_branch - new branch name
# Example Note text is based on event_type:
# #
# Example Note text: # update: "changed target branch from `Old` to `New`"
# # delete: "changed automatically target branch to `New` because `Old` was deleted"
# "changed target branch from `Old` to `New`"
# #
# Returns the created Note object # Returns the created Note object
def change_branch(branch_type, old_branch, new_branch) def change_branch(branch_type, event_type, old_branch, new_branch)
body = "changed #{branch_type} branch from `#{old_branch}` to `#{new_branch}`" body =
case event_type.to_s
when 'delete'
"changed automatically #{branch_type} branch to `#{new_branch}` because `#{old_branch}` was deleted"
when 'update'
"changed #{branch_type} branch from `#{old_branch}` to `#{new_branch}`"
else
raise ArgumentError, "invalid value for event_type: #{event_type}"
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'branch')) create_note(NoteSummary.new(noteable, project, author, body, action: 'branch'))
end end
......
...@@ -208,7 +208,7 @@ open. Merge requests are often chained in this manner, with one merge request ...@@ -208,7 +208,7 @@ open. Merge requests are often chained in this manner, with one merge request
depending on another: depending on another:
- **Merge request 1**: merge `feature-alpha` into `master`. - **Merge request 1**: merge `feature-alpha` into `master`.
- **Merge Request 2**: merge `feature-beta` into `feature-alpha`. - **Merge request 2**: merge `feature-beta` into `feature-alpha`.
These merge requests are usually handled in one of these ways: These merge requests are usually handled in one of these ways:
......
...@@ -50,7 +50,7 @@ module EE ...@@ -50,7 +50,7 @@ module EE
end end
override :create_branch_change_note override :create_branch_change_note
def create_branch_change_note(merge_request, branch_type, old_branch, new_branch) def create_branch_change_note(merge_request, branch_type, event_type, old_branch, new_branch)
super super
reset_approvals(merge_request) reset_approvals(merge_request)
......
...@@ -132,6 +132,12 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -132,6 +132,12 @@ RSpec.describe MergeRequests::PostMergeService do
end end
context 'for a merge request chain' do context 'for a merge request chain' do
before do
::MergeRequests::UpdateService
.new(project, user, force_remove_source_branch: '1')
.execute(merge_request)
end
context 'when there is another MR' do context 'when there is another MR' do
let!(:another_merge_request) do let!(:another_merge_request) do
create(:merge_request, create(:merge_request,
...@@ -142,11 +148,19 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -142,11 +148,19 @@ RSpec.describe MergeRequests::PostMergeService do
) )
end end
shared_examples 'does not retarget merge request' do
it 'another merge request is unchanged' do
expect { subject }.not_to change { another_merge_request.reload.target_branch }
.from(merge_request.source_branch)
end
end
shared_examples 'retargets merge request' do shared_examples 'retargets merge request' do
it 'another merge request is retargeted' do it 'another merge request is retargeted' do
expect(SystemNoteService) expect(SystemNoteService)
.to receive(:change_branch).once .to receive(:change_branch).once
.with(another_merge_request, another_merge_request.project, user, 'target', .with(another_merge_request, another_merge_request.project, user,
'target', 'delete',
merge_request.source_branch, merge_request.target_branch) merge_request.source_branch, merge_request.target_branch)
expect { subject }.to change { another_merge_request.reload.target_branch } expect { subject }.to change { another_merge_request.reload.target_branch }
...@@ -159,17 +173,17 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -159,17 +173,17 @@ RSpec.describe MergeRequests::PostMergeService do
stub_feature_flags(retarget_merge_requests: false) stub_feature_flags(retarget_merge_requests: false)
end end
it 'another merge request is unchanged' do include_examples 'does not retarget merge request'
expect { subject }.not_to change { another_merge_request.reload.target_branch }
.from(merge_request.source_branch)
end
end end
context 'when source branch is to be kept' do
before do
::MergeRequests::UpdateService
.new(project, user, force_remove_source_branch: false)
.execute(merge_request)
end end
shared_examples 'does not retarget merge request' do include_examples 'does not retarget merge request'
it 'another merge request is unchanged' do
expect { subject }.not_to change { another_merge_request.reload.target_branch }
.from(merge_request.source_branch)
end end
end end
......
...@@ -913,6 +913,33 @@ RSpec.describe MergeRequests::UpdateService, :mailer do ...@@ -913,6 +913,33 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
end end
end end
context 'updating `target_branch`' do
let(:merge_request) do
create(:merge_request,
source_project: project,
source_branch: 'mr-b',
target_branch: 'mr-a')
end
it 'updates to master' do
expect(SystemNoteService).to receive(:change_branch).with(
merge_request, project, user, 'target', 'update', 'mr-a', 'master'
)
expect { update_merge_request(target_branch: 'master') }
.to change { merge_request.reload.target_branch }.from('mr-a').to('master')
end
it 'updates to master because of branch deletion' do
expect(SystemNoteService).to receive(:change_branch).with(
merge_request, project, user, 'target', 'delete', 'mr-a', 'master'
)
expect { update_merge_request(target_branch: 'master', target_branch_was_deleted: true) }
.to change { merge_request.reload.target_branch }.from('mr-a').to('master')
end
end
it_behaves_like 'issuable record that supports quick actions' do it_behaves_like 'issuable record that supports quick actions' do
let(:existing_merge_request) { create(:merge_request, source_project: project) } let(:existing_merge_request) { create(:merge_request, source_project: project) }
let(:issuable) { described_class.new(project, user, params).execute(existing_merge_request) } let(:issuable) { described_class.new(project, user, params).execute(existing_merge_request) }
......
...@@ -213,15 +213,16 @@ RSpec.describe SystemNoteService do ...@@ -213,15 +213,16 @@ RSpec.describe SystemNoteService do
describe '.change_branch' do describe '.change_branch' do
it 'calls MergeRequestsService' do it 'calls MergeRequestsService' do
old_branch = double old_branch = double('old_branch')
new_branch = double new_branch = double('new_branch')
branch_type = double branch_type = double('branch_type')
event_type = double('event_type')
expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service| expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
expect(service).to receive(:change_branch).with(branch_type, old_branch, new_branch) expect(service).to receive(:change_branch).with(branch_type, event_type, old_branch, new_branch)
end end
described_class.change_branch(noteable, project, author, branch_type, old_branch, new_branch) described_class.change_branch(noteable, project, author, branch_type, event_type, old_branch, new_branch)
end end
end end
......
...@@ -167,20 +167,40 @@ RSpec.describe ::SystemNotes::MergeRequestsService do ...@@ -167,20 +167,40 @@ RSpec.describe ::SystemNotes::MergeRequestsService do
end end
describe '.change_branch' do describe '.change_branch' do
subject { service.change_branch('target', old_branch, new_branch) }
let(:old_branch) { 'old_branch'} let(:old_branch) { 'old_branch'}
let(:new_branch) { 'new_branch'} let(:new_branch) { 'new_branch'}
it_behaves_like 'a system note' do it_behaves_like 'a system note' do
let(:action) { 'branch' } let(:action) { 'branch' }
subject { service.change_branch('target', 'update', old_branch, new_branch) }
end end
context 'when target branch name changed' do context 'when target branch name changed' do
context 'on update' do
subject { service.change_branch('target', 'update', old_branch, new_branch) }
it 'sets the note text' do it 'sets the note text' do
expect(subject.note).to eq "changed target branch from `#{old_branch}` to `#{new_branch}`" expect(subject.note).to eq "changed target branch from `#{old_branch}` to `#{new_branch}`"
end end
end end
context 'on delete' do
subject { service.change_branch('target', 'delete', old_branch, new_branch) }
it 'sets the note text' do
expect(subject.note).to eq "changed automatically target branch to `#{new_branch}` because `#{old_branch}` was deleted"
end
end
context 'for invalid event_type' do
subject { service.change_branch('target', 'invalid', old_branch, new_branch) }
it 'raises exception' do
expect { subject }.to raise_error /invalid value for event_type/
end
end
end
end end
describe '.change_branch_presence' do describe '.change_branch_presence' do
......
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