Webhook.rst
1 Webhook 2 ======= 3 4 Creating and Listening to Webhooks with PyGithub and Pyramid 5 ------------------------------------------------------------ 6 7 To receive a continuous stream of events, one can set up a wsgiref app using Pyramid to handle 8 incoming POST requests. 9 10 The below code sets up a listener which creates and utilizes a webhook. Using 11 'pull_request' and 'push' for the EVENT attributes, any time a PR is opened, closed, merged, or synced, or a commit is pushed, 12 Github sends a POST containing a payload with information about the PR/push and its state. 13 14 The below example was drawn largely from `Github's Examples <https://github.com/github/platform-samples/blob/master/api/python/building-a-ci-server/server.py>`__ 15 on working with Webhooks. A list of all applicable event types for Webhooks can be found in `Github's documentation <https://developer.github.com/v3/issues/events/>`__ 16 17 .. code-block:: python 18 19 from wsgiref.simple_server import make_server 20 from pyramid.config import Configurator 21 from pyramid.view import view_config, view_defaults 22 from pyramid.response import Response 23 from github import Github 24 25 ENDPOINT = "webhook" 26 27 @view_defaults( 28 route_name=ENDPOINT, renderer="json", request_method="POST" 29 ) 30 class PayloadView(object): 31 """ 32 View receiving of Github payload. By default, this view it's fired only if 33 the request is json and method POST. 34 """ 35 36 def __init__(self, request): 37 self.request = request 38 # Payload from Github, it's a dict 39 self.payload = self.request.json 40 41 @view_config(header="X-Github-Event:push") 42 def payload_push(self): 43 """This method is a continuation of PayloadView process, triggered if 44 header HTTP-X-Github-Event type is Push""" 45 print("No. commits in push:", len(self.payload['commits'])) 46 return Response("success") 47 48 @view_config(header="X-Github-Event:pull_request") 49 def payload_pull_request(self): 50 """This method is a continuation of PayloadView process, triggered if 51 header HTTP-X-Github-Event type is Pull Request""" 52 print("PR", self.payload['action']) 53 print("No. Commits in PR:", self.payload['pull_request']['commits']) 54 55 return Response("success") 56 57 @view_config(header="X-Github-Event:ping") 58 def payload_else(self): 59 print("Pinged! Webhook created with id {}!".format(self.payload["hook"]["id"])) 60 return {"status": 200} 61 62 63 def create_webhook(): 64 """ Creates a webhook for the specified repository. 65 66 This is a programmatic approach to creating webhooks with PyGithub's API. If you wish, this can be done 67 manually at your repository's page on Github in the "Settings" section. There is a option there to work with 68 and configure Webhooks. 69 """ 70 71 USERNAME = "" 72 PASSWORD = "" 73 OWNER = "" 74 REPO_NAME = "" 75 EVENTS = ["push", "pull_request"] 76 HOST = "" 77 78 config = { 79 "url": "http://{host}/{endpoint}".format(host=HOST, endpoint=ENDPOINT), 80 "content_type": "json" 81 } 82 83 g = Github(USERNAME, PASSWORD) 84 repo = g.get_repo("{owner}/{repo_name}".format(owner=OWNER, repo_name=REPO_NAME)) 85 repo.create_hook("web", config, EVENTS, active=True) 86 87 88 if __name__ == "__main__": 89 config = Configurator() 90 91 create_webhook() 92 93 config.add_route(ENDPOINT, "/{}".format(ENDPOINT)) 94 config.scan() 95 96 app = config.make_wsgi_app() 97 server = make_server("0.0.0.0", 80, app) 98 server.serve_forever() 99 100 101 Outputs from a server configured as above: 102 103 .. code-block:: console 104 105 x.y.w.z - - [15/Oct/2018 23:49:19] "POST /webhook HTTP/1.1" 200 15 106 Pinged! Webhook created with id <redacted id>! 107 No. commits in push: 1 108 x.y.w.z - - [15/Oct/2018 23:49:32] "POST /webhook HTTP/1.1" 200 7 109 PR synchronize 110 x.y.w.z - - [15/Oct/2018 23:49:33] "POST /webhook HTTP/1.1" 200 7 111 No. Commits in PR: 10 112 PR closed 113 x.y.w.z - - [15/Oct/2018 23:49:56] "POST /webhook HTTP/1.1" 200 7 114 No. Commits in PR: 10 115 x.y.w.z - - [15/Oct/2018 23:50:00] "POST /webhook HTTP/1.1" 200 7 116 PR reopened 117 No. Commits in PR: 10