diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst index 92b7a6fa80..416174dd2a 100644 --- a/doc/source/zuul.rst +++ b/doc/source/zuul.rst @@ -246,6 +246,10 @@ explanation of each of the parameters:: ``code-review: 2`` matches a ``+2`` vote on the code review category. Multiple approvals may be listed. + *email_filter* + This is used for any event. It takes a regex applied on the performer + email. Example: ``email_filter: .*@example.org$``. + *comment_filter* This is only used for ``comment-added`` events. It accepts a list of regexes that are searched for in the comment string. If any of these diff --git a/etc/layout.yaml-sample b/etc/layout.yaml-sample index 2012e749c5..eec855355f 100644 --- a/etc/layout.yaml-sample +++ b/etc/layout.yaml-sample @@ -8,6 +8,16 @@ pipelines: failure: verified: -1 + - name: tests + manager: IndependentPipelineManager + trigger: + - event: patchset-created + email_filter: ^.*@example.org$ + success: + verified: 1 + failure: + verified: -1 + - name: post manager: IndependentPipelineManager trigger: @@ -35,6 +45,8 @@ jobs: projects: - name: example/project check: + - project-merge + tests: - project-merge: - project-test gate: diff --git a/zuul/model.py b/zuul/model.py index 13981319ea..9ec4a684af 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -520,6 +520,8 @@ class TriggerEvent(object): # common self.type = None self.project_name = None + # Representation of the user account that performed the event. + self.account = None # patchset-created, comment-added, etc. self.change_number = None self.change_url = None @@ -565,7 +567,7 @@ class TriggerEvent(object): class EventFilter(object): def __init__(self, types=[], branches=[], refs=[], approvals={}, - comment_filters=[]): + comment_filters=[], email_filters=[]): self._types = types self._branches = branches self._refs = refs @@ -573,6 +575,7 @@ class EventFilter(object): self.branches = [re.compile(x) for x in branches] self.refs = [re.compile(x) for x in refs] self.comment_filters = [re.compile(x) for x in comment_filters] + self.email_filters = [re.compile(x) for x in email_filters] self.approvals = approvals def __repr__(self): @@ -629,6 +632,19 @@ class EventFilter(object): if self.comment_filters and not matches_comment_filter: return False + # We better have an account provided by Gerrit to do + # email filtering. + if event.account is not None: + # email_filters are ORed + matches_email_filter = False + for email_filter in self.email_filters: + account_email = event.account.get('email') + if (account_email is not None and + email_filter.search(account_email)): + matches_email_filter = True + if self.email_filters and not matches_email_filter: + return False + # approvals are ANDed for category, value in self.approvals.items(): matches_approval = False diff --git a/zuul/scheduler.py b/zuul/scheduler.py index 60e85f3886..31a504e285 100644 --- a/zuul/scheduler.py +++ b/zuul/scheduler.py @@ -99,7 +99,9 @@ class Scheduler(threading.Thread): refs=toList(trigger.get('ref')), approvals=approvals, comment_filters= - toList(trigger.get('comment_filter'))) + toList(trigger.get('comment_filter')), + email_filters= + toList(trigger.get('email_filter'))) manager.event_filters.append(f) for config_job in data['jobs']: diff --git a/zuul/trigger/gerrit.py b/zuul/trigger/gerrit.py index 3a8f104136..892eb3661e 100644 --- a/zuul/trigger/gerrit.py +++ b/zuul/trigger/gerrit.py @@ -59,6 +59,24 @@ class GerritEventConnector(threading.Thread): event.ref = refupdate.get('refName') event.oldrev = refupdate.get('oldRev') event.newrev = refupdate.get('newRev') + # Map the event types to a field name holding a Gerrit + # account attribute. See Gerrit stream-event documentation + # in cmd-stream-events.html + accountfield_from_type = { + 'patchset-created': 'uploader', + 'change-abandoned': 'abandoner', + 'change-restored': 'restorer', + 'change-merged': 'submitter', + 'comment-added': 'author', + 'ref-updated': 'submitter', + } + try: + event.account = data.get(accountfield_from_type[event.type]) + except KeyError: + self.log.error("Received unrecongized event type '%s' from Gerrit.\ + Can not get account information." % event.type) + event.account = None + self.sched.addEvent(event) self.gerrit.eventDone()