summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGauvain Pocentek <gauvain@pocentek.net>2016-03-22 17:59:53 +0100
committerGauvain Pocentek <gauvain@pocentek.net>2016-03-22 17:59:53 +0100
commit43e8a2a82deff4c95e156fc951f88ff6e95cf7b8 (patch)
treee121aaa940d8c03a84f266a967173748b7bb1bf4
parentbb463ae4e0ed79e472c0d594f76dc8177a29fb5c (diff)
downloadgitlab-43e8a2a82deff4c95e156fc951f88ff6e95cf7b8.tar.gz
Add support for MergeRequest validation
Both API and CLI support this feature. fixes #105
-rw-r--r--gitlab/cli.py20
-rw-r--r--gitlab/exceptions.py22
-rw-r--r--gitlab/objects.py37
-rwxr-xr-xtools/functional_tests.sh17
-rw-r--r--tools/python_test.py18
5 files changed, 111 insertions, 3 deletions
diff --git a/gitlab/cli.py b/gitlab/cli.py
index 090978b..91c45a0 100644
--- a/gitlab/cli.py
+++ b/gitlab/cli.py
@@ -41,6 +41,12 @@ EXTRA_ACTIONS = {
'blob': {'required': ['id', 'project-id',
'filepath']},
'builds': {'required': ['id', 'project-id']}},
+ gitlab.ProjectMergeRequest: {
+ 'merge': {'required': ['id', 'project-id'],
+ 'optional': ['merge-commit-message',
+ 'should-remove-source-branch',
+ 'merged-when-build-succeeds']}
+ },
gitlab.ProjectMilestone: {'issues': {'required': ['id', 'project-id']}},
gitlab.Project: {'search': {'required': ['query']},
'owned': {},
@@ -217,6 +223,18 @@ class GitlabCLI(object):
except Exception as e:
_die("Impossible to retry project build (%s)" % str(e))
+ def do_project_merge_request_merge(self, cls, gl, what, args):
+ try:
+ o = self.do_get(cls, gl, what, args)
+ should_remove = args['should_remove_source_branch']
+ build_succeeds = args['merged_when_build_succeeds']
+ return o.merge(
+ merge_commit_message=args['merge_commit_message'],
+ should_remove_source_branch=should_remove,
+ merged_when_build_succeeds=build_succeeds)
+ except Exception as e:
+ _die("Impossible to validate merge request (%s)" % str(e))
+
def do_project_milestone_issues(self, cls, gl, what, args):
try:
o = self.do_get(cls, gl, what, args)
@@ -298,6 +316,8 @@ def _populate_sub_parser_by_class(cls, sub_parser):
d = EXTRA_ACTIONS[cls][action_name]
[sub_parser_action.add_argument("--%s" % arg, required=True)
for arg in d.get('required', [])]
+ [sub_parser_action.add_argument("--%s" % arg, required=False)
+ for arg in d.get('optional', [])]
def _build_parser(args=sys.argv[1:]):
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
index 1b5ec6a..ce1f680 100644
--- a/gitlab/exceptions.py
+++ b/gitlab/exceptions.py
@@ -91,6 +91,18 @@ class GitlabUnblockError(GitlabOperationError):
pass
+class GitlabMRForbiddenError(GitlabOperationError):
+ pass
+
+
+class GitlabMRClosedError(GitlabOperationError):
+ pass
+
+
+class GitlabMROnBuildSuccessError(GitlabOperationError):
+ pass
+
+
def raise_error_from_response(response, error, expected_code=200):
"""Tries to parse gitlab error message from response and raises error.
@@ -99,7 +111,8 @@ def raise_error_from_response(response, error, expected_code=200):
If response status code is 401, raises instead GitlabAuthenticationError.
response: requests response object
- error: Error-class to raise. Should be inherited from GitLabError
+ error: Error-class or dict {return-code => class} of possible error class
+ to raise. Should be inherited from GitLabError
"""
if expected_code == response.status_code:
@@ -110,8 +123,11 @@ def raise_error_from_response(response, error, expected_code=200):
except (KeyError, ValueError):
message = response.content
- if response.status_code == 401:
- error = GitlabAuthenticationError
+ if isinstance(error, dict):
+ error = error.get(response.status_code, GitlabOperationError)
+ else:
+ if response.status_code == 401:
+ error = GitlabAuthenticationError
raise error(error_message=message,
response_code=response.status_code,
diff --git a/gitlab/objects.py b/gitlab/objects.py
index c5a47a0..3b8a467 100644
--- a/gitlab/objects.py
+++ b/gitlab/objects.py
@@ -1085,6 +1085,43 @@ class ProjectMergeRequest(GitlabObject):
self.gitlab, id, project_id=self.project_id,
merge_request_id=self.id, **kwargs)
+ def merge(self, merge_commit_message=None,
+ should_remove_source_branch=False,
+ merged_when_build_succeeds=False,
+ **kwargs):
+ """Accept the merge request.
+
+ Args:
+ merge_commit_message (bool): Commit message
+ should_remove_source_branch (bool): If True, removes the source
+ branch
+ merged_when_build_succeeds (bool): Wait for the build to succeed,
+ then merge
+
+ Returns:
+ ProjectMergeRequet: The updated MR
+ Raises:
+ GitlabConnectionError: If the server cannot be reached.
+ GitlabMRForbiddenError: If the user doesn't have permission to
+ close thr MR
+ GitlabMRClosedError: If the MR is already closed
+ """
+ url = '/projects/%s/merge_requests/%s/merge' % (self.project_id,
+ self.id)
+ data = {}
+ if merge_commit_message:
+ data['merge_commit_message'] = merge_commit_message
+ if should_remove_source_branch:
+ data['should_remove_source_branch'] = 'should_remove_source_branch'
+ if merged_when_build_succeeds:
+ data['merged_when_build_succeeds'] = 'merged_when_build_succeeds'
+
+ r = self.gitlab._raw_put(url, data=data, **kwargs)
+ errors = {401: GitlabMRForbiddenError,
+ 405: GitlabMRClosedError}
+ raise_error_from_response(r, errors)
+ return ProjectMergeRequest(self, r.json())
+
class ProjectMergeRequestManager(BaseManager):
obj_cls = ProjectMergeRequest
diff --git a/tools/functional_tests.sh b/tools/functional_tests.sh
index 84339e3..a4a8d06 100755
--- a/tools/functional_tests.sh
+++ b/tools/functional_tests.sh
@@ -80,6 +80,23 @@ testcase "branch creation" '
--branch-name branch1 --ref master >/dev/null 2>&1
'
+GITLAB project-file create --project-id "$PROJECT_ID" \
+ --file-path README2 --branch-name branch1 --content "CONTENT" \
+ --commit-message "second commit" >/dev/null 2>&1
+
+testcase "merge request creation" '
+ OUTPUT=$(GITLAB project-merge-request create \
+ --project-id "$PROJECT_ID" \
+ --source-branch branch1 --target-branch master \
+ --title "Update README")
+'
+MR_ID=$(pecho "${OUTPUT}" | grep ^id: | cut -d' ' -f2)
+
+testcase "merge request validation" '
+ GITLAB project-merge-request merge --project-id "$PROJECT_ID" \
+ --id "$MR_ID" >/dev/null 2>&1
+'
+
testcase "branch deletion" '
GITLAB project-branch delete --project-id "$PROJECT_ID" \
--name branch1 >/dev/null 2>&1
diff --git a/tools/python_test.py b/tools/python_test.py
index d32dccd..c5e955e 100644
--- a/tools/python_test.py
+++ b/tools/python_test.py
@@ -208,3 +208,21 @@ v1.save()
v1 = admin_project.variables.get(v1.key)
assert(v1.value == 'new_value1')
v1.delete()
+
+# branches and merges
+to_merge = admin_project.branches.create({'branch_name': 'branch1',
+ 'ref': 'master'})
+admin_project.files.create({'file_path': 'README2.rst',
+ 'branch_name': 'branch1',
+ 'content': 'Initial content',
+ 'commit_message': 'New commit in new branch'})
+mr = admin_project.mergerequests.create({'source_branch': 'branch1',
+ 'target_branch': 'master',
+ 'title': 'MR readme2'})
+ret = mr.merge()
+admin_project.branches.delete('branch1')
+
+try:
+ mr.merge()
+except gitlab.GitlabMRClosedError:
+ pass