Simple Python WSGI application to handle GitHub webhooks.
git clone https://github.com/carlos-jenkins/python-github-webhooks.git cd python-github-webhooks
sudo pip install -r requirements.txt
You can configure what the application does by copying the sample config file
config.json.sample
to config.json
and adapting it to your needs:
{ "github_ips_only": true, "enforce_secret": "", "return_scripts_info": true "hooks_path": "/absolute/path/to/your/hooks/" }
github_ips_only: | Restrict application to be called only by GitHub IPs. IPs
whitelist is obtained from
GitHub Meta
(endpoint). Default: true . |
---|---|
enforce_secret: | Enforce body signature with HTTP header X-Hub-Signature .
See secret at
GitHub WebHooks Documentation.
Default: '' (do not enforce). |
return_scripts_info: | Return a JSON with the stdout , stderr and exit
code for each executed hook using the hook name as key. If this option is set
you will be able to see the result of your hooks from within your GitHub
hooks configuration page (see "Recent Deliveries").
Default: true . |
hooks_path: | Configures a path to import the hooks. If not set, it'll import the hooks from the default location (/.../python-github-webhooks/hooks) |
This application will execute scripts in the hooks directory using the following order:
hooks/{event}-{name}-{branch}-background hooks/{event}-{name}-{branch} hooks/{event}-{name}-background hooks/{event}-{name} hooks/{event}-background hooks/{event} hooks/all-background hooks/all
Hook files with the "-background" suffix will be spawned asyncronously, and STDOUT/STDERR and return code will not be captured.
The application will pass to the hooks the path to a JSON file holding the payload for the request as first argument. The event type will be passed as second argument. For example:
hooks/push-myrepo-master /tmp/sXFHji push
Hooks can be written in any scripting language as long as the file is executable and has a shebang. A simple example in Python could be:
#!/usr/bin/env python # Python Example for Python GitHub Webhooks # File: push-myrepo-master import sys import json with open(sys.argv[1], 'r') as jsf: payload = json.loads(jsf.read()) ### Do something with the payload name = payload['repository']['name'] outfile = '/tmp/hook-{}.log'.format(name) with open(outfile, 'w') as f: f.write(json.dumps(payload))
Not all events have an associated branch, so a branch-specific hook cannot fire for such events. For events that contain a pull_request object, the base branch (target for the pull request) is used, not the head branch.
Backgrounded hooks are responsible for cleaning up their own temp files, as provided in argv[1]. The duplicate webhook content is required to preent a race condition where the content file is deleted before the child process can read it.
The payload structure depends on the event type. Please review:
https://developer.github.com/v3/activity/events/types/
To deploy in Apache, just add a WSGIScriptAlias
directive to your
VirtualHost file:
<VirtualHost *:80> ServerAdmin you@my.site.com ServerName my.site.com DocumentRoot /var/www/site.com/my/htdocs/ # Handle Github webhook <Directory "/var/www/site.com/my/python-github-webhooks"> Order deny,allow Allow from all </Directory> WSGIScriptAlias /webhooks /var/www/site.com/my/python-github-webhooks/webhooks.py </VirtualHost>
You can now register the hook in your Github repository settings:
https://github.com/youruser/myrepo/settings/hooks
To register the webhook select Content type: application/json
and set the URL to the URL
of your WSGI script:
http://my.site.com/webhooks/
To deploy in a Docker container you have to expose the port 5000, for example with the following command:
git clone http://github.com/carlos-jenkins/python-github-webhooks.git docker build -t carlos-jenkins/python-github-webhooks python-github-webhooks docker run -d --name webhooks -p 5000:5000 carlos-jenkins/python-github-webhooks
You can also mount volume to setup the hooks/
directory, and the file
config.json
:
docker run -d --name webhooks \ -v /path/to/my/hooks:/src/hooks \ -v /path/to/my/config.json:/src/config.json \ -p 5000:5000 python-github-webhooks
To test your hook you may use the GitHub REST API with curl
:
https://developer.github.com/v3/
curl --user "<youruser>" https://api.github.com/repos/<youruser>/<myrepo>/hooks
Take note of the test_url.
curl --user "<youruser>" -i -X POST <test_url>
You should be able to see any log error in your webapp.
When running in Apache, the stderr
of the hooks that return non-zero will
be logged in Apache's error logs. For example:
sudo tail -f /var/log/apache2/error.log
Will log errors in your scripts if printed to stderr
.
You can also launch the Flask web server in debug mode at port 5000
.
python webhooks.py
This can help debug problem with the WSGI application itself.
Copyright (C) 2014-2015 Carlos Jenkins <carlos@jenkins.co.cr> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
This project is just the reinterpretation and merge of two approaches and a modification of Carlos Jenkins' work:
Thanks.