Repository: buildbot/buildbot

Issue:
Make GitHub hook listen to other kind of events as well
This ticket is a migrated Trac ticket [2887](http://trac.buildbot.net/ticket/2887)

People contributed to the original ticket: @djmitche, @sa2ajj
Ticket created on: `Sep 07 2014`
Ticket last modified on: `Jun 22 2015`

---

That would allow to build around Buildbot systems aware of a particular workflow.

Cf.: [[Bors] and [https://github.com/barosl/homu Homu|https://github.com/graydon/bors]]


---

_Comment from_: @dustin
_Date_: `Sep 07 2014`

This would be a powerful improvement, if the events and reactions to them were easily configurable.




Discussion:
would also be nice if the event was available as a property for the build (pr vs push vs comment for eg)

Resolving patch:
diff --git a/master/buildbot/www/hooks/bitbucket.py b/master/buildbot/www/hooks/bitbucket.py
--- a/master/buildbot/www/hooks/bitbucket.py
+++ b/master/buildbot/www/hooks/bitbucket.py
@@ -23,6 +23,10 @@
 
 from twisted.python import log
 
+from buildbot.util import bytes2NativeString
+
+_HEADER_EVENT = b'X-Event-Key'
+
 
 def getChanges(request, options=None):
     """Catch a POST request from BitBucket and start a build process
@@ -34,6 +38,8 @@ def getChanges(request, options=None):
     :param options: additional options
     """
 
+    event_type = request.getHeader(_HEADER_EVENT)
+    event_type = bytes2NativeString(event_type)
     payload = json.loads(request.args['payload'][0])
     repo_url = '%s%s' % (
         payload['canon_url'], payload['repository']['absolute_url'])
@@ -50,7 +56,10 @@ def getChanges(request, options=None):
             'branch': commit['branch'],
             'revlink': '%scommits/%s' % (repo_url, commit['raw_node']),
             'repository': repo_url,
-            'project': project
+            'project': project,
+            'properties': {
+                'event': event_type,
+            },
         })
         log.msg('New revision: %s' % (commit['node'],))
 
diff --git a/master/buildbot/www/hooks/github.py b/master/buildbot/www/hooks/github.py
--- a/master/buildbot/www/hooks/github.py
+++ b/master/buildbot/www/hooks/github.py
@@ -64,7 +64,7 @@ def process(self, request):
         if handler is None:
             raise ValueError('Unknown event: {}'.format(event_type))
 
-        return handler(payload)
+        return handler(payload, event_type)
 
     def _get_payload(self, request):
         content = request.content.read()
@@ -108,10 +108,10 @@ def _get_payload(self, request):
 
         return payload
 
-    def handle_ping(self, _):
+    def handle_ping(self, _, __):
         return [], 'git'
 
-    def handle_push(self, payload):
+    def handle_push(self, payload, event):
         # This field is unused:
         user = None
         # user = payload['pusher']['name']
@@ -121,13 +121,14 @@ def handle_push(self, payload):
         # project = request.args.get('project', [''])[0]
         project = payload['repository']['full_name']
 
-        changes = self._process_change(payload, user, repo, repo_url, project)
+        changes = self._process_change(payload, user, repo, repo_url, project,
+                                       event)
 
         log.msg("Received {} changes from github".format(len(changes)))
 
         return changes, 'git'
 
-    def handle_pull_request(self, payload):
+    def handle_pull_request(self, payload, event):
         changes = []
         number = payload['number']
         refname = 'refs/pull/{}/merge'.format(number)
@@ -152,7 +153,10 @@ def handle_pull_request(self, payload):
             # TODO: Get author name based on login id using txgithub module
             'author': payload['sender']['login'],
             'comments': 'GitHub Pull Request #{} ({} commit{})'.format(
-                number, commits, 's' if commits != 1 else '')
+                number, commits, 's' if commits != 1 else ''),
+            'properties': {
+                'event': event,
+            },
         }
 
         if callable(self._codebase):
@@ -166,7 +170,7 @@ def handle_pull_request(self, payload):
             len(changes), number))
         return changes, 'git'
 
-    def _process_change(self, payload, user, repo, repo_url, project):
+    def _process_change(self, payload, user, repo, repo_url, project, event):
         """
         Consumes the JSON as a python object and actually starts the build.
 
@@ -209,7 +213,10 @@ def _process_change(self, payload, user, repo, repo_url, project):
                 'revlink': commit['url'],
                 'repository': repo_url,
                 'project': project,
-                'properties': {'github_distinct': commit.get('distinct', True)}
+                'properties': {
+                    'github_distinct': commit.get('distinct', True),
+                    'event': event,
+                },
             }
 
             if callable(self._codebase):
diff --git a/master/buildbot/www/hooks/gitlab.py b/master/buildbot/www/hooks/gitlab.py
--- a/master/buildbot/www/hooks/gitlab.py
+++ b/master/buildbot/www/hooks/gitlab.py
@@ -23,11 +23,14 @@
 
 from twisted.python import log
 
+from buildbot.util import bytes2NativeString
 
+_HEADER_EVENT = b'X-Gitlab-Event'
 _HEADER_GITLAB_TOKEN = b'X-Gitlab-Token'
 
 
-def _process_change(payload, user, repo, repo_url, project, codebase=None):
+def _process_change(payload, user, repo, repo_url, project, event,
+                    codebase=None):
     """
     Consumes the JSON as a python object and actually starts the build.
 
@@ -74,7 +77,10 @@ def _process_change(payload, user, repo, repo_url, project, codebase=None):
             'branch': branch,
             'revlink': commit['url'],
             'repository': repo_url,
