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:
parent
f1368e84ec
commit
e20d254e6d
|
@ -1584,6 +1584,18 @@ class FakeGitlabAPIClient(gitlabconnection.GitlabAPIClient):
|
||||||
if match:
|
if match:
|
||||||
return [{'name': 'master'}], 200, "", "GET"
|
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):
|
class GitlabChangeReference(git.Reference):
|
||||||
_common_path_default = "refs/merge-requests"
|
_common_path_default = "refs/merge-requests"
|
||||||
|
@ -1612,6 +1624,7 @@ class FakeGitlabMergeRequest(object):
|
||||||
self.merge_status = 'can_be_merged'
|
self.merge_status = 'can_be_merged'
|
||||||
self.uuid = uuid.uuid4().hex
|
self.uuid = uuid.uuid4().hex
|
||||||
self.labels = []
|
self.labels = []
|
||||||
|
self.notes = []
|
||||||
self.upstream_root = upstream_root
|
self.upstream_root = upstream_root
|
||||||
self.url = "https://%s/%s/merge_requests/%s" % (
|
self.url = "https://%s/%s/merge_requests/%s" % (
|
||||||
self.gitlab.server, urllib.parse.quote_plus(
|
self.gitlab.server, urllib.parse.quote_plus(
|
||||||
|
@ -1632,6 +1645,15 @@ class FakeGitlabMergeRequest(object):
|
||||||
def getMRReference(self):
|
def getMRReference(self):
|
||||||
return '%s/head' % self.number
|
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):
|
def _addCommitInMR(self, files=[], reset=False):
|
||||||
repo = self._getRepo()
|
repo = self._getRepo()
|
||||||
ref = repo.references[self.getMRReference()]
|
ref = repo.references[self.getMRReference()]
|
||||||
|
|
|
@ -6,6 +6,15 @@
|
||||||
- event: gl_merge_request
|
- event: gl_merge_request
|
||||||
action:
|
action:
|
||||||
- opened
|
- opened
|
||||||
|
start:
|
||||||
|
gitlab:
|
||||||
|
comment: True
|
||||||
|
success:
|
||||||
|
gitlab:
|
||||||
|
comment: True
|
||||||
|
failure:
|
||||||
|
gitlab:
|
||||||
|
comment: True
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: base
|
name: base
|
||||||
|
@ -16,8 +25,13 @@
|
||||||
name: project-test1
|
name: project-test1
|
||||||
run: playbooks/project-test1.yaml
|
run: playbooks/project-test1.yaml
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: project-test2
|
||||||
|
run: playbooks/project-test2.yaml
|
||||||
|
|
||||||
- project:
|
- project:
|
||||||
name: org/project
|
name: org/project
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- project-test1
|
- project-test1
|
||||||
|
- project-test2
|
||||||
|
|
|
@ -12,11 +12,14 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import re
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from tests.base import ZuulTestCase, simple_layout
|
from tests.base import ZuulTestCase, simple_layout
|
||||||
from tests.base import ZuulWebFixture
|
from tests.base import ZuulWebFixture
|
||||||
|
|
||||||
|
from testtools.matchers import MatchesRegex
|
||||||
|
|
||||||
|
|
||||||
class TestGitlabWebhook(ZuulTestCase):
|
class TestGitlabWebhook(ZuulTestCase):
|
||||||
config_file = 'zuul-gitlab-driver.conf'
|
config_file = 'zuul-gitlab-driver.conf'
|
||||||
|
@ -67,3 +70,42 @@ class TestGitlabWebhook(ZuulTestCase):
|
||||||
|
|
||||||
self.assertEqual('SUCCESS',
|
self.assertEqual('SUCCESS',
|
||||||
self.getJobFromHistory('project-test1').result)
|
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))
|
||||||
|
|
|
@ -184,7 +184,7 @@ class GitlabAPIClient():
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.baseurl = '%s/api/v4/' % baseurl
|
self.baseurl = '%s/api/v4/' % baseurl
|
||||||
self.api_token = api_token
|
self.api_token = api_token
|
||||||
self.headers = {'Authorization': 'Authorization: Bearer %s' % (
|
self.headers = {'Authorization': 'Bearer %s' % (
|
||||||
self.api_token)}
|
self.api_token)}
|
||||||
|
|
||||||
def _manage_error(self, data, code, url, verb, zuul_event_id=None):
|
def _manage_error(self, data, code, url, verb, zuul_event_id=None):
|
||||||
|
@ -204,6 +204,15 @@ class GitlabAPIClient():
|
||||||
ret.status_code, ret.text))
|
ret.status_code, ret.text))
|
||||||
return ret.json(), ret.status_code, ret.url, 'GET'
|
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
|
# https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr
|
||||||
def get_mr(self, project_name, number, zuul_event_id=None):
|
def get_mr(self, project_name, number, zuul_event_id=None):
|
||||||
path = "/projects/%s/merge_requests/%s" % (
|
path = "/projects/%s/merge_requests/%s" % (
|
||||||
|
@ -220,6 +229,17 @@ class GitlabAPIClient():
|
||||||
self._manage_error(*resp, zuul_event_id=zuul_event_id)
|
self._manage_error(*resp, zuul_event_id=zuul_event_id)
|
||||||
return [branch['name'] for branch in resp[0]]
|
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):
|
class GitlabConnection(BaseConnection):
|
||||||
driver_name = 'gitlab'
|
driver_name = 'gitlab'
|
||||||
|
@ -393,6 +413,12 @@ class GitlabConnection(BaseConnection):
|
||||||
log.info('Got MR %s#%s', project_name, number)
|
log.info('Got MR %s#%s', project_name, number)
|
||||||
return mr
|
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):
|
class GitlabWebController(BaseWebController):
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ import logging
|
||||||
import voluptuous as v
|
import voluptuous as v
|
||||||
|
|
||||||
from zuul.reporter import BaseReporter
|
from zuul.reporter import BaseReporter
|
||||||
|
from zuul.lib.logutil import get_annotated_logger
|
||||||
|
from zuul.driver.gitlab.gitlabsource import GitlabSource
|
||||||
|
|
||||||
|
|
||||||
class GitlabReporter(BaseReporter):
|
class GitlabReporter(BaseReporter):
|
||||||
|
@ -26,10 +28,30 @@ class GitlabReporter(BaseReporter):
|
||||||
|
|
||||||
def __init__(self, driver, connection, pipeline, config=None):
|
def __init__(self, driver, connection, pipeline, config=None):
|
||||||
super(GitlabReporter, self).__init__(driver, connection, config)
|
super(GitlabReporter, self).__init__(driver, connection, config)
|
||||||
|
self._create_comment = self.config.get('comment', True)
|
||||||
|
|
||||||
def report(self, item):
|
def report(self, item):
|
||||||
"""Report on an event."""
|
"""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):
|
def mergePull(self, item):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -39,4 +61,7 @@ class GitlabReporter(BaseReporter):
|
||||||
|
|
||||||
|
|
||||||
def getSchema():
|
def getSchema():
|
||||||
return v.Schema({})
|
gitlab_reporter = v.Schema({
|
||||||
|
'comment': bool,
|
||||||
|
})
|
||||||
|
return gitlab_reporter
|
||||||
|
|
Loading…
Reference in New Issue