Commit 48e461e2 authored by Shinya Maeda's avatar Shinya Maeda

Add To Merge Train When Pipeline Succeeds auto merge strategy

This commit adds the new auto merge strategy "Add to Merge Train When
Pipeline succeeds". It works similarily with MWPS, but it adds to
merge train instead of merging.
parent ab34a0d4
# frozen_string_literal: true
module AutoMerge
class AddToMergeTrainWhenPipelineSucceedsService < AutoMerge::BaseService
def execute(merge_request)
super do
SystemNoteService.add_to_merge_train_when_pipeline_succeeds(merge_request, project, current_user, merge_request.diff_head_commit)
end
end
def process(merge_request)
return unless merge_request.actual_head_pipeline&.success?
merge_train_service = AutoMerge::MergeTrainService.new(project, merge_request.merge_user)
##
# We are currently abusing `#cancel` method to cancel the auto merge when
# a system failure happens. We should split the interfaces into two
# for explicitly telling that the cancel action is not triggered by the merge user directly.
# https://gitlab.com/gitlab-org/gitlab-ee/issues/12134
return cancel(merge_request) unless merge_train_service.available_for?(merge_request)
merge_train_service.execute(merge_request)
end
def cancel(merge_request)
super(merge_request) do
SystemNoteService.cancel_add_to_merge_train_when_pipeline_succeeds(merge_request, project, current_user)
end
end
def available_for?(merge_request)
merge_request.project.merge_trains_enabled? &&
!merge_request.for_fork? &&
merge_request.actual_head_pipeline&.active? &&
merge_request.mergeable_state?(skip_ci_check: true)
end
end
end
...@@ -5,7 +5,8 @@ module EE ...@@ -5,7 +5,8 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
STRATEGY_MERGE_TRAIN = 'merge_train'.freeze STRATEGY_MERGE_TRAIN = 'merge_train'.freeze
EE_STRATEGIES = [STRATEGY_MERGE_TRAIN].freeze STRATEGY_ADD_TO_MERGE_TRAIN_WHEN_PIPELINE_SUCCEEDS = 'add_to_merge_train_when_pipeline_succeeds'.freeze
EE_STRATEGIES = [STRATEGY_MERGE_TRAIN, STRATEGY_ADD_TO_MERGE_TRAIN_WHEN_PIPELINE_SUCCEEDS].freeze
class_methods do class_methods do
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
......
...@@ -210,5 +210,19 @@ module EE ...@@ -210,5 +210,19 @@ module EE
create_note(NoteSummary.new(noteable, project, author, body, action: 'merge')) create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
end end
# Called when 'add to merge train when pipeline succeeds' is executed
def add_to_merge_train_when_pipeline_succeeds(noteable, project, author, last_commit)
body = "enabled automatic add to merge train when the pipeline for #{last_commit.to_reference(project)} succeeds"
create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
end
# Called when 'add to merge train when pipeline succeeds' is canceled
def cancel_add_to_merge_train_when_pipeline_succeeds(noteable, project, author)
body = 'cancelled automatic add to merge train'
create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
end
end end
end end
---
title: "[New Auto Merge Strategy] Add To Merge Train When Pipeline Succeeds"
merge_request: 13767
author:
type: added
...@@ -24,6 +24,12 @@ FactoryBot.modify do ...@@ -24,6 +24,12 @@ FactoryBot.modify do
end end
end end
trait :add_to_merge_train_when_pipeline_succeeds do
auto_merge_enabled true
auto_merge_strategy AutoMergeService::STRATEGY_ADD_TO_MERGE_TRAIN_WHEN_PIPELINE_SUCCEEDS
merge_user { author }
end
transient do transient do
approval_groups [] approval_groups []
approval_users [] approval_users []
......
# frozen_string_literal: true
require 'spec_helper'
describe AutoMerge::AddToMergeTrainWhenPipelineSucceedsService do
set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:service) { described_class.new(project, user) }
let(:merge_request) do
create(:merge_request, :with_merge_request_pipeline,
source_project: project, source_branch: 'feature',
target_project: project, target_branch: 'master')
end
let(:pipeline) { merge_request.reload.all_pipelines.first }
before do
stub_licensed_features(merge_trains: true, merge_pipelines: true)
project.add_maintainer(user)
project.update!(merge_trains_enabled: true, merge_pipelines_enabled: true)
allow(AutoMergeProcessWorker).to receive(:perform_async) { }
merge_request.update_head_pipeline
end
describe '#execute' do
subject { service.execute(merge_request) }
it 'enables auto merge' do
expect(SystemNoteService)
.to receive(:add_to_merge_train_when_pipeline_succeeds)
.with(merge_request, project, user, merge_request.diff_head_commit)
subject
expect(merge_request).to be_auto_merge_enabled
end
end
describe '#process' do
subject { service.process(merge_request) }
context 'when the latest pipeline in the merge request has succeeded' do
before do
pipeline.succeed!
end
it 'executes MergeTrainService' do
expect_next_instance_of(AutoMerge::MergeTrainService) do |train_service|
expect(train_service).to receive(:execute).with(merge_request)
end
subject
end
context 'when merge train strategy is not available for the merge request' do
before do
train_service = double
allow(train_service).to receive(:available_for?) { false }
allow(AutoMerge::MergeTrainService).to receive(:new) { train_service }
end
it 'cancels auto merge' do
expect(service).to receive(:cancel).once
subject
end
end
end
context 'when the latest pipeline in the merge request is running' do
it 'does not initialize MergeTrainService' do
expect(AutoMerge::MergeTrainService).not_to receive(:new)
subject
end
end
end
describe '#cancel' do
subject { service.cancel(merge_request) }
let(:merge_request) { create(:merge_request, :add_to_merge_train_when_pipeline_succeeds, merge_user: user) }
it 'cancels auto merge' do
expect(SystemNoteService)
.to receive(:cancel_add_to_merge_train_when_pipeline_succeeds)
.with(merge_request, project, user)
subject
expect(merge_request).not_to be_auto_merge_enabled
end
end
describe '#available_for?' do
subject { service.available_for?(merge_request) }
it { is_expected.to eq(true) }
context 'when merge trains option is disabled' do
before do
expect(merge_request.project).to receive(:merge_trains_enabled?) { false }
end
it { is_expected.to eq(false) }
end
context 'when merge request is submitted from a forked project' do
before do
allow(merge_request).to receive(:for_fork?) { true }
end
it { is_expected.to eq(false) }
end
context 'when the latest pipeline in the merge request is not active' do
before do
pipeline.succeed!
end
it { is_expected.to eq(false) }
end
context 'when merge request is not mergeable' do
before do
merge_request.update!(title: merge_request.wip_title)
end
it { is_expected.to eq(false) }
end
end
end
...@@ -7,7 +7,8 @@ describe AutoMergeService do ...@@ -7,7 +7,8 @@ describe AutoMergeService do
subject { described_class.all_strategies } subject { described_class.all_strategies }
it 'includes all strategies' do it 'includes all strategies' do
is_expected.to include(AutoMergeService::STRATEGY_MERGE_TRAIN) is_expected.to include(AutoMergeService::STRATEGY_MERGE_TRAIN,
AutoMergeService::STRATEGY_ADD_TO_MERGE_TRAIN_WHEN_PIPELINE_SUCCEEDS)
end end
end end
end end
...@@ -370,4 +370,38 @@ describe SystemNoteService do ...@@ -370,4 +370,38 @@ describe SystemNoteService do
end end
end end
end end
describe '.add_to_merge_train_when_pipeline_succeeds' do
subject { described_class.add_to_merge_train_when_pipeline_succeeds(noteable, project, author, noteable.diff_head_commit) }
let(:pipeline) { build(:ci_pipeline_without_jobs) }
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
it_behaves_like 'a system note' do
let(:action) { 'merge' }
end
it "posts the 'add to merge train when pipeline succeeds' system note" do
expect(subject.note).to match(%r{enabled automatic add to merge train when the pipeline for (\w+/\w+@)?\h{40} succeeds})
end
end
describe '.cancel_add_to_merge_train_when_pipeline_succeeds' do
subject { described_class.cancel_add_to_merge_train_when_pipeline_succeeds(noteable, project, author) }
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
it_behaves_like 'a system note' do
let(:action) { 'merge' }
end
it "posts the 'add to merge train when pipeline succeeds' system note" do
expect(subject.note).to eq 'cancelled automatic add to merge train'
end
end
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