From 8c037712a53c1c54e46298fbb93441d9b7a7144a Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sun, 22 Mar 2020 06:03:53 -0400 Subject: [PATCH 1/2] test: create separate module for commit tests --- gitlab/tests/objects/test_commits.py | 79 ++++++++++++++++++++++++++++ gitlab/tests/test_gitlab.py | 44 ---------------- 2 files changed, 79 insertions(+), 44 deletions(-) create mode 100644 gitlab/tests/objects/test_commits.py diff --git a/gitlab/tests/objects/test_commits.py b/gitlab/tests/objects/test_commits.py new file mode 100644 index 000000000..3247d60db --- /dev/null +++ b/gitlab/tests/objects/test_commits.py @@ -0,0 +1,79 @@ +from httmock import urlmatch, response, with_httmock + +from .test_projects import headers, TestProject + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/repository/commits/6b2257ea", + method="get", +) +def resp_get_commit(url, request): + """Mock for commit GET response.""" + content = """{ + "id": "6b2257eabcec3db1f59dafbd84935e3caea04235", + "short_id": "6b2257ea", + "title": "Initial commit" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", path="/api/v4/projects/1/repository/commits", method="post", +) +def resp_create_commit(url, request): + """Mock for commit create POST response.""" + content = """{ + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f", + "title": "Commit message" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", path="/api/v4/projects/1/repository/commits/6b2257ea", method="post", +) +def resp_revert_commit(url, request): + """Mock for commit revert POST response.""" + content = """{ + "id": "8b090c1b79a14f2bd9e8a738f717824ff53aebad", + "short_id": "8b090c1b", + "title":"Revert \\"Initial commit\\"" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +class TestCommit(TestProject): + """ + Base class for commit tests. Inherits from TestProject, + since currently all commit methods are under projects. + """ + + @with_httmock(resp_get_commit) + def test_get_commit(self): + commit = self.project.commits.get("6b2257ea") + self.assertEqual(commit.short_id, "6b2257ea") + self.assertEqual(commit.title, "Initial commit") + + @with_httmock(resp_create_commit) + def test_create_commit(self): + data = { + "branch": "master", + "commit_message": "Commit message", + "actions": [{"action": "create", "file_path": "README", "content": "",}], + } + commit = self.project.commits.create(data) + self.assertEqual(commit.short_id, "ed899a2f") + self.assertEqual(commit.title, data["commit_message"]) + + @with_httmock(resp_revert_commit) + def test_revert_commit(self): + commit = self.project.commits.get("6b2257ea", lazy=True) + revert_commit = commit.revert(branch="master") + self.assertEqual(revert_commit["short_id"], "8b090c1b") + self.assertEqual(revert_commit["title"], 'Revert "Initial commit"') diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py index 591f166ec..d104c7dea 100644 --- a/gitlab/tests/test_gitlab.py +++ b/gitlab/tests/test_gitlab.py @@ -843,50 +843,6 @@ def resp_deactivate(url, request): self.gl.users.get(1, lazy=True).activate() self.gl.users.get(1, lazy=True).deactivate() - def test_commit_revert(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/repository/commits/6b2257ea", - method="get", - ) - def resp_get_commit(url, request): - headers = {"content-type": "application/json"} - content = """{ - "id": "6b2257eabcec3db1f59dafbd84935e3caea04235", - "short_id": "6b2257ea", - "title": "Initial commit" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/repository/commits/6b2257ea", - method="post", - ) - def resp_revert_commit(url, request): - headers = {"content-type": "application/json"} - content = """{ - "id": "8b090c1b79a14f2bd9e8a738f717824ff53aebad", - "short_id": "8b090c1b", - "title":"Revert \\"Initial commit\\"" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_commit): - project = self.gl.projects.get(1, lazy=True) - commit = project.commits.get("6b2257ea") - self.assertEqual(commit.short_id, "6b2257ea") - self.assertEqual(commit.title, "Initial commit") - - with HTTMock(resp_revert_commit): - revert_commit = commit.revert(branch="master") - self.assertEqual(revert_commit["short_id"], "8b090c1b") - self.assertEqual(revert_commit["title"], 'Revert "Initial commit"') - def test_update_submodule(self): @urlmatch( scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get" From da7a809772233be27fa8e563925dd2e44e1ce058 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sun, 22 Mar 2020 11:52:59 -0400 Subject: [PATCH 2/2] feat: add support for commit GPG signature API --- docs/cli.rst | 12 ++++++++++++ docs/gl_objects/commits.rst | 4 ++++ gitlab/tests/objects/test_commits.py | 27 +++++++++++++++++++++++++++ gitlab/v4/objects.py | 18 ++++++++++++++++++ tools/cli_test_v4.sh | 7 +++++++ tools/python_test_v4.py | 8 ++++++++ 6 files changed, 76 insertions(+) diff --git a/docs/cli.rst b/docs/cli.rst index 320790203..b4a6c5e0a 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -236,6 +236,18 @@ Create a snippet: $ gitlab project-snippet create --project-id 2 --title "the title" \ --file-name "the name" --code "the code" +Get a specific project commit by its SHA id: + +.. code-block:: console + + $ gitlab project-commit get --project-id 2 --id a43290c + +Get the GPG signature of a signed commit: + +.. code-block:: console + + $ gitlab project-commit signature --project-id 2 --id a43290c + Define the status of a commit (as would be done from a CI tool for example): .. code-block:: console diff --git a/docs/gl_objects/commits.rst b/docs/gl_objects/commits.rst index abfedc8a4..e6bdfd882 100644 --- a/docs/gl_objects/commits.rst +++ b/docs/gl_objects/commits.rst @@ -82,6 +82,10 @@ Get the references the commit has been pushed to (branches and tags):: commit.refs('tag') # only tags commit.refs('branch') # only branches +Get the GPG signature of the commit (if the commit was signed):: + + commit.signature() + List the merge requests related to a commit:: commit.merge_requests() diff --git a/gitlab/tests/objects/test_commits.py b/gitlab/tests/objects/test_commits.py index 3247d60db..23a42852f 100644 --- a/gitlab/tests/objects/test_commits.py +++ b/gitlab/tests/objects/test_commits.py @@ -48,6 +48,26 @@ def resp_revert_commit(url, request): return response(200, content, headers, None, 5, request) +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/repository/commits/6b2257ea/signature", + method="get", +) +def resp_get_commit_gpg_signature(url, request): + """Mock for commit GPG signature GET response.""" + content = """{ + "gpg_key_id": 1, + "gpg_key_primary_keyid": "8254AAB3FBD54AC9", + "gpg_key_user_name": "John Doe", + "gpg_key_user_email": "johndoe@example.com", + "verification_status": "verified", + "gpg_key_subkey_id": null + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + class TestCommit(TestProject): """ Base class for commit tests. Inherits from TestProject, @@ -77,3 +97,10 @@ def test_revert_commit(self): revert_commit = commit.revert(branch="master") self.assertEqual(revert_commit["short_id"], "8b090c1b") self.assertEqual(revert_commit["title"], 'Revert "Initial commit"') + + @with_httmock(resp_get_commit_gpg_signature) + def test_get_commit_gpg_signature(self): + commit = self.project.commits.get("6b2257ea", lazy=True) + signature = commit.signature() + self.assertEqual(signature["gpg_key_primary_keyid"], "8254AAB3FBD54AC9") + self.assertEqual(signature["verification_status"], "verified") diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 9da9adf2c..96327b230 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -2172,6 +2172,24 @@ def revert(self, branch, **kwargs): post_data = {"branch": branch} return self.manager.gitlab.http_post(path, post_data=post_data, **kwargs) + @cli.register_custom_action("ProjectCommit") + @exc.on_http_error(exc.GitlabGetError) + def signature(self, **kwargs): + """Get the GPG signature of the commit. + + Args: + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabGetError: If the signature could not be retrieved + + Returns: + dict: The commit's GPG signature data + """ + path = "%s/%s/signature" % (self.manager.path, self.get_id()) + return self.manager.gitlab.http_get(path, **kwargs) + class ProjectCommitManager(RetrieveMixin, CreateMixin, RESTManager): _path = "/projects/%(project_id)s/repository/commits" diff --git a/tools/cli_test_v4.sh b/tools/cli_test_v4.sh index cf157f436..b9167057a 100755 --- a/tools/cli_test_v4.sh +++ b/tools/cli_test_v4.sh @@ -113,6 +113,13 @@ testcase "revert commit" ' --id "$COMMIT_ID" --branch master ' +# Test commit GPG signature +testcase "attempt to get GPG signature of unsigned commit" ' + OUTPUT=$(GITLAB project-commit signature --project-id "$PROJECT_ID" \ + --id "$COMMIT_ID" 2>&1 || exit 0) + echo "$OUTPUT" | grep -q "404 GPG Signature Not Found" +' + # Test project labels testcase "create project label" ' OUTPUT=$(GITLAB -v project-label create --project-id $PROJECT_ID \ diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py index fad8c69eb..69b0d3181 100644 --- a/tools/python_test_v4.py +++ b/tools/python_test_v4.py @@ -473,6 +473,14 @@ # assert commit.refs() # assert commit.merge_requests() +# commit GPG signature (for unsigned commits) +# TODO: reasonable tests for signed commits? +try: + signature = commit.signature() +except gitlab.GitlabGetError as e: + error_message = e.error_message +assert error_message == "404 GPG Signature Not Found" + # commit comment commit.comments.create({"note": "This is a commit comment"}) # assert len(commit.comments.list()) == 1