diff options
author | Gauvain Pocentek <gauvain@pocentek.net> | 2016-03-22 17:59:53 +0100 |
---|---|---|
committer | Gauvain Pocentek <gauvain@pocentek.net> | 2016-03-22 17:59:53 +0100 |
commit | 43e8a2a82deff4c95e156fc951f88ff6e95cf7b8 (patch) | |
tree | e121aaa940d8c03a84f266a967173748b7bb1bf4 | |
parent | bb463ae4e0ed79e472c0d594f76dc8177a29fb5c (diff) | |
download | gitlab-43e8a2a82deff4c95e156fc951f88ff6e95cf7b8.tar.gz |
Add support for MergeRequest validation
Both API and CLI support this feature.
fixes #105
-rw-r--r-- | gitlab/cli.py | 20 | ||||
-rw-r--r-- | gitlab/exceptions.py | 22 | ||||
-rw-r--r-- | gitlab/objects.py | 37 | ||||
-rwxr-xr-x | tools/functional_tests.sh | 17 | ||||
-rw-r--r-- | tools/python_test.py | 18 |
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 |