-            'project': project
+            'project': project,
+            'properties': {
+                'event': event,
+            },
         }
 
         if codebase is not None:
@@ -102,6 +108,8 @@ def getChanges(request, options=None):
         payload = json.load(request.content)
     except Exception as e:
         raise ValueError("Error loading JSON: " + str(e))
+    event_type = request.getHeader(_HEADER_EVENT)
+    event_type = bytes2NativeString(event_type)
     user = payload['user_name']
     repo = payload['repository']['name']
     repo_url = payload['repository']['url']
@@ -112,6 +120,6 @@ def getChanges(request, options=None):
     # This field is unused:
     # private = payload['repository']['private']
     changes = _process_change(
-        payload, user, repo, repo_url, project, codebase=codebase)
+        payload, user, repo, repo_url, project, event_type, codebase=codebase)
     log.msg("Received %s changes from gitlab" % len(changes))
     return (changes, 'git')


Test patch:
diff --git a/master/buildbot/test/unit/test_www_hooks_bitbucket.py b/master/buildbot/test/unit/test_www_hooks_bitbucket.py
--- a/master/buildbot/test/unit/test_www_hooks_bitbucket.py
+++ b/master/buildbot/test/unit/test_www_hooks_bitbucket.py
@@ -25,6 +25,7 @@
 import buildbot.www.change_hook as change_hook
 from buildbot.test.fake.web import FakeRequest
 from buildbot.test.fake.web import fakeMasterForHooks
+from buildbot.www.hooks.bitbucket import _HEADER_EVENT
 
 
 gitJsonPayload = """{
@@ -152,6 +153,7 @@ def testGitWithChange(self):
         change_dict = {'payload': [gitJsonPayload]}
 
         request = FakeRequest(change_dict)
+        request.received_headers[_HEADER_EVENT] = "repo:push"
         request.uri = '/change_hook/bitbucket'
         request.method = 'POST'
 
@@ -179,6 +181,9 @@ def testGitWithChange(self):
             'https://bitbucket.org/marcus/project-x/commits/'
             '620ade18607ac42d872b568bb92acaa9a28620e9'
         )
+        self.assertEqual(
+            commit['properties']['event'],
+            'repo:push')
 
     @inlineCallbacks
     def testGitWithNoCommitsPayload(self):
@@ -198,6 +203,7 @@ def testMercurialWithChange(self):
         change_dict = {'payload': [mercurialJsonPayload]}
 
         request = FakeRequest(change_dict)
+        request.received_headers[_HEADER_EVENT] = "repo:push"
         request.uri = '/change_hook/bitbucket'
         request.method = 'POST'
 
@@ -225,6 +231,9 @@ def testMercurialWithChange(self):
             'https://bitbucket.org/marcus/project-x/commits/'
             '620ade18607ac42d872b568bb92acaa9a28620e9'
         )
+        self.assertEqual(
+            commit['properties']['event'],
+            'repo:push')
 
     @inlineCallbacks
     def testMercurialWithNoCommitsPayload(self):
diff --git a/master/buildbot/test/unit/test_www_hooks_github.py b/master/buildbot/test/unit/test_www_hooks_github.py
--- a/master/buildbot/test/unit/test_www_hooks_github.py
+++ b/master/buildbot/test/unit/test_www_hooks_github.py
@@ -448,6 +448,7 @@ def _check_git_with_change(self, payload):
         self.assertEqual(change["revlink"],
                          "http://github.com/defunkt/github/commit/"
                          "de8251ff97ee194a289832576287d6f8ad74e3d0")
+        self.assertEqual(change["properties"]["event"], "push")
 
     def test_git_with_change_encoded(self):
         self._check_git_with_change([gitJsonPayload])
@@ -569,6 +570,7 @@ def _check_git_with_pull(self, payload):
         self.assertEqual(change["branch"], "refs/pull/50/merge")
         self.assertEqual(change["revlink"],
                          "https://github.com/defunkt/github/pull/50")
+        self.assertEqual(change["properties"]["event"], "pull_request")
 
     def test_git_with_pull_encoded(self):
         self._check_git_with_pull([gitJsonPayloadPullRequest])
diff --git a/master/buildbot/test/unit/test_www_hooks_gitlab.py b/master/buildbot/test/unit/test_www_hooks_gitlab.py
--- a/master/buildbot/test/unit/test_www_hooks_gitlab.py
+++ b/master/buildbot/test/unit/test_www_hooks_gitlab.py
@@ -26,6 +26,7 @@
 import buildbot.www.change_hook as change_hook
 from buildbot.test.fake.web import FakeRequest
 from buildbot.test.fake.web import fakeMasterForHooks
+from buildbot.www.hooks.gitlab import _HEADER_EVENT
 from buildbot.www.hooks.gitlab import _HEADER_GITLAB_TOKEN
 
 
@@ -220,6 +221,17 @@ def check_changes(r):
         d.addCallback(check_changes)
         return d
 
+    @defer.inlineCallbacks
+    def test_event_property(self):
+        self.request = FakeRequest(content=gitJsonPayload)
+        self.request.received_headers[_HEADER_EVENT] = "Push Hook"
+        self.request.uri = "/change_hook/gitlab"
+        self.request.method = "POST"
+        yield self.request.test_render(self.changeHook)
+        self.assertEqual(len(self.changeHook.master.addedChanges), 2)
+        change = self.changeHook.master.addedChanges[0]
+        self.assertEqual(change["properties"]["event"], "Push Hook")
+
 
 class TestChangeHookConfiguredWithSecret(unittest.TestCase):