Gitlab - Minimal reporter ables to comment on MR

For history:
https://gitlab.com/fabien.dot.boucher/demo-zuul/merge_requests/7#note_244782493

Change-Id: I2b7486c00dad24752d7073f301ce8d3384c0646c
This commit is contained in:
Fabien Boucher 2019-11-14 17:26:57 +01:00 committed by Tristan Cacqueray
parent f1368e84ec
commit e20d254e6d
5 changed files with 132 additions and 3 deletions

View File

@ -1584,6 +1584,18 @@ class FakeGitlabAPIClient(gitlabconnection.GitlabAPIClient):
if match:
return [{'name': 'master'}], 200, "", "GET"
def post(self, url, params=None, zuul_event_id=None):
self.log.info(
"Posting on resource %s, params (%s) ..." % (url, params))
match = re.match(r'.+/projects/(.+)/merge_requests/(\d+)/notes$', url)
if match:
mr = self._get_mr(match)
mr.addNote(params['body'])
return {}, 200, "", "POST"
class GitlabChangeReference(git.Reference):
_common_path_default = "refs/merge-requests"
@ -1612,6 +1624,7 @@ class FakeGitlabMergeRequest(object):
self.merge_status = 'can_be_merged'
self.uuid = uuid.uuid4().hex
self.labels = []
self.notes = []
self.upstream_root = upstream_root
self.url = "https://%s/%s/merge_requests/%s" % (
self.gitlab.server, urllib.parse.quote_plus(
@ -1632,6 +1645,15 @@ class FakeGitlabMergeRequest(object):
def getMRReference(self):
return '%s/head' % self.number
def addNote(self, body):
self.notes.append(
{
"body": body,
"created_at": datetime.datetime.now().strftime(
'%Y-%m-%dT%H:%M:%S.%fZ'),
}
)
def _addCommitInMR(self, files=[], reset=False):
repo = self._getRepo()
ref = repo.references[self.getMRReference()]

View File

@ -6,6 +6,15 @@
- event: gl_merge_request
action:
- opened
start:
gitlab:
comment: True
success:
gitlab:
comment: True
failure:
gitlab:
comment: True
- job:
name: base
@ -16,8 +25,13 @@
name: project-test1
run: playbooks/project-test1.yaml
- job:
name: project-test2
run: playbooks/project-test2.yaml
- project:
name: org/project
check:
jobs:
- project-test1
- project-test2

View File

@ -12,11 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import re
import socket
from tests.base import ZuulTestCase, simple_layout
from tests.base import ZuulWebFixture
from testtools.matchers import MatchesRegex
class TestGitlabWebhook(ZuulTestCase):
config_file = 'zuul-gitlab-driver.conf'
@ -67,3 +70,42 @@ class TestGitlabWebhook(ZuulTestCase):
self.assertEqual('SUCCESS',
self.getJobFromHistory('project-test1').result)
class TestGitlabDriver(ZuulTestCase):
config_file = 'zuul-gitlab-driver.conf'
@simple_layout('layouts/basic-gitlab.yaml', driver='gitlab')
def test_pull_request_opened(self):
description = "This is the\nMR description."
A = self.fake_gitlab.openFakeMergeRequest(
'org/project', 'master', 'A', description=description)
self.fake_gitlab.emitEvent(
A.getMergeRequestEvent(), project='org/project')
self.waitUntilSettled()
self.assertEqual('SUCCESS',
self.getJobFromHistory('project-test1').result)
self.assertEqual('SUCCESS',
self.getJobFromHistory('project-test2').result)
job = self.getJobFromHistory('project-test2')
zuulvars = job.parameters['zuul']
self.assertEqual(str(A.number), zuulvars['change'])
self.assertEqual(str(A.patch_number), zuulvars['patchset'])
self.assertEqual('master', zuulvars['branch'])
self.assertEquals('https://gitlab/org/project/merge_requests/1',
zuulvars['items'][0]['change_url'])
self.assertEqual(zuulvars["message"], description)
self.assertEqual(2, len(self.history))
self.assertEqual(2, len(A.notes))
self.assertEqual(
A.notes[0]['body'], "Starting check jobs.")
self.assertThat(
A.notes[1]['body'],
MatchesRegex(r'.*project-test1.*SUCCESS.*', re.DOTALL))
self.assertThat(
A.notes[1]['body'],
MatchesRegex(r'.*project-test2.*SUCCESS.*', re.DOTALL))

View File

@ -184,7 +184,7 @@ class GitlabAPIClient():
self.session = requests.Session()
self.baseurl = '%s/api/v4/' % baseurl
self.api_token = api_token
self.headers = {'Authorization': 'Authorization: Bearer %s' % (
self.headers = {'Authorization': 'Bearer %s' % (
self.api_token)}
def _manage_error(self, data, code, url, verb, zuul_event_id=None):
@ -204,6 +204,15 @@ class GitlabAPIClient():
ret.status_code, ret.text))
return ret.json(), ret.status_code, ret.url, 'GET'
def post(self, url, params=None, zuul_event_id=None):
log = get_annotated_logger(self.log, zuul_event_id)
log.info(
"Posting on resource %s, params (%s) ..." % (url, params))
ret = self.session.post(url, data=params, headers=self.headers)
log.debug("POST returned (code: %s): %s" % (
ret.status_code, ret.text))
return ret.json(), ret.status_code, ret.url, 'POST'
# https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr
def get_mr(self, project_name, number, zuul_event_id=None):
path = "/projects/%s/merge_requests/%s" % (
@ -220,6 +229,17 @@ class GitlabAPIClient():
self._manage_error(*resp, zuul_event_id=zuul_event_id)
return [branch['name'] for branch in resp[0]]
# https://docs.gitlab.com/ee/api/notes.html#create-new-merge-request-note
def comment_mr(self, project_name, number, msg, zuul_event_id=None):
path = "/projects/%s/merge_requests/%s/notes" % (
quote_plus(project_name), number)
params = {'body': msg}
resp = self.post(
self.baseurl + path, params=params,
zuul_event_id=zuul_event_id)
self._manage_error(*resp, zuul_event_id=zuul_event_id)
return resp[0]
class GitlabConnection(BaseConnection):
driver_name = 'gitlab'
@ -393,6 +413,12 @@ class GitlabConnection(BaseConnection):
log.info('Got MR %s#%s', project_name, number)
return mr
def commentMR(self, project_name, number, message, event=None):
log = get_annotated_logger(self.log, event)
self.gl_client.comment_mr(
project_name, number, message, zuul_event_id=event)
log.info("Commented on MR %s#%s", project_name, number)
class GitlabWebController(BaseWebController):

View File

@ -16,6 +16,8 @@ import logging
import voluptuous as v
from zuul.reporter import BaseReporter
from zuul.lib.logutil import get_annotated_logger
from zuul.driver.gitlab.gitlabsource import GitlabSource
class GitlabReporter(BaseReporter):
@ -26,10 +28,30 @@ class GitlabReporter(BaseReporter):
def __init__(self, driver, connection, pipeline, config=None):
super(GitlabReporter, self).__init__(driver, connection, config)
self._create_comment = self.config.get('comment', True)
def report(self, item):
"""Report on an event."""
raise NotImplementedError()
if not isinstance(item.change.project.source, GitlabSource):
return
if item.change.project.source.connection.canonical_hostname != \
self.connection.canonical_hostname:
return
if hasattr(item.change, 'number'):
if self._create_comment:
self.addMRComment(item)
def addMRComment(self, item, comment=None):
log = get_annotated_logger(self.log, item.event)
message = comment or self._formatItemReport(item)
project = item.change.project.name
pr_number = item.change.number
log.debug('Reporting change %s, params %s, message: %s',
item.change, self.config, message)
self.connection.commentMR(project, pr_number, message,
event=item.event)
def mergePull(self, item):
raise NotImplementedError()
@ -39,4 +61,7 @@ class GitlabReporter(BaseReporter):
def getSchema():
return v.Schema({})
gitlab_reporter = v.Schema({
'comment': bool,
})
return gitlab_reporter