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
|
from pathlib import Path
import asyncio
import contextlib
import logging.config
import sys
import tempfile
import typing
import subprocess
import appdirs
import click
from hat import aio
from hat import json
from hatter import common
user_config_dir: Path = Path(appdirs.user_config_dir('hatter'))
user_data_dir: Path = Path(appdirs.user_data_dir('hatter'))
default_conf_path: Path = user_config_dir / 'server.yaml'
default_db_path: Path = user_data_dir / 'hatter.db'
ssh_key_path: typing.Optional[Path] = None
@click.group()
@click.option('--log-level',
default='INFO',
type=click.Choice(['CRITICAL', 'ERROR', 'WARNING', 'INFO',
'DEBUG', 'NOTSET']),
help="log level")
@click.option('--ssh-key', default=None, metavar='PATH', type=Path,
help="private key used for ssh authentication")
def main(log_level: str,
ssh_key: typing.Optional[Path]):
global ssh_key_path
ssh_key_path = ssh_key
logging.config.dictConfig({
'version': 1,
'formatters': {
'console': {
'format': "[%(asctime)s %(levelname)s %(name)s] %(message)s"}},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'console',
'level': log_level}},
'root': {
'level': log_level,
'handlers': ['console']},
'disable_existing_loggers': False})
@main.command()
@click.argument('url', required=True)
@click.argument('branch', required=False, default='master')
@click.argument('action', required=False, default='.hatter.yaml')
def execute(url: str,
branch: str,
action: str):
with tempfile.TemporaryDirectory() as repo_dir:
repo_dir = Path(repo_dir)
subprocess.run(['git', 'clone', '-q', '--depth', '1',
'-b', branch, url, str(repo_dir)],
check=True)
conf = json.decode_file(repo_dir / '.hatter.yaml')
common.json_schema_repo.validate('hatter://action.yaml#', conf)
image = conf['image']
command = conf['command']
subprocess.run(['podman', 'run', '-i', '--rm',
'-v', f'{repo_dir}:/hatter',
image, '/bin/sh'],
input=f'set -e\ncd /hatter\n{command}\n',
encoding='utf-8',
check=True)
@main.command()
@click.option('--host', default='0.0.0.0',
help="listening host name (default 0.0.0.0)")
@click.option('--port', default=24000, type=int,
help="listening TCP port (default 24000)")
@click.option('--conf', default=default_conf_path, metavar='PATH', type=Path,
help="configuration defined by hatter://server.yaml# "
"(default $XDG_CONFIG_HOME/hatter/server.yaml)")
@click.option('--db', default=default_db_path, metavar='PATH', type=Path,
help="sqlite database path "
"(default $XDG_CONFIG_HOME/hatter/hatter.db")
def server(host: str,
port: int,
conf: Path,
db: Path):
conf = json.decode_file(conf)
common.json_schema_repo.validate('hatter://server.yaml#', conf)
with contextlib.suppress(asyncio.CancelledError):
aio.run_asyncio(async_server(host, port, conf, db))
async def async_server(host: str,
port: int,
conf: json.Data,
db_path: Path):
pass
if __name__ == '__main__':
sys.argv[0] = 'hatter'
main()
|