Merge "Add trigger capability on github pr review" into feature/zuulv3

This commit is contained in:
Jenkins 2017-05-17 23:06:27 +00:00 committed by Gerrit Code Review
commit e35885cbde
7 changed files with 128 additions and 16 deletions

View File

@ -109,7 +109,10 @@ A connection name with the github driver can take multiple events with the
following options.
**event**
The pull request event from github. A ``pull_request`` event will
The event from github. Supported events are ``pull_request``,
``pull_request_review``, and ``push``.
A ``pull_request`` event will
have associated action(s) to trigger from. The supported actions are:
*opened* - pull request opened
@ -126,32 +129,46 @@ following options.
*unlabeled* - label removed from pull request
*review* - review added on pull request
*push* - head reference updated (pushed to branch)
A ``pull_request_review`` event will
have associated action(s) to trigger from. The supported actions are:
*submitted* - pull request review added
*dismissed* - pull request review removed
**branch**
The branch associated with the event. Example: ``master``. This
field is treated as a regular expression, and multiple branches may
be listed. Used for ``pull-request`` events.
be listed. Used for ``pull_request`` and ``pull_request_review`` events.
**comment**
This is only used for ``pull_request`` ``comment`` events. It accepts a list
of regexes that are searched for in the comment string. If any of these
This is only used for ``pull_request`` ``comment`` actions. It accepts a
list of regexes that are searched for in the comment string. If any of these
regexes matches a portion of the comment string the trigger is matched.
``comment: retrigger`` will match when comments containing 'retrigger'
somewhere in the comment text are added to a pull request.
**label**
This is only used for ``labeled`` and ``unlabeled`` actions. It accepts a list
of strings each of which matches the label name in the event literally.
``label: recheck`` will match a ``labeled`` action when pull request is
labeled with a ``recheck`` label. ``label: 'do not test'`` will match a
``unlabeled`` action when a label with name ``do not test`` is removed from
the pull request.
This is only used for ``labeled`` and ``unlabeled`` ``pull_request`` actions.
It accepts a list of strings each of which matches the label name in the
event literally. ``label: recheck`` will match a ``labeled`` action when
pull request is labeled with a ``recheck`` label. ``label: 'do not test'``
will match a ``unlabeled`` action when a label with name ``do not test`` is
removed from the pull request.
Additionally a ``push`` event can be configured, with an ``ref`` field. This
field is treated as a regular expression and multiple refs may be listed.
Github always sends full ref name, eg. ``refs/tags/bar`` and this string is
matched against the regexp.
**state**
This is only used for ``pull_request_review`` events. It accepts a list of
strings each of which is matched to the review state, which can be one of
``approved``, ``comment``, or ``request_changes``.
**ref**
This is only used for ``push`` events. This field is treated as a regular
expression and multiple refs may be listed. Github always sends full ref
name, eg. ``refs/tags/bar`` and this string is matched against the regexp.
GitHub Configuration
~~~~~~~~~~~~~~~~~~~~

View File

@ -617,6 +617,36 @@ class FakeGithubPullRequest(object):
}
return (name, data)
def getReviewAddedEvent(self, review):
name = 'pull_request_review'
data = {
'action': 'submitted',
'pull_request': {
'number': self.number,
'title': self.subject,
'updated_at': self.updated_at,
'base': {
'ref': self.branch,
'repo': {
'full_name': self.project
}
},
'head': {
'sha': self.head_sha
}
},
'review': {
'state': review
},
'repository': {
'full_name': self.project
},
'sender': {
'login': 'ghuser'
}
}
return (name, data)
def addLabel(self, name):
if name not in self.labels:
self.labels.append(name)

View File

@ -0,0 +1,21 @@
- pipeline:
name: reviews
manager: independent
trigger:
github:
- event: pull_request_review
action: submitted
state: 'approve'
success:
github:
label:
- 'tests passed'
- job:
name: project-reviews
- project:
name: org/project
reviews:
jobs:
- project-reviews

View File

@ -177,6 +177,21 @@ class TestGithubDriver(ZuulTestCase):
self.assertEqual(2, len(self.history))
self.assertEqual(['other label'], C.labels)
@simple_layout('layouts/reviews-github.yaml', driver='github')
def test_review_event(self):
A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
self.fake_github.emitEvent(A.getReviewAddedEvent('approve'))
self.waitUntilSettled()
self.assertEqual(1, len(self.history))
self.assertEqual('project-reviews', self.history[0].name)
self.assertEqual(['tests passed'], A.labels)
# test_review_unmatched_event
B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
self.fake_github.emitEvent(B.getReviewAddedEvent('comment'))
self.waitUntilSettled()
self.assertEqual(1, len(self.history))
@simple_layout('layouts/dequeue-github.yaml', driver='github')
def test_dequeue_pull_synchronized(self):
self.executor_server.hold_jobs_in_build = True

View File

@ -143,6 +143,24 @@ class GithubWebhookListener():
event.action = 'comment'
return event
def _event_pull_request_review(self, request):
"""Handles pull request reviews"""
body = request.json_body
pr_body = body.get('pull_request')
if pr_body is None:
return
review = body.get('review')
if review is None:
return
event = self._pull_request_to_event(pr_body)
event.state = review.get('state')
event.account = self._get_sender(body)
event.type = 'pull_request_review'
event.action = body.get('action')
return event
def _issue_to_pull_request(self, body):
number = body.get('issue').get('number')
project_name = body.get('repository').get('full_name')

View File

@ -40,7 +40,8 @@ class GithubTrigger(BaseTrigger):
refs=toList(trigger.get('ref')),
comments=toList(trigger.get('comment')),
labels=toList(trigger.get('label')),
unlabels=toList(trigger.get('unlabel'))
unlabels=toList(trigger.get('unlabel')),
states=toList(trigger.get('state'))
)
efilters.append(f)
@ -57,6 +58,7 @@ def getSchema():
github_trigger = {
v.Required('event'):
toList(v.Any('pull_request',
'pull_request_review',
'push')),
'action': toList(str),
'branch': toList(str),
@ -64,6 +66,7 @@ def getSchema():
'comment': toList(str),
'label': toList(str),
'unlabel': toList(str),
'state': toList(str),
}
return github_trigger

View File

@ -1896,6 +1896,7 @@ class TriggerEvent(object):
self.comment = None
self.label = None
self.unlabel = None
self.state = None
# ref-updated
self.ref = None
self.oldrev = None
@ -2047,7 +2048,7 @@ class EventFilter(BaseFilter):
def __init__(self, trigger, types=[], branches=[], refs=[],
event_approvals={}, comments=[], emails=[], usernames=[],
timespecs=[], required_approvals=[], reject_approvals=[],
pipelines=[], actions=[], labels=[], unlabels=[],
pipelines=[], actions=[], labels=[], unlabels=[], states=[],
ignore_deletes=True):
super(EventFilter, self).__init__(
required_approvals=required_approvals,
@ -2072,6 +2073,7 @@ class EventFilter(BaseFilter):
self.timespecs = timespecs
self.labels = labels
self.unlabels = unlabels
self.states = states
self.ignore_deletes = ignore_deletes
def __repr__(self):
@ -2110,6 +2112,8 @@ class EventFilter(BaseFilter):
ret += ' labels: %s' % ', '.join(self.labels)
if self.unlabels:
ret += ' unlabels: %s' % ', '.join(self.unlabels)
if self.states:
ret += ' states: %s' % ', '.join(self.states)
ret += '>'
return ret
@ -2222,6 +2226,10 @@ class EventFilter(BaseFilter):
if self.unlabels and event.unlabel not in self.unlabels:
return False
# states are ORed
if self.states and event.state not in self.states:
return False
return True