Encapsulate determining the event purpose
Github webhook events are pretty detailed, so it's able to distinguish
eg. between opening a new pull request and pushing into already existing
pull request. As this does not map exactly onto gerrit events, provide
a level of abstraction for places where certain kind of events have to
be distinguished.
This causes that dequeue mechanism starts to work with github pull
requests.
Change-Id: I90ef72ccf2d4e669b7e1304e5b9eb351ca9b5b62
diff --git a/tests/fixtures/layouts/dequeue-github.yaml b/tests/fixtures/layouts/dequeue-github.yaml
new file mode 100644
index 0000000..25e92c9
--- /dev/null
+++ b/tests/fixtures/layouts/dequeue-github.yaml
@@ -0,0 +1,18 @@
+- pipeline:
+ name: check
+ manager: independent
+ trigger:
+ github:
+ - event: pull_request
+ action:
+ - opened
+ - changed
+
+- job:
+ name: one-job-project-merge
+
+- project:
+ name: org/one-job-project
+ check:
+ jobs:
+ - one-job-project-merge
diff --git a/tests/unit/test_github_driver.py b/tests/unit/test_github_driver.py
index c7c5f3a..97a0066 100644
--- a/tests/unit/test_github_driver.py
+++ b/tests/unit/test_github_driver.py
@@ -15,6 +15,7 @@
import logging
import re
from testtools.matchers import MatchesRegex
+import time
from tests.base import ZuulTestCase, simple_layout, random_sha1
@@ -138,6 +139,47 @@
self.assertEqual(2, len(self.history))
self.assertEqual(['other label'], C.labels)
+ @simple_layout('layouts/dequeue-github.yaml', driver='github')
+ def test_dequeue_pull_synchronized(self):
+ self.executor_server.hold_jobs_in_build = True
+
+ pr = self.fake_github.openFakePullRequest(
+ 'org/one-job-project', 'master')
+ self.fake_github.emitEvent(pr.getPullRequestOpenedEvent())
+ self.waitUntilSettled()
+
+ # event update stamp has resolution one second, wait so the latter
+ # one has newer timestamp
+ time.sleep(1)
+ pr.addCommit()
+ self.fake_github.emitEvent(pr.getPullRequestSynchronizeEvent())
+ self.waitUntilSettled()
+
+ self.executor_server.hold_jobs_in_build = False
+ self.executor_server.release()
+ self.waitUntilSettled()
+
+ self.assertEqual(2, len(self.history))
+ self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
+
+ @simple_layout('layouts/dequeue-github.yaml', driver='github')
+ def test_dequeue_pull_abandoned(self):
+ self.executor_server.hold_jobs_in_build = True
+
+ pr = self.fake_github.openFakePullRequest(
+ 'org/one-job-project', 'master')
+ self.fake_github.emitEvent(pr.getPullRequestOpenedEvent())
+ self.waitUntilSettled()
+ self.fake_github.emitEvent(pr.getPullRequestClosedEvent())
+ self.waitUntilSettled()
+
+ self.executor_server.hold_jobs_in_build = False
+ self.executor_server.release()
+ self.waitUntilSettled()
+
+ self.assertEqual(1, len(self.history))
+ self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
+
@simple_layout('layouts/basic-github.yaml', driver='github')
def test_git_https_url(self):
"""Test that git_ssh option gives git url with ssh"""
diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py
index c73b88e..ecf72a7 100644
--- a/zuul/driver/github/githubconnection.py
+++ b/zuul/driver/github/githubconnection.py
@@ -24,7 +24,7 @@
from github3.exceptions import MethodNotAllowed
from zuul.connection import BaseConnection
-from zuul.model import PullRequest, Ref, TriggerEvent
+from zuul.model import PullRequest, Ref, GithubTriggerEvent
from zuul.exceptions import MergeFailure
@@ -77,7 +77,7 @@
body = request.json_body
base_repo = body.get('repository')
- event = TriggerEvent()
+ event = GithubTriggerEvent()
event.trigger_name = 'github'
event.project_name = base_repo.get('full_name')
event.type = 'push'
@@ -175,7 +175,7 @@
return True
def _pull_request_to_event(self, pr_body):
- event = TriggerEvent()
+ event = GithubTriggerEvent()
event.trigger_name = 'github'
base = pr_body.get('base')
diff --git a/zuul/model.py b/zuul/model.py
index a86fbbd..7ed8339 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -1926,6 +1926,25 @@
return ret
+ def isPatchsetCreated(self):
+ return 'patchset-created' == self.type
+
+ def isChangeAbandoned(self):
+ return 'change-abandoned' == self.type
+
+
+class GithubTriggerEvent(TriggerEvent):
+
+ def isPatchsetCreated(self):
+ if self.type == 'pull_request':
+ return self.action in ['opened', 'changed']
+ return False
+
+ def isChangeAbandoned(self):
+ if self.type == 'pull_request':
+ return 'closed' == self.action
+ return False
+
class BaseFilter(object):
"""Base Class for filtering which Changes and Events to process."""
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 53ca4c1..2e9bef2 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -759,9 +759,9 @@
change.project.unparsed_config = None
self.reconfigureTenant(tenant)
for pipeline in tenant.layout.pipelines.values():
- if event.type == 'patchset-created':
+ if event.isPatchsetCreated():
pipeline.manager.removeOldVersionsOfChange(change)
- elif event.type == 'change-abandoned':
+ elif event.isChangeAbandoned():
pipeline.manager.removeAbandonedChange(change)
if pipeline.manager.eventMatches(event, change):
pipeline.manager.addChange(change)