Support regex matching of github status
The github status requirements matching and trigger filter are currently plain text matching based. This currently limits sharing of pipeline definitions between tenants as zuul reports the status as '<tenant>/<pipeline>'. This currently makes it necessary to define trigger filter for each tenant [1] and completely blocks pipeline requirements. A solution to this is regex matching which makes it possible to define the filter once [2]. Further this enables an interesting further use case to trigger on any successfull status [3]. This makes it easier to cooperate with other CI systems or github apps which also set a status. Directly use re2 as this will be used in the future for regex matching. [1] Trigger filter snippet trigger: github: - event: pull_request action: status status: - zuul:tenant1/check:success - zuul:tenant2/check:success - zuul:tenant3/check:success - zuul:tenant4/check:success [2] Regex trigger filter snippet trigger: github: - event: pull_request action: status status: - zuul:.+/check:success [3] Generic success filter snippet trigger: github: - event: pull_request action: status status: - .*:success Change-Id: Id1b9d7334db78d0f13db33d47a80ffdb65f921df
This commit is contained in:
parent
42e35c2adf
commit
0c3b8fb963
|
@ -18,3 +18,5 @@ python3-dev [platform:dpkg]
|
|||
python3-devel [platform:rpm]
|
||||
bubblewrap [platform:rpm]
|
||||
redhat-rpm-config [platform:rpm]
|
||||
libre2-dev [platform:dpkg]
|
||||
re2-devel [platform:rpm]
|
||||
|
|
|
@ -219,7 +219,8 @@ the following options.
|
|||
|
||||
.. value:: status
|
||||
|
||||
Status set on commit.
|
||||
Status set on commit. The syntax is ``user:status:value``.
|
||||
This also can be a regular expression.
|
||||
|
||||
A :value:`pipeline.trigger.<github
|
||||
source>.event.pull_request_review` event will have associated
|
||||
|
@ -420,7 +421,8 @@ enqueued into the pipeline.
|
|||
.. attr:: status
|
||||
|
||||
A string value that corresponds with the status of the pull
|
||||
request. The syntax is ``user:status:value``.
|
||||
request. The syntax is ``user:status:value``. This can also
|
||||
be a regular expression.
|
||||
|
||||
.. attr:: label
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
The GitHub trigger status filter
|
||||
:value:`pipeline.trigger.<github source>.action.status` and pipeline
|
||||
requirements :attr:`pipeline.require.<github source>.status` now support
|
||||
regular expression matching.
|
|
@ -24,3 +24,4 @@ iso8601
|
|||
aiohttp<3.0.0
|
||||
uvloop;python_version>='3.5'
|
||||
psutil
|
||||
fb-re2>=1.0.6
|
||||
|
|
|
@ -13,6 +13,21 @@
|
|||
github:
|
||||
comment: true
|
||||
|
||||
- pipeline:
|
||||
name: pipeline_regex
|
||||
manager: independent
|
||||
require:
|
||||
github:
|
||||
status: zuul:.*:success
|
||||
trigger:
|
||||
github:
|
||||
- event: pull_request
|
||||
action: comment
|
||||
comment: test regex
|
||||
success:
|
||||
github:
|
||||
comment: true
|
||||
|
||||
- pipeline:
|
||||
name: reject_status
|
||||
manager: independent
|
||||
|
@ -28,6 +43,21 @@
|
|||
github:
|
||||
comment: true
|
||||
|
||||
- pipeline:
|
||||
name: reject_status_regex
|
||||
manager: independent
|
||||
reject:
|
||||
github:
|
||||
status: zuul:.*/check:error
|
||||
trigger:
|
||||
github:
|
||||
- event: pull_request
|
||||
action: comment
|
||||
comment: test regex
|
||||
success:
|
||||
github:
|
||||
comment: true
|
||||
|
||||
- pipeline:
|
||||
name: trigger_status
|
||||
manager: independent
|
||||
|
@ -37,6 +67,10 @@
|
|||
action: comment
|
||||
comment: trigger me
|
||||
require-status: zuul:tenant-one/check:success
|
||||
- event: pull_request
|
||||
action: comment
|
||||
comment: trigger regex
|
||||
require-status: zuul:.*:success
|
||||
success:
|
||||
github:
|
||||
comment: true
|
||||
|
@ -48,7 +82,14 @@
|
|||
github:
|
||||
- event: pull_request
|
||||
action: status
|
||||
status: zuul:tenant-one/check:success
|
||||
status:
|
||||
# first line is to check if a list works here
|
||||
- dummy:tenant-one/check:success
|
||||
- zuul:tenant-one/check:success
|
||||
- event: pull_request
|
||||
action: status
|
||||
status:
|
||||
- other-ci:.+:success
|
||||
success:
|
||||
github:
|
||||
status: success
|
||||
|
@ -314,6 +355,9 @@
|
|||
pipeline:
|
||||
jobs:
|
||||
- project1-pipeline
|
||||
pipeline_regex:
|
||||
jobs:
|
||||
- project1-pipeline
|
||||
trigger_status:
|
||||
jobs:
|
||||
- project1-pipeline
|
||||
|
@ -383,6 +427,9 @@
|
|||
reject_status:
|
||||
jobs:
|
||||
- project12-status
|
||||
reject_status_regex:
|
||||
jobs:
|
||||
- project12-status
|
||||
|
||||
- project:
|
||||
name: org/project13
|
||||
|
|
|
@ -49,6 +49,12 @@ class TestGithubRequirements(ZuulTestCase):
|
|||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, 'project1-pipeline')
|
||||
|
||||
# Trigger regex matched status
|
||||
self.fake_github.emitEvent(A.getCommentAddedEvent('test regex'))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, 'project1-pipeline')
|
||||
|
||||
@simple_layout('layouts/requirements-github.yaml', driver='github')
|
||||
def test_trigger_require_status(self):
|
||||
"Test trigger requirement: status"
|
||||
|
@ -76,6 +82,11 @@ class TestGithubRequirements(ZuulTestCase):
|
|||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, 'project1-pipeline')
|
||||
|
||||
self.fake_github.emitEvent(A.getCommentAddedEvent('trigger regex'))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, 'project1-pipeline')
|
||||
|
||||
@simple_layout('layouts/requirements-github.yaml', driver='github')
|
||||
def test_trigger_on_status(self):
|
||||
"Test trigger on: status"
|
||||
|
@ -118,6 +129,13 @@ class TestGithubRequirements(ZuulTestCase):
|
|||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
|
||||
# A success status with a regex match goes in
|
||||
self.fake_github.emitEvent(A.getCommitStatusEvent('cooltest',
|
||||
user='other-ci'))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, 'project2-trigger')
|
||||
|
||||
@simple_layout('layouts/requirements-github.yaml', driver='github')
|
||||
def test_pipeline_require_review_username(self):
|
||||
"Test pipeline requirement: review username"
|
||||
|
@ -521,6 +539,12 @@ class TestGithubRequirements(ZuulTestCase):
|
|||
# Status should cause it to be rejected
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
# Test that also the regex matched pipeline doesn't trigger
|
||||
self.fake_github.emitEvent(A.getCommentAddedEvent('test regex'))
|
||||
self.waitUntilSettled()
|
||||
# Status should cause it to be rejected
|
||||
self.assertEqual(len(self.history), 0)
|
||||
|
||||
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
|
||||
context='tenant-one/check')
|
||||
# Now that status is not error, it should be enqueued
|
||||
|
@ -528,3 +552,9 @@ class TestGithubRequirements(ZuulTestCase):
|
|||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 1)
|
||||
self.assertEqual(self.history[0].name, 'project12-status')
|
||||
|
||||
# Test that also the regex matched pipeline triggers now
|
||||
self.fake_github.emitEvent(A.getCommentAddedEvent('test regex'))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(len(self.history), 2)
|
||||
self.assertEqual(self.history[1].name, 'project12-status')
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import copy
|
||||
import re
|
||||
import re2
|
||||
import time
|
||||
|
||||
from zuul.model import Change, TriggerEvent, EventFilter, RefFilter
|
||||
|
@ -171,16 +172,20 @@ class GithubCommonFilter(object):
|
|||
# statuses and the filter statuses are a null intersection, there
|
||||
# are no matches and we return false
|
||||
if self.required_statuses:
|
||||
if set(change.status).isdisjoint(set(self.required_statuses)):
|
||||
return False
|
||||
for required_status in self.required_statuses:
|
||||
for status in change.status:
|
||||
if re2.fullmatch(required_status, status):
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
|
||||
def matchesNoRejectStatuses(self, change):
|
||||
# statuses are ANDed
|
||||
# If any of the rejected statusses are present, we return false
|
||||
for rstatus in self.reject_statuses:
|
||||
if rstatus in change.status:
|
||||
return False
|
||||
for status in change.status:
|
||||
if re2.fullmatch(rstatus, status):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
@ -299,8 +304,14 @@ class GithubEventFilter(EventFilter, GithubCommonFilter):
|
|||
return False
|
||||
|
||||
# statuses are ORed
|
||||
if self.statuses and event.status not in self.statuses:
|
||||
return False
|
||||
if self.statuses:
|
||||
status_found = False
|
||||
for status in self.statuses:
|
||||
if re2.fullmatch(status, event.status):
|
||||
status_found = True
|
||||
break
|
||||
if not status_found:
|
||||
return False
|
||||
|
||||
if not self.matchesStatuses(change):
|
||||
return False
|
||||
|
|
Loading…
Reference in New Issue