Skip to content

Commit 43e8a2a

Browse files
author
Gauvain Pocentek
committed
Add support for MergeRequest validation
Both API and CLI support this feature. fixes #105
1 parent bb463ae commit 43e8a2a

File tree

5 files changed

+111
-3
lines changed

5 files changed

+111
-3
lines changed

gitlab/cli.py

+20
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
'blob': {'required': ['id', 'project-id',
4242
'filepath']},
4343
'builds': {'required': ['id', 'project-id']}},
44+
gitlab.ProjectMergeRequest: {
45+
'merge': {'required': ['id', 'project-id'],
46+
'optional': ['merge-commit-message',
47+
'should-remove-source-branch',
48+
'merged-when-build-succeeds']}
49+
},
4450
gitlab.ProjectMilestone: {'issues': {'required': ['id', 'project-id']}},
4551
gitlab.Project: {'search': {'required': ['query']},
4652
'owned': {},
@@ -217,6 +223,18 @@ def do_project_build_retry(self, cls, gl, what, args):
217223
except Exception as e:
218224
_die("Impossible to retry project build (%s)" % str(e))
219225

226+
def do_project_merge_request_merge(self, cls, gl, what, args):
227+
try:
228+
o = self.do_get(cls, gl, what, args)
229+
should_remove = args['should_remove_source_branch']
230+
build_succeeds = args['merged_when_build_succeeds']
231+
return o.merge(
232+
merge_commit_message=args['merge_commit_message'],
233+
should_remove_source_branch=should_remove,
234+
merged_when_build_succeeds=build_succeeds)
235+
except Exception as e:
236+
_die("Impossible to validate merge request (%s)" % str(e))
237+
220238
def do_project_milestone_issues(self, cls, gl, what, args):
221239
try:
222240
o = self.do_get(cls, gl, what, args)
@@ -298,6 +316,8 @@ def _populate_sub_parser_by_class(cls, sub_parser):
298316
d = EXTRA_ACTIONS[cls][action_name]
299317
[sub_parser_action.add_argument("--%s" % arg, required=True)
300318
for arg in d.get('required', [])]
319+
[sub_parser_action.add_argument("--%s" % arg, required=False)
320+
for arg in d.get('optional', [])]
301321

302322

303323
def _build_parser(args=sys.argv[1:]):

gitlab/exceptions.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ class GitlabUnblockError(GitlabOperationError):
9191
pass
9292

9393

94+
class GitlabMRForbiddenError(GitlabOperationError):
95+
pass
96+
97+
98+
class GitlabMRClosedError(GitlabOperationError):
99+
pass
100+
101+
102+
class GitlabMROnBuildSuccessError(GitlabOperationError):
103+
pass
104+
105+
94106
def raise_error_from_response(response, error, expected_code=200):
95107
"""Tries to parse gitlab error message from response and raises error.
96108
@@ -99,7 +111,8 @@ def raise_error_from_response(response, error, expected_code=200):
99111
If response status code is 401, raises instead GitlabAuthenticationError.
100112
101113
response: requests response object
102-
error: Error-class to raise. Should be inherited from GitLabError
114+
error: Error-class or dict {return-code => class} of possible error class
115+
to raise. Should be inherited from GitLabError
103116
"""
104117

105118
if expected_code == response.status_code:
@@ -110,8 +123,11 @@ def raise_error_from_response(response, error, expected_code=200):
110123
except (KeyError, ValueError):
111124
message = response.content
112125

113-
if response.status_code == 401:
114-
error = GitlabAuthenticationError
126+
if isinstance(error, dict):
127+
error = error.get(response.status_code, GitlabOperationError)
128+
else:
129+
if response.status_code == 401:
130+
error = GitlabAuthenticationError
115131

116132
raise error(error_message=message,
117133
response_code=response.status_code,

gitlab/objects.py

+37
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,43 @@ def Note(self, id=None, **kwargs):
10851085
self.gitlab, id, project_id=self.project_id,
10861086
merge_request_id=self.id, **kwargs)
10871087

1088+
def merge(self, merge_commit_message=None,
1089+
should_remove_source_branch=False,
1090+
merged_when_build_succeeds=False,
1091+
**kwargs):
1092+
"""Accept the merge request.
1093+
1094+
Args:
1095+
merge_commit_message (bool): Commit message
1096+
should_remove_source_branch (bool): If True, removes the source
1097+
branch
1098+
merged_when_build_succeeds (bool): Wait for the build to succeed,
1099+
then merge
1100+
1101+
Returns:
1102+
ProjectMergeRequet: The updated MR
1103+
Raises:
1104+
GitlabConnectionError: If the server cannot be reached.
1105+
GitlabMRForbiddenError: If the user doesn't have permission to
1106+
close thr MR
1107+
GitlabMRClosedError: If the MR is already closed
1108+
"""
1109+
url = '/projects/%s/merge_requests/%s/merge' % (self.project_id,
1110+
self.id)
1111+
data = {}
1112+
if merge_commit_message:
1113+
data['merge_commit_message'] = merge_commit_message
1114+
if should_remove_source_branch:
1115+
data['should_remove_source_branch'] = 'should_remove_source_branch'
1116+
if merged_when_build_succeeds:
1117+
data['merged_when_build_succeeds'] = 'merged_when_build_succeeds'
1118+
1119+
r = self.gitlab._raw_put(url, data=data, **kwargs)
1120+
errors = {401: GitlabMRForbiddenError,
1121+
405: GitlabMRClosedError}
1122+
raise_error_from_response(r, errors)
1123+
return ProjectMergeRequest(self, r.json())
1124+
10881125

10891126
class ProjectMergeRequestManager(BaseManager):
10901127
obj_cls = ProjectMergeRequest

tools/functional_tests.sh

+17
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,23 @@ testcase "branch creation" '
8080
--branch-name branch1 --ref master >/dev/null 2>&1
8181
'
8282

83+
GITLAB project-file create --project-id "$PROJECT_ID" \
84+
--file-path README2 --branch-name branch1 --content "CONTENT" \
85+
--commit-message "second commit" >/dev/null 2>&1
86+
87+
testcase "merge request creation" '
88+
OUTPUT=$(GITLAB project-merge-request create \
89+
--project-id "$PROJECT_ID" \
90+
--source-branch branch1 --target-branch master \
91+
--title "Update README")
92+
'
93+
MR_ID=$(pecho "${OUTPUT}" | grep ^id: | cut -d' ' -f2)
94+
95+
testcase "merge request validation" '
96+
GITLAB project-merge-request merge --project-id "$PROJECT_ID" \
97+
--id "$MR_ID" >/dev/null 2>&1
98+
'
99+
83100
testcase "branch deletion" '
84101
GITLAB project-branch delete --project-id "$PROJECT_ID" \
85102
--name branch1 >/dev/null 2>&1

tools/python_test.py

+18
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,21 @@
208208
v1 = admin_project.variables.get(v1.key)
209209
assert(v1.value == 'new_value1')
210210
v1.delete()
211+
212+
# branches and merges
213+
to_merge = admin_project.branches.create({'branch_name': 'branch1',
214+
'ref': 'master'})
215+
admin_project.files.create({'file_path': 'README2.rst',
216+
'branch_name': 'branch1',
217+
'content': 'Initial content',
218+
'commit_message': 'New commit in new branch'})
219+
mr = admin_project.mergerequests.create({'source_branch': 'branch1',
220+
'target_branch': 'master',
221+
'title': 'MR readme2'})
222+
ret = mr.merge()
223+
admin_project.branches.delete('branch1')
224+
225+
try:
226+
mr.merge()
227+
except gitlab.GitlabMRClosedError:
228+
pass

0 commit comments

Comments
 (0)