Permalink
Browse files

Add GitHub Webhooks listener for continuous deployment

  • Loading branch information...
puntonim committed Aug 19, 2014
1 parent 2417bd4 commit 1771aa8614d82a3faf9467c9c1c60cd62555af05
Showing with 119 additions and 3 deletions.
  1. +99 −0 biostar/server/webhooks_listener.py
  2. +11 −0 biostar/settings/base.py
  3. +5 −2 biostar/urls.py
  4. +4 −1 conf/defaults.env
@@ -0,0 +1,99 @@
import json
import hmac
import hashlib
import subprocess
import logging
from django.conf import settings
from django.http import HttpResponse, Http404
from django.views.decorators.csrf import csrf_exempt
log = logging.getLogger(__name__)
@csrf_exempt
def github(request):
# Only POST requests are served.
if not request.method.upper() == 'POST':
raise Http404
# If no events is enabled, return 404.
if not any(settings.GITHUB_WEBHOOK_EVENTS.values()):
raise Http404
# Read the signature sent by GitHub.
github_signature = request.META.get('HTTP_X_HUB_SIGNATURE', None)
if not github_signature:
raise Http404
# Compute the signature based on the password.
signature = 'sha1=' + hmac.new('{}'.format(settings.GITHUB_WEBHOOK_PASSWORD),
request.body,
hashlib.sha1).hexdigest()
# Compare the signatures.
#if not hmac.compare_digest(signature, github_signature): # Works only for Python >= 2.7.7|3.3
if not signature == github_signature:
raise Http404
# The signature is valid, parse the content.
payload = json.loads(request.body)
log.debug(payload)
event = request.META.get('HTTP_X_GITHUB_EVENT', '') # 'push', 'release', ...
log.info('Received a GitHub Webhook with event={}'.format(event))
handler = {'push': handle_push,
'release': handle_release}.get(event, None)
if handler:
try:
handler(payload)
run_script()
except EventNotMonitored as ex:
log.info('A GitHub Webhook was received but the given event is not '
'monitored. {}'.format(ex))
return HttpResponse('OK')
def handle_push(payload):
if not settings.GITHUB_WEBHOOK_EVENTS.get('push', False):
raise EventNotMonitored('The monitoring of the push event is disabled in the settings.')
pushed_branch = payload['ref']
if not 'refs/heads/{}'.format(settings.GITHUB_WEBHOOK_PUSH_MONITORED_BRANCH) in pushed_branch:
raise EventNotMonitored('Push webhooks received on the branch {}. '
'This branch is not being monitored. '
'The monitored branch is {}.'.format(
pushed_branch,
settings.GITHUB_WEBHOOK_PUSH_MONITORED_BRANCH))
log.info('Push webhooks received on the monitored branch {}.'.format(
settings.GITHUB_WEBHOOK_PUSH_MONITORED_BRANCH))
def handle_release(payload):
if not settings.GITHUB_WEBHOOK_EVENTS.get('release', False):
raise EventNotMonitored('The monitoring of the release event is disabled in the settings.')
tag_name = payload['release']['tag_name']
draft = payload['release']['draft']
if draft:
raise EventNotMonitored('Release webhooks received, but it is a draft release. '
'Draft releases are not monitored. '
'Tag_name={} and draft={}.'.format(tag_name, draft))
log.info('Release webhooks received with tag_name={}.'.format(tag_name))
def run_script():
log.info('Triggering the script: {}'.format(settings.GITHUB_WEBHOOK_SCRIPT_TO_TRIGGER))
subprocess.Popen('{} >>{} 2>&1'.format(
settings.GITHUB_WEBHOOK_SCRIPT_TO_TRIGGER,
settings.GITHUB_WEBHOOK_SCRIPT_LOG_FILE), shell=True)
class EventNotMonitored(Exception):
pass
View
@@ -485,3 +485,14 @@ def abspath(*args):
EMAIL_PORT = get_env("EMAIL_PORT", func=int)
EMAIL_HOST_USER = get_env("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = get_env("EMAIL_HOST_PASSWORD")
# Continuous deployment with GitHub Webhooks.
# Setting all events to False will disable the continuous deployment.
GITHUB_WEBHOOK_EVENTS = {
'push': False,
'release': False,
}
GITHUB_WEBHOOK_PASSWORD = '' # Or: get_env('GITHUB_WEBHOOK_PASSWORD')
GITHUB_WEBHOOK_PUSH_MONITORED_BRANCH = 'master'
GITHUB_WEBHOOK_SCRIPT_TO_TRIGGER = 'path_to_the_script'
GITHUB_WEBHOOK_SCRIPT_LOG_FILE = 'path_to_the_log_file'
View
@@ -6,7 +6,7 @@
admin.autodiscover()
from django.views.generic import TemplateView
from biostar.server import views, ajax, search, moderate, api, orcid
from biostar.server import views, ajax, search, moderate, api, orcid, webhooks_listener
from biostar.apps.posts.views import NewAnswer, NewPost, EditPost, external_post_handler
from biostar.apps.posts import explorer
from biostar.apps.users.views import external_logout, external_login, CaptchaView, EmailListView
@@ -120,6 +120,9 @@
url(r'^api/stats/date/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$',
api.daily_stats_on_date, name='api-stats-on-date'),
# GitHub Webhooks for continuous deployment.
url(r'^github_webhooks/$', webhooks_listener.github, name='github-webhooks'),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
@@ -163,4 +166,4 @@
urlpatterns += patterns('',
url(r'^__debug__/', include(debug_toolbar.urls)),
url(r'^test/login/', test_login),
)
)
View
@@ -58,4 +58,7 @@ export ORCID_PROVIDER_KEY='foobar'
export ORCID_PROVIDER_SECRET_KEY='foobar'
# Variable only used during migration from Biostar 1.0.
export BIOSTAR_MIGRATE_DIR="~/tmp/biostar-migrate"
export BIOSTAR_MIGRATE_DIR="~/tmp/biostar-migrate"
# GitHub Webhook password for continuous deployment.
export GITHUB_WEBHOOK_PASSWORD='foobar'

0 comments on commit 1771aa8

Please sign in to comment.