diff options
Diffstat (limited to 'src_py/hatter')
| -rw-r--r-- | src_py/hatter/backend.py | 18 | ||||
| -rw-r--r-- | src_py/hatter/main.py | 60 | ||||
| -rw-r--r-- | src_py/hatter/server.py | 21 | ||||
| -rw-r--r-- | src_py/hatter/ui.py | 76 |
4 files changed, 165 insertions, 10 deletions
diff --git a/src_py/hatter/backend.py b/src_py/hatter/backend.py new file mode 100644 index 0000000..059c636 --- /dev/null +++ b/src_py/hatter/backend.py @@ -0,0 +1,18 @@ +from pathlib import Path + +from hat import aio + + +async def create(db_path: Path + ) -> 'Backend': + backend = Backend() + backend._async_group = aio.Group() + + return backend + + +class Backend(aio.Resource): + + @property + def async_group(self): + return self._async_group diff --git a/src_py/hatter/main.py b/src_py/hatter/main.py index f1046a5..1d094b5 100644 --- a/src_py/hatter/main.py +++ b/src_py/hatter/main.py @@ -2,18 +2,20 @@ from pathlib import Path import asyncio import contextlib import logging.config +import subprocess import sys import tempfile import typing -import subprocess - -import appdirs -import click from hat import aio from hat import json +import appdirs +import click from hatter import common +import hatter.backend +import hatter.server +import hatter.ui user_config_dir: Path = Path(appdirs.user_config_dir('hatter')) @@ -56,19 +58,36 @@ def main(log_level: str, @main.command() @click.argument('url', required=True) -@click.argument('branch', required=False, default='master') +@click.argument('commit', required=False, default='master') @click.argument('action', required=False, default='.hatter.yaml') def execute(url: str, - branch: str, + commit: str, action: str): + with contextlib.suppress(Exception): + path = Path(url) + if path.exists(): + url = str(path.resolve()) + with tempfile.TemporaryDirectory() as repo_dir: repo_dir = Path(repo_dir) - subprocess.run(['git', 'clone', '-q', '--depth', '1', - '-b', branch, url, str(repo_dir)], + subprocess.run(['git', 'init', '-q'], + cwd=str(repo_dir), check=True) - conf = json.decode_file(repo_dir / '.hatter.yaml') + subprocess.run(['git', 'remote', 'add', 'origin', url], + cwd=str(repo_dir), + check=True) + + subprocess.run(['git', 'fetch', '-q', '--depth=1', 'origin', commit], + cwd=str(repo_dir), + check=True) + + subprocess.run(['git', 'checkout', '-q', commit], + cwd=str(repo_dir), + check=True) + + conf = json.decode_file(repo_dir / action) common.json_schema_repo.validate('hatter://action.yaml#', conf) image = conf['image'] @@ -107,7 +126,28 @@ async def async_server(host: str, port: int, conf: json.Data, db_path: Path): - pass + async_group = aio.Group() + + try: + backend = await hatter.backend.create(db_path) + _bind_resource(async_group, backend) + + server = await hatter.server.create(conf, backend) + _bind_resource(async_group, server) + + ui = await hatter.ui.create(host, port, server) + _bind_resource(async_group, ui) + + await async_group.wait_closing() + + finally: + await aio.uncancellable(async_group.async_close()) + + +def _bind_resource(async_group, resource): + async_group.spawn(aio.call_on_cancel, resource.async_close) + async_group.spawn(aio.call_on_done, resource.wait_closing(), + async_group.close) if __name__ == '__main__': diff --git a/src_py/hatter/server.py b/src_py/hatter/server.py new file mode 100644 index 0000000..f9ee29f --- /dev/null +++ b/src_py/hatter/server.py @@ -0,0 +1,21 @@ +from hat import aio +from hat import json + +import hatter.backend + + +async def create(conf: json.Data, + backend: hatter.backend.Backend + ) -> 'Server': + server = Server() + server._backend = backend + server._async_group = aio.Group() + + return server + + +class Server(aio.Resource): + + @property + def async_group(self): + return self._async_group diff --git a/src_py/hatter/ui.py b/src_py/hatter/ui.py new file mode 100644 index 0000000..525df6b --- /dev/null +++ b/src_py/hatter/ui.py @@ -0,0 +1,76 @@ +from pathlib import Path + +from hat import aio +import aiohttp.web + +from hatter import common +import hatter.server + + +static_dir: Path = common.package_path / 'ui' + + +async def create(host: str, + port: int, + server: hatter.server.Server + ) -> 'UI': + ui = UI() + ui._server = server + ui._async_group = aio.Group() + + app = aiohttp.web.Application() + app.add_routes([ + aiohttp.web.get('/', ui._get_root_handler), + aiohttp.web.get('/repo/{repo}', server._get_repo_handler), + aiohttp.web.get('/repo/{repo}/commit/{commit}', + server._get_commit_handler), + aiohttp.web.post('/repo/{repo}/webhook', + server._post_webhook_handler), + aiohttp.web.post('/repo/{repo}/commit/{commit}/run', + server._post_run_handler), + aiohttp.web.post('/repo/{repo}/commit/{commit}/remove', + server._post_remove_handler), + aiohttp.web.static('/', static_dir)]) + + runner = aiohttp.web.AppRunner(app) + await runner.setup() + ui.async_group.spawn(aio.call_on_cancel, runner.cleanup) + + try: + site = aiohttp.web.TCPSite(runner=runner, + host=host, + port=port, + shutdown_timeout=0.1, + reuse_address=True) + await site.start() + + except BaseException: + await aio.uncancellable(ui.async_group.async_close()) + raise + + return ui + + +class UI(aio.Resource): + + @property + def async_group(self): + return self._async_group + + async def _get_root_handler(self, request): + pass + + async def _get_repo_handler(self, request): + pass + + async def _get_commit_handler(self, request): + pass + + async def _post_webhook_handler(self, request): + pass + + async def _post_run_handler(self, request): + pass + + async def _post_remove_handler(self, request): + pass |
