summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2019-04-09 12:53:07 +0000
committerJohn T Skarbek <jskarbek@gitlab.com>2019-04-10 15:12:32 -0400
commitc24cbc01908d5a10b3152127497adec3d07aaaae (patch)
tree37ca9b5d15d30e59db119d54e12030c04bd98bad /app
parent8a0593156bc065f536b25276c88d58b13937c1b2 (diff)
downloadgitlab-ce-c24cbc01908d5a10b3152127497adec3d07aaaae.tar.gz
Merge branch '43263-git-push-option-to-create-mr' into 'master'
Git push options to create a merge request, set target_branch and set merge when pipeline succeeds Closes #53198 and #43263 See merge request gitlab-org/gitlab-ce!26752
Diffstat (limited to 'app')
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/merge_requests/push_options_handler_service.rb162
-rw-r--r--app/workers/post_receive.rb2
3 files changed, 164 insertions, 2 deletions
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 8973c5ffc9e..41dee4e5641 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -37,7 +37,7 @@ module Ci
variables_attributes: params[:variables_attributes],
project: project,
current_user: current_user,
- push_options: params[:push_options],
+ push_options: params[:push_options] || {},
chat_data: params[:chat_data],
**extra_options(options))
diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb
new file mode 100644
index 00000000000..d92eb0a68c3
--- /dev/null
+++ b/app/services/merge_requests/push_options_handler_service.rb
@@ -0,0 +1,162 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ class PushOptionsHandlerService
+ LIMIT = 10
+
+ attr_reader :branches, :changes_by_branch, :current_user, :errors,
+ :project, :push_options, :target_project
+
+ def initialize(project, current_user, changes, push_options)
+ @project = project
+ @target_project = @project.default_merge_request_target
+ @current_user = current_user
+ @branches = get_branches(changes)
+ @push_options = push_options
+ @errors = []
+ end
+
+ def execute
+ validate_service
+ return self if errors.present?
+
+ branches.each do |branch|
+ execute_for_branch(branch)
+ rescue Gitlab::Access::AccessDeniedError
+ errors << 'User access was denied'
+ rescue StandardError => e
+ Gitlab::AppLogger.error(e)
+ errors << 'An unknown error occurred'
+ end
+
+ self
+ end
+
+ private
+
+ def get_branches(raw_changes)
+ Gitlab::ChangesList.new(raw_changes).map do |changes|
+ next unless Gitlab::Git.branch_ref?(changes[:ref])
+
+ # Deleted branch
+ next if Gitlab::Git.blank_ref?(changes[:newrev])
+
+ # Default branch
+ branch_name = Gitlab::Git.branch_name(changes[:ref])
+ next if branch_name == target_project.default_branch
+
+ branch_name
+ end.compact.uniq
+ end
+
+ 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"
+ end
+ end
+
+ # Returns a Hash of branch => MergeRequest
+ def merge_requests
+ @merge_requests ||= MergeRequest.from_project(target_project)
+ .opened
+ .from_source_branches(branches)
+ .index_by(&:source_branch)
+ end
+
+ def execute_for_branch(branch)
+ merge_request = merge_requests[branch]
+
+ 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
+
+ # Use BuildService to assign the standard attributes of a merge request
+ merge_request = ::MergeRequests::BuildService.new(
+ project,
+ current_user,
+ create_params(branch)
+ ).execute
+
+ unless merge_request.errors.present?
+ merge_request = ::MergeRequests::CreateService.new(
+ project,
+ current_user,
+ merge_request.attributes
+ ).execute
+ end
+
+ collect_errors_from_merge_request(merge_request) unless merge_request.persisted?
+ end
+
+ def update!(merge_request)
+ merge_request = ::MergeRequests::UpdateService.new(
+ target_project,
+ current_user,
+ update_params
+ ).execute(merge_request)
+
+ collect_errors_from_merge_request(merge_request) unless merge_request.valid?
+ end
+
+ def create_params(branch)
+ params = {
+ assignee: current_user,
+ source_branch: branch,
+ source_project: project,
+ target_branch: push_options[:target] || target_project.default_branch,
+ target_project: target_project
+ }
+
+ if push_options.key?(:merge_when_pipeline_succeeds)
+ params.merge!(
+ merge_when_pipeline_succeeds: push_options[:merge_when_pipeline_succeeds],
+ merge_user: current_user
+ )
+ end
+
+ params
+ end
+
+ def update_params
+ params = {}
+
+ if push_options.key?(:merge_when_pipeline_succeeds)
+ params.merge!(
+ merge_when_pipeline_succeeds: push_options[:merge_when_pipeline_succeeds],
+ merge_user: current_user
+ )
+ end
+
+ if push_options.key?(:target)
+ params[:target_branch] = push_options[:target]
+ end
+
+ params
+ end
+
+ def collect_errors_from_merge_request(merge_request)
+ merge_request.errors.full_messages.each do |error|
+ errors << error
+ end
+ end
+ end
+end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 396f44396a3..a5554f07699 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -3,7 +3,7 @@
class PostReceive
include ApplicationWorker
- def perform(gl_repository, identifier, changes, push_options = [])
+ def perform(gl_repository, identifier, changes, push_options = {})
project, repo_type = Gitlab::GlRepository.parse(gl_repository)
if project.nil?