-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhandle_mirror_webhook.py
118 lines (102 loc) · 4.11 KB
/
handle_mirror_webhook.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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import git
import hashlib
import hglib
import hmac
import json
import logging
import os
import requests
from ipaddress import ip_address, ip_network
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, HTTPError
LOCAL_GIT_REPO_PATH = os.environ.get('LOCAL_GIT_REPO_PATH', '/tmp/yt-git')
LOCAL_HG_REPO_PATH = os.environ.get('LOCAL_HG_REPO_PATH', '/tmp/yt-hg')
HG_REPO = os.environ.get('HG_REPO', 'ssh://[email protected]/yt_analysis/yt')
GH_REPO = os.environ.get('GH_REPO', '[email protected]:yt-project/yt.git')
GH_SECRET = os.environ.get('GH_SECRET', 'bla')
@gen.coroutine
def sync_repos():
try:
gh_repo = git.Repo(LOCAL_GIT_REPO_PATH)
except (git.exc.NoSuchPathError, git.exc.InvalidGitRepositoryError):
logging.info('initializing %s' % LOCAL_GIT_REPO_PATH)
gh_repo = git.Repo.init(LOCAL_GIT_REPO_PATH)
gh_repo.create_remote('origin', GH_REPO)
finally:
gh_repo.close()
configs = ['extensions.hggit=']
try:
repo = hglib.open(LOCAL_HG_REPO_PATH, configs=configs)
repo.close()
except hglib.error.ServerError:
logging.info('cloning %s to %s' % (HG_REPO, LOCAL_HG_REPO_PATH))
hglib.clone(source=HG_REPO, dest=LOCAL_HG_REPO_PATH)
# need to do this to ensure the correct hashes in the
# converted git repo since two-way conversion is lossy see
# e.g. https://groups.google.com/forum/#!topic/hg-git/1r1LBrqLeXc
with hglib.open(LOCAL_HG_REPO_PATH, configs=configs) as repo:
logging.info('pushing %s to %s' %
(LOCAL_HG_REPO_PATH, LOCAL_GIT_REPO_PATH))
repo.push(LOCAL_GIT_REPO_PATH)
with git.Repo(LOCAL_GIT_REPO_PATH) as repo:
logging.info('pull from %s on branch master' % GH_REPO)
repo.remotes.origin.pull('master')
with hglib.open(LOCAL_HG_REPO_PATH, configs=configs) as repo:
logging.info('pull from %s to %s on branch master' %
(LOCAL_GIT_REPO_PATH, LOCAL_HG_REPO_PATH))
repo.pull(LOCAL_GIT_REPO_PATH)
repo.update('master', check=True)
logging.info('push from %s to %s on bookmark master' %
(LOCAL_HG_REPO_PATH, HG_REPO))
repo.push(HG_REPO, bookmark='master', force=True)
logging.info('Done!')
class MainHandler(RequestHandler):
@gen.coroutine
def _verify_ip(self):
remote_ip = self.request.headers.get(
"X-Real-IP", self.request.remote_ip)
src_ip = ip_address(u'{}'.format(remote_ip))
whitelist = requests.get('https://api.github.com/meta').json()['hooks']
for valid_ip in whitelist:
if src_ip in ip_network(valid_ip):
break
else:
raise HTTPError(403, 'Remote address not whitelisted')
@gen.coroutine
def _verify_signature(self):
header_signature = self.request.headers.get('X-Hub-Signature')
if not header_signature:
raise HTTPError(403, 'Request is missing "X-Hub-Signature"')
try:
sha_name, signature = header_signature.split('=')
except:
raise HTTPError(403, '"X-Hub-Signature" is malformed')
if sha_name != 'sha1':
raise HTTPError(501, 'Only SHA1 is supported')
mac = hmac.new(str(GH_SECRET), msg=self.request.body,
digestmod=hashlib.sha1)
if not hmac.compare_digest(str(mac.hexdigest()), str(signature)):
raise HTTPError(403, 'Wrong signature')
@gen.coroutine
def post(self):
yield [
self._verify_ip(),
self._verify_signature()
]
# Implement ping
event = self.request.headers.get('X-GitHub-Event', 'ping')
if event == 'ping':
self.write(json.dumps({'msg': 'pong'}))
elif event in ['push']:
IOLoop.current().spawn_callback(sync_repos)
self.set_status(202)
self.finish()
if __name__ == "__main__":
logging.getLogger().setLevel(logging.INFO)
handlers = [
(r"/", MainHandler),
]
app = Application(handlers)
app.listen(5000)
IOLoop.current().start()