2019-03-29 01:07:03 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module MergeRequests
|
2021-05-12 03:10:21 +00:00
|
|
|
class PushOptionsHandlerService < ::BaseProjectService
|
2019-03-29 01:07:03 +00:00
|
|
|
LIMIT = 10
|
|
|
|
|
2021-05-12 03:10:21 +00:00
|
|
|
attr_reader :errors, :changes,
|
|
|
|
:push_options, :target_project
|
|
|
|
|
|
|
|
def initialize(project:, current_user:, params: {}, changes:, push_options:)
|
|
|
|
super(project: project, current_user: current_user, params: params)
|
2019-03-29 01:07:03 +00:00
|
|
|
|
2019-04-08 05:07:53 +00:00
|
|
|
@target_project = @project.default_merge_request_target
|
2019-11-26 12:06:18 +00:00
|
|
|
@changes = Gitlab::ChangesList.new(changes)
|
2019-03-29 01:07:03 +00:00
|
|
|
@push_options = push_options
|
|
|
|
@errors = []
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute
|
2019-04-08 05:07:53 +00:00
|
|
|
validate_service
|
|
|
|
return self if errors.present?
|
|
|
|
|
2019-03-29 01:07:03 +00:00
|
|
|
branches.each do |branch|
|
|
|
|
execute_for_branch(branch)
|
2019-04-08 05:07:53 +00:00
|
|
|
rescue Gitlab::Access::AccessDeniedError
|
|
|
|
errors << 'User access was denied'
|
2019-04-08 22:55:07 +00:00
|
|
|
rescue StandardError => e
|
|
|
|
Gitlab::AppLogger.error(e)
|
|
|
|
errors << 'An unknown error occurred'
|
2019-03-29 01:07:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2019-11-26 12:06:18 +00:00
|
|
|
def branches
|
|
|
|
changes_by_branch.keys
|
|
|
|
end
|
|
|
|
|
|
|
|
def changes_by_branch
|
|
|
|
@changes_by_branch ||= changes.each_with_object({}) do |changes, result|
|
2019-04-08 05:07:53 +00:00
|
|
|
next unless Gitlab::Git.branch_ref?(changes[:ref])
|
2019-03-29 01:07:03 +00:00
|
|
|
|
|
|
|
# Deleted branch
|
2019-04-08 05:07:53 +00:00
|
|
|
next if Gitlab::Git.blank_ref?(changes[:newrev])
|
2019-03-29 01:07:03 +00:00
|
|
|
|
|
|
|
# Default branch
|
2019-04-08 05:07:53 +00:00
|
|
|
branch_name = Gitlab::Git.branch_name(changes[:ref])
|
|
|
|
next if branch_name == target_project.default_branch
|
|
|
|
|
2019-11-26 12:06:18 +00:00
|
|
|
result[branch_name] = changes
|
|
|
|
end
|
2019-04-08 05:07:53 +00:00
|
|
|
end
|
2019-03-29 01:07:03 +00:00
|
|
|
|
2019-04-08 05:07:53 +00:00
|
|
|
def validate_service
|
|
|
|
errors << 'User is required' if current_user.nil?
|
|
|
|
|
|
|
|
unless target_project.merge_requests_enabled?
|
|
|
|
errors << "Merge requests are not enabled for project #{target_project.full_path}"
|
|
|
|
end
|
|
|
|
|
|
|
|
if branches.size > LIMIT
|
|
|
|
errors << "Too many branches pushed (#{branches.size} were pushed, limit is #{LIMIT})"
|
|
|
|
end
|
|
|
|
|
|
|
|
if push_options[:target] && !target_project.repository.branch_exists?(push_options[:target])
|
|
|
|
errors << "Branch #{push_options[:target]} does not exist"
|
2019-03-29 01:07:03 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-08 05:07:53 +00:00
|
|
|
# Returns a Hash of branch => MergeRequest
|
2019-03-29 01:07:03 +00:00
|
|
|
def merge_requests
|
2019-04-08 05:07:53 +00:00
|
|
|
@merge_requests ||= MergeRequest.from_project(target_project)
|
2019-03-29 01:07:03 +00:00
|
|
|
.opened
|
2019-04-08 05:07:53 +00:00
|
|
|
.from_source_branches(branches)
|
|
|
|
.index_by(&:source_branch)
|
2019-03-29 01:07:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def execute_for_branch(branch)
|
2019-04-08 05:07:53 +00:00
|
|
|
merge_request = merge_requests[branch]
|
2019-03-29 01:07:03 +00:00
|
|
|
|
|
|
|
if merge_request
|
|
|
|
update!(merge_request)
|
|
|
|
else
|
|
|
|
create!(branch)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def create!(branch)
|
|
|
|
unless push_options[:create]
|
|
|
|
errors << "A merge_request.create push option is required to create a merge request for branch #{branch}"
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-04-08 05:07:53 +00:00
|
|
|
# Use BuildService to assign the standard attributes of a merge request
|
|
|
|
merge_request = ::MergeRequests::BuildService.new(
|
2021-05-12 03:10:21 +00:00
|
|
|
project: project,
|
|
|
|
current_user: current_user,
|
|
|
|
params: create_params(branch)
|
2019-03-29 01:07:03 +00:00
|
|
|
).execute
|
|
|
|
|
2019-04-08 05:07:53 +00:00
|
|
|
unless merge_request.errors.present?
|
|
|
|
merge_request = ::MergeRequests::CreateService.new(
|
2021-05-12 03:10:21 +00:00
|
|
|
project: project,
|
|
|
|
current_user: current_user,
|
|
|
|
params: merge_request.attributes.merge(assignees: merge_request.assignees,
|
2019-11-26 12:06:18 +00:00
|
|
|
label_ids: merge_request.label_ids)
|
2019-04-08 05:07:53 +00:00
|
|
|
).execute
|
|
|
|
end
|
|
|
|
|
2019-03-29 01:07:03 +00:00
|
|
|
collect_errors_from_merge_request(merge_request) unless merge_request.persisted?
|
|
|
|
end
|
|
|
|
|
|
|
|
def update!(merge_request)
|
|
|
|
merge_request = ::MergeRequests::UpdateService.new(
|
2021-05-12 03:10:21 +00:00
|
|
|
project: target_project,
|
|
|
|
current_user: current_user,
|
|
|
|
params: update_params(merge_request)
|
2019-03-29 01:07:03 +00:00
|
|
|
).execute(merge_request)
|
|
|
|
|
|
|
|
collect_errors_from_merge_request(merge_request) unless merge_request.valid?
|
|
|
|
end
|
|
|
|
|
2019-07-17 07:19:16 +00:00
|
|
|
def base_params
|
2019-07-16 07:16:41 +00:00
|
|
|
params = {
|
|
|
|
title: push_options[:title],
|
|
|
|
description: push_options[:description],
|
|
|
|
target_branch: push_options[:target],
|
2019-08-15 03:43:33 +00:00
|
|
|
force_remove_source_branch: push_options[:remove_source_branch],
|
|
|
|
label: push_options[:label],
|
2021-03-16 09:11:17 +00:00
|
|
|
unlabel: push_options[:unlabel],
|
|
|
|
assign: push_options[:assign],
|
|
|
|
unassign: push_options[:unassign]
|
2019-07-16 07:16:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
params.compact!
|
2019-04-05 13:22:58 +00:00
|
|
|
|
2019-08-15 03:43:33 +00:00
|
|
|
params[:add_labels] = params.delete(:label).keys if params.has_key?(:label)
|
|
|
|
params[:remove_labels] = params.delete(:unlabel).keys if params.has_key?(:unlabel)
|
|
|
|
|
2021-03-16 09:11:17 +00:00
|
|
|
params[:add_assignee_ids] = params.delete(:assign).keys if params.has_key?(:assign)
|
|
|
|
params[:remove_assignee_ids] = params.delete(:unassign).keys if params.has_key?(:unassign)
|
|
|
|
|
2021-06-23 15:07:50 +00:00
|
|
|
if push_options[:milestone]
|
|
|
|
milestone = Milestone.for_projects_and_groups(@project, @project.ancestors_upto)&.find_by_name(push_options[:milestone])
|
|
|
|
params[:milestone] = milestone if milestone
|
|
|
|
end
|
2021-06-17 18:10:22 +00:00
|
|
|
|
2019-04-05 13:22:58 +00:00
|
|
|
params
|
|
|
|
end
|
|
|
|
|
2019-11-26 12:06:18 +00:00
|
|
|
def merge_params(branch)
|
|
|
|
return {} unless push_options.key?(:merge_when_pipeline_succeeds)
|
|
|
|
|
|
|
|
{
|
|
|
|
merge_when_pipeline_succeeds: push_options[:merge_when_pipeline_succeeds],
|
|
|
|
merge_user: current_user,
|
|
|
|
sha: changes_by_branch.dig(branch, :newrev)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2019-07-17 07:19:16 +00:00
|
|
|
def create_params(branch)
|
|
|
|
params = base_params
|
2019-04-05 13:22:58 +00:00
|
|
|
|
2019-07-17 07:19:16 +00:00
|
|
|
params.merge!(
|
|
|
|
assignees: [current_user],
|
|
|
|
source_branch: branch,
|
|
|
|
source_project: project,
|
|
|
|
target_project: target_project
|
|
|
|
)
|
2019-07-14 18:19:13 +00:00
|
|
|
|
2019-11-26 12:06:18 +00:00
|
|
|
params.merge!(merge_params(branch))
|
|
|
|
|
2019-07-17 07:19:16 +00:00
|
|
|
params[:target_branch] ||= target_project.default_branch
|
2019-04-05 13:22:58 +00:00
|
|
|
|
|
|
|
params
|
2019-03-29 01:07:03 +00:00
|
|
|
end
|
|
|
|
|
2019-11-26 12:06:18 +00:00
|
|
|
def update_params(merge_request)
|
|
|
|
base_params.merge(merge_params(merge_request.source_branch))
|
2019-07-17 07:19:16 +00:00
|
|
|
end
|
|
|
|
|
2019-03-29 01:07:03 +00:00
|
|
|
def collect_errors_from_merge_request(merge_request)
|
2019-04-08 05:07:53 +00:00
|
|
|
merge_request.errors.full_messages.each do |error|
|
|
|
|
errors << error
|
|
|
|
end
|
2019-03-29 01:07:03 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|