354:23e5b4d4f272 default tip
Anton Shestakov <av6@dwimlabs.net>, Mon, 13 Apr 2020 19:01:24 +0800
tests: update a link

previous change 340:2d97d5a9d437

hooks-queue.py

Permissions: -rwxr-xr-x

Other formats: Feeds:
#!/usr/bin/env python
import json
import logging
from argparse import ArgumentParser, FileType, SUPPRESS
import redis
import yaml
from tornado.ioloop import IOLoop
from tornado.options import define, options
from tornado.web import Application, HTTPError, RequestHandler, URLSpec
from candolint.utils import lookup_option, timestamp
class BaseHookHandler(RequestHandler):
@property
def rconn(self):
return self.application.rconn
def write_error(self, status_code, **kwargs):
result = self._reason
if 'exc_info' in kwargs:
result = getattr(kwargs['exc_info'][1], 'log_message', result)
self.finish({'result': result})
def parse_json_payload(self):
try:
return json.loads(self.request.body)
except ValueError:
raise HTTPError(400, 'malformed JSON')
def push(self, base, changes):
for change in changes:
item = base.copy()
item['change'] = change
data = json.dumps(item)
logging.debug('Pushing %s', data)
self.rconn.rpush('candolint:queue:changes', data)
logging.info('Pushed a change for %s', item['repo'])
self.finish({'result': 'OK', 'queued': len(changes)})
class BitbucketHookHandler(BaseHookHandler):
def post(self):
event = self.request.headers.get('X-Event-Key')
logging.debug('%s received event %r', self.__class__.__name__, event)
payload = self.parse_json_payload()
base = {
'url': payload['repository']['links']['html']['href'],
'scm': payload['repository']['scm'],
'repo': payload['repository']['name'],
'timestamp': timestamp(),
'source': 'bitbucket.org'
}
changes = set()
if payload['repository']['scm'] == 'git':
for change in payload['push']['changes']:
if change['new']['type'] == 'branch':
changes.add(change['new']['name'])
else:
for change in payload['push']['changes']:
for head in change['new'].get('heads', []):
changes.add(head['hash'])
changes.add(change['new']['target']['hash'])
self.push(base, changes)
class GithubHookHandler(BaseHookHandler):
def post(self):
event = self.request.headers.get('X-GitHub-Event', 'nonexistent')
logging.debug('%s received event %r', self.__class__.__name__, event)
getattr(self, 'handle_' + event, self.unknown_event)()
def handle_ping(self):
self.finish({'result': 'pong'})
def handle_push(self):
payload = self.parse_json_payload()
base = {
'url': payload['repository']['html_url'],
'scm': 'git',
'repo': payload['repository']['name'],
'timestamp': timestamp(),
'source': 'github.com'
}
ref = payload['ref'].rpartition('/')[-1]
changes = [ref]
self.push(base, changes)
def unknown_event(self):
self.finish({'result': 'unknown event'})
class CandolintHooks(Application):
def __init__(self, rconn, debug=False):
handlers = [
URLSpec(r'/bitbucket', BitbucketHookHandler),
URLSpec(r'/github', GithubHookHandler),
]
super(CandolintHooks, self).__init__(handlers, debug=debug)
self.rconn = rconn
def listen(self, port, address='', **kwargs):
name = self.__class__.__name__
logging.info('%s is serving on http://%s:%d/', name, address, port)
super(CandolintHooks, self).listen(port, address, **kwargs)
def main():
define('listen', metavar='IP', default='127.0.0.1')
define('port', metavar='PORT', default=8034, type=int)
define('xheaders', metavar='True|False', default=False, type=bool)
options.logging = None
parser = ArgumentParser(argument_default=SUPPRESS)
parser.add_argument(
'-c', '--config', type=FileType('r'),
help='configuration file (YAML)')
parser.add_argument(
'-d', '--debug', action='store_true', default=False,
help='enable debugging output')
group = parser.add_argument_group(
'queue configuration',
'these options that can also be specified in the configuration file')
group.add_argument('--redis-host', help='(default: 127.0.0.1)')
group.add_argument('--redis-port', type=int, help='(default: 6379)')
group.add_argument('--redis-password')
args, extra = parser.parse_known_args()
options.parse_command_line([None] + extra) # 1st argument is ignored
config = yaml.safe_load(args.config) if hasattr(args, 'config') else {}
rhost = lookup_option('redis-host', args, config, default='127.0.0.1')
rport = lookup_option('redis-port', args, config, default=6379)
rpass = lookup_option('redis-password', args, config)
logging.basicConfig(
level=logging.DEBUG if args.debug else logging.INFO,
format='%(asctime)s %(levelname)-8s %(message)s')
logging.debug('Connecting to Redis server')
rconn = redis.StrictRedis(host=rhost, port=rport, password=rpass, db=0)
logging.info('Connected to Redis server')
application = CandolintHooks(rconn, args.debug)
application.listen(options.port, options.listen, xheaders=options.xheaders)
IOLoop.current().start()
if __name__ == '__main__':
main()