1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
# frozen_string_literal: true
require 'gitlab/email/handler/base_handler'
require 'gitlab/email/handler/reply_processing'
module Gitlab
module Email
module Handler
class CreateMergeRequestHandler < BaseHandler
include ReplyProcessing
attr_reader :project_path, :incoming_email_token
def initialize(mail, mail_key)
super(mail, mail_key)
if m = /\A([^\+]*)\+merge-request\+(.*)/.match(mail_key.to_s)
@project_path, @incoming_email_token = m.captures
end
end
def can_handle?
@project_path && @incoming_email_token
end
def execute
raise ProjectNotFound unless project
validate_permission!(:create_merge_request_in)
validate_permission!(:create_merge_request_from)
verify_record!(
record: create_merge_request,
invalid_exception: InvalidMergeRequestError,
record_name: 'merge_request')
end
# rubocop: disable CodeReuse/ActiveRecord
def author
@author ||= User.find_by(incoming_email_token: incoming_email_token)
end
# rubocop: enable CodeReuse/ActiveRecord
def project
@project ||= Project.find_by_full_path(project_path)
end
def metrics_params
super.merge(includes_patches: patch_attachments.any?)
end
private
def build_merge_request
MergeRequests::BuildService.new(project, author, merge_request_params).execute
end
def create_merge_request
merge_request = build_merge_request
if patch_attachments.any?
apply_patches_to_source_branch(start_branch: merge_request.target_branch)
remove_patch_attachments
# Rebuild the merge request as the source branch might just have
# been created, so we should re-validate.
merge_request = build_merge_request
end
if merge_request.errors.any?
merge_request
else
MergeRequests::CreateService.new(project, author).create(merge_request)
end
end
def merge_request_params
params = {
source_project_id: project.id,
source_branch: source_branch,
target_project_id: project.id
}
params[:description] = message if message.present?
params
end
def apply_patches_to_source_branch(start_branch:)
patches = patch_attachments.map { |patch| patch.body.decoded }
result = Commits::CommitPatchService
.new(project, author, branch_name: source_branch, patches: patches, start_branch: start_branch)
.execute
if result[:status] != :success
message = "Could not apply patches to #{source_branch}:\n#{result[:message]}"
raise InvalidAttachment, message
end
end
def remove_patch_attachments
patch_attachments.each { |patch| mail.parts.delete(patch) }
# reset the message, so it needs to be reporocessed when the attachments
# have been modified
@message = nil
end
def patch_attachments
@patches ||= mail.attachments
.select { |attachment| attachment.filename.ends_with?('.patch') }
.sort_by(&:filename)
end
def source_branch
@source_branch ||= mail.subject
end
end
end
end
end
|