用 Django 来响应 Github Webhook
由 瞳人
发布于 June 20, 2016, 4:44 p.m.
16 个评论
利用 Django 来响应 Github 的 webhook 请求.
由于本 vps 上的 gitlab 开销大,所以我把所有项目移到 github 上。因此原先针对 gitlab 的 webhook 代码就需要更新了。
关于 github webhook 的介绍,可以看后文的参考链接,这里就简单叙述一下解决方案。
-
当你向你 github 中的 repo 添加一个新的 webhook 时,github 会给你发一个
ping
事件的 post 请求, 这时你就可以在数据库中登记这个webhook。 -
你可以在 github 上设置触发该 webhook 的事件类型,我这里就设置了
push
事件。 -
为了安全起见,github 可以设置一个密钥,然后你可以在响应代码中进行验证。
-
同样的,与之前 gitlab 的 webhook 代码 中一样,我们现在数据库中添加一些需要验证的信息,包括 repository 的名字,ssh clone 地址即 'git@github.com:user/repo.git',secret,repository 的 id 等,但在初始化时我们先把
repo_id
填成-1
,这个值稍后由代码更新。
代码
models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # coding=utf-8 from django.db import models from django.utils.translation import ugettext as _ class Github_Webhook(models.Model): ''' Model for webhook of github ''' repo_name = models.CharField( verbose_name = _(u'repository name'), help_text = _(u' '), max_length = 255 ) repo_ssh_url = models.CharField( verbose_name = _(u'repository ssh_url'), help_text = _(u' '), max_length = 255 ) repo_id = models.IntegerField( verbose_name = _(u'repository id value'), help_text = _(u'Your repository id in github'), default = 0 ) secret = models.CharField( verbose_name = _(u'repository secret'), help_text = _(u' '), max_length = 255 ) def __unicode__(self): return u'%s' % (self.repo_name,) |
views.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | @csrf_exempt def github_webhook(request): if request.method == 'POST' and request.body: http_x_github_event = request.META.get('HTTP_X_GITHUB_EVENT', '') http_x_hub_signature = request.META.get('HTTP_X_HUB_SIGNATURE', '') json_data = json.loads(request.body) repo_data = json_data.get('repository', '') sender_data = json_data.get('sender', '') if repo_data and sender_data and http_x_hub_signature: user_id = sender_data.get('id', '') user_name = sender_data.get('login', '') repo_name = repo_data.get('name', '') repo_id = repo_data.get('id', '') repo_ssh_url = repo_data.get('ssh_url', '') sha_name, signature = http_x_hub_signature.split('=') if sha_name != 'sha1': return HttpResponseForbidden('HeHe!') webhook = Github_Webhook.objects.filter( repo_name=repo_name, repo_ssh_url=repo_ssh_url ).first() if webhook: # HMAC requires the key to be bytes, but data is string mac = hmac.new(str(webhook.secret), msg=request.body, digestmod=sha1) # Python prior to 2.7.7 does not have hmac.compare_digest if hexversion >= 0x020707F0: if not hmac.compare_digest(str(mac.hexdigest()), str(signature)): return HttpResponseForbidden('HeHe!') else: # What compare_digest provides is protection against timing # attacks; we can live without this protection for a web-based # application if not str(mac.hexdigest()) == str(signature): return HttpResponseForbidden('HeHe!') if webhook.repo_id == -1 and http_x_github_event == 'ping': # Save this webhook webhook.repo_id = int(repo_id) webhook.save() content = u'Save github webhook: [%s] by user %s, %s' % ( webhook, user_id, user_name) write_log(content) return HttpResponse('Webhook saved!') if webhook.repo_id == int(repo_id) and http_x_github_event == 'push': # Do your webhook job # such as restarting a docker container. content = u'Restart github webhook: [%s] by user %s, %s' % ( webhook, user_id, user_name) write_log(content) return HttpResponse('Restarted!') return HttpResponseForbidden('HeHe!') |
参考链接

eddie June 23, 2016, 11:26 p.m. | Reply
你好,看了你的《Django 博客评论系统替代多说 (二)》,感觉收获很多。但是遇到一个问题,就是写好之后,runserver,会出现:
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet
网上搜了一圈也没有找到解决方法,如果我把mycomments里面的__init__.py中的内容都删掉,就不会出现上面的错误了,但是会出现别的错误。。。
还望博主不吝赐教,感谢感谢~~

eddie June 27, 2016, 6:53 p.m. | Reply
你好!
我的django版本是1.9.2,python版本是3.3

瞳人 June 28, 2016, 10:50 a.m. | Reply
不好意思啊,我这个评论框好久没有改过了,所以回复起来比较麻烦。。
我django用的是 1.8,python 2。但是看起来不像是版本问题。
你在添加这个 mycomments 这个app 之前都是正常的吗?
是不是在 settings.py 里面加错了 app?

瞳人 June 28, 2016, 10:52 a.m. | Reply
我自己先试一下 python 3 和 django 1.9。。

瞳人 June 28, 2016, 11:14 a.m. | Reply
和 python 3 应该没关系,但是和 django 1.9 有关系。我看见说 In Django 1.9, you shouldn't import models from your app's __init__.py file.
我有空把我的环境升级一下到 django 1.9 看看。

eddie June 28, 2016, 2:02 p.m. | Reply
多谢博主,等待好消息~
我现在随便用了一个友言评论,但是比较死板。还是自己写的靠谱,还能多练练手。

瞳人 June 28, 2016, 4 p.m. | Reply
不用谢。我最近在准备 GRE,所以空余时间不是很多。
我发现一件悲剧的事情,qq邮箱又开始封 mailgun 发送的邮件了。
所以评论不能及时送达我qq邮箱了。
对了,你现在的博客地址是啥,我们交流交流。

瞳人 June 29, 2016, 3:29 p.m. | Reply
我看了一下 django 1.8 是 LTS 版本,而1.9不是,所以我暂时并不打算升级到1.9。因为好久没有维护代码了,我打算先按照 django 1.8 来重构一下代码,然后再看看依据1.9要做些什么改变。

eddie June 29, 2016, 3:40 p.m. | Reply
嗯嗯,我可以把django版本从1.9降回1.8么?

瞳人 June 29, 2016, 3:41 p.m. | Reply
我看你网站是放在 sae 上面的,我不知道这个是怎么做的。你项目本来就是 1.9 的还是 1.8 的?

瞳人 June 30, 2016, 11:46 a.m. | Reply
我今天把博客python环境升级到3.4了,过两天从django1.8升级到1.9

瞳人 June 30, 2016, 7:54 p.m. | Reply
我已经解决了这个问题,这个问题和 python3 没有关系,是和 django 1.9 有关系,请参考文章 http://answ.me/post/comment-in-django-part-2/ 中新的 __init__.py 代码即可。
16 Comments