diff --git a/gogsmaker.py b/gogsmaker.py index e8e7f4a..e7bd7d3 100644 --- a/gogsmaker.py +++ b/gogsmaker.py @@ -2,16 +2,92 @@ A webhook-handler for Gogs running `make` when needed. """ +import os +import subprocess from flask import Flask, request -from .settings import HOOKS +from . import settings app = Flask(__name__) +class UnmonitoredRepository(Exception): + pass + + +class GitError(Exception): + def __init__(self, what): + self.what = what + + def __str__(self): + return self.what + + +def get_hook(url): + ''' Get the hook matching an URL, or raise UnmonitoredRepository ''' + for hook in settings.HOOKS: + if hook['url'] == url: + return hook + raise UnmonitoredRepository + + +def repo_path(hook): + ''' Get the path at which the hook's repo is cloned ''' + return os.path.join(settings.CLONE_ROOT, hook['name']) + + +def update_repo(hook, clone_url): + ''' Update (or clone) the given repository. May raise GitError. ''' + path = repo_path(hook) + if os.path.isdir(os.path.join(path, '.git')): # Repo is already cloned + try: + subprocess.run(['git', 'clone', clone_url, path], check=True) + except subprocess.CalledProcessError: + raise GitError("Cannot clone {}".format(clone_url)) + + else: # Simply update + try: + subprocess.run(['git', '-C', path, 'reset', '--hard'], + check=True) # Just in case. + subprocess.run(['git', '-C', path, 'pull'], check=True) + except subprocess.CalledProcessError: + raise GitError("Cannot pull {}".format(hook['name'])) + + +def gogs_payload(required): + def wrapper(fct): + payload = request.json + if payload is None: + return 'Expected json\n', 415 + + for field in required + ['repository/html_url']: + path = field.split('/') + explore = payload + for section in path: + if section not in explore: + return ( + 'Invalid json: missing {}\n'.format('/'.join(path)), + 400) + explore = explore[section] + + try: + hook = get_hook(payload['repository']['html_url']) + except UnmonitoredRepository: + return 'Unmonitored repository\n', 403 + + return fct(payload, hook) + return wrapper + + @app.route('/', methods=['POST']) -def view_root(): - payload = request.json - if payload is None: - return 'Expected json\n', 415 - print(payload) +@gogs_payload(['repository/clone_url']) +def view_root(payload, hook): + clone_url = payload['repository']['clone_url'] + + try: + update_repo(hook, clone_url) + except GitError as error: + return 'Git error: {}\n'.format(error), 500 + + # TODO: make + return 'OK\n', 200 diff --git a/settings.default.py b/settings.default.py index e726f26..6a684f9 100644 --- a/settings.default.py +++ b/settings.default.py @@ -14,3 +14,6 @@ HOOKS = [ 'targets': ['all'], }, ] + +# Directory in which the repositories are cloned +CLONE_ROOT = 'repos'