Skip to content

Instantly share code, notes, and snippets.

@nhomar
Created August 31, 2014 19:37
Show Gist options
  • Save nhomar/94b1e670371e95cfd51c to your computer and use it in GitHub Desktop.
Save nhomar/94b1e670371e95cfd51c to your computer and use it in GitHub Desktop.
Implementing github hooks
import argparse
import hashlib
import hmac
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import pprint
import os
import sys
# It's not recommended to store the key within the code. Following
# http://12factor.net/config, we'll store this in the environment.
# Note that the key must be a bytes object.
HOOK_SECRET_KEY = os.environb[b'HOOK_SECRET_KEY']
class GithubHookHandler(BaseHTTPRequestHandler):
"""Base class for webhook handlers.
Subclass it and implement 'handle_payload'.
"""
def _validate_signature(self, data):
sha_name, signature = self.headers['X-Hub-Signature'].split('=')
if sha_name != 'sha1':
return False
# HMAC requires its key to be bytes, but data is strings.
mac = hmac.new(HOOK_SECRET_KEY, msg=data, digestmod=hashlib.sha1)
return hmac.compare_digest(mac.hexdigest(), signature)
def do_POST(self):
data_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(data_length)
if not self._validate_signature(post_data):
self.send_response(401)
return
payload = json.loads(post_data.decode('utf-8'))
self.handle_payload(payload)
self.send_response(200)
class MyHandler(GithubHookHandler):
def handle_payload(self, json_payload):
"""Simple handler that pretty-prints the payload."""
print('JSON payload')
pprint.pprint(json_payload)
if __name__ == '__main__':
argparser = argparse.ArgumentParser(description='Github hook handler')
argparser.add_argument('port', type=int, help='TCP port to listen on')
args = argparser.parse_args()
server = HTTPServer(('', args.port), MyHandler)
server.serve_forever()
@nhomar
Copy link
Author

nhomar commented Aug 31, 2014

Original Explanation.

the original article is here:

Payload server in Python 3 for Github webhooks
July 9th, 2014 at 5:50 am

The Github Webhooks API is powerful and flexible, making it simple to integrate services with your source repository. Lately I’ve been tinkering with it a bit, but all the examples Github has are in Ruby. So I put together a simple demo server in Python 3. Though simple (it’s completely self contained and only needs Python 3 to run), it’s complete, covering even webhook security by verifying the signature created with the API’s secret token.

Just run it at some port on your server and point the webhook you create to it. Currently it just runs on the server’s root path (e.g. http://myserver.com:1234), but should be trivial to modify to any path.

By the way, I found ngrok to be invaluable for testing this. It creates a tunnel from your localhost’s port to a unique URL you can set as the webhook destination on Github. This makes it possible to quickly iterate and test the server on your local machine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment