diff options
Diffstat (limited to 'gamechest/steps_install.py')
| -rw-r--r-- | gamechest/steps_install.py | 109 | 
1 files changed, 109 insertions, 0 deletions
diff --git a/gamechest/steps_install.py b/gamechest/steps_install.py new file mode 100644 index 0000000..7c508bb --- /dev/null +++ b/gamechest/steps_install.py @@ -0,0 +1,109 @@ +#!python3 + +import contextlib +import os +import re +import selectors +import subprocess + +from stepper import Stepper + +_rsync_progress_re = re.compile(r'\s(\d+)%\s') + +def _rsync_loop(queue, proc, selector): +    while True: +        with contextlib.suppress(queue.Empty): +            if queue.get_nowait() == Stepper.Command.STOP: +                proc.terminate() +                break + +        if proc.poll() is not None: +            break + +        if not any(key +            for key, mask in selector.select(timeout=0.250) +            if mask & selectors.EVENT_READ and key.fileobj == proc.stdout): +            continue + +        # stdout.readline is a blocking call if there is no endline in +        # stdout outputed by the subprocess. +        # normally rsync output should be line buffered thus at any read event +        # a call to readline should not block (or not long enough to be +        # able to process a queued command quick enough). +        line = proc.stdout.readline() +        if match := _rsync_progress_re.search(line): +            progress = int(match.group(1)) +            yield progress + +def step_rsync(queue, conf, status): +    package_name = conf.game_data['package_name'] +    command = ( +        'rsync', +        '-a', +        '--partial', +        '--info=progress2', +        f'{conf.repository_host}:{conf.repository_path}/{package_name}', +        f'{conf.gamedir}/.', +    ) +    debug('running command %s', command) +    with contextlib.ExitStack() as stack: +        proc = stack.enter_context(subprocess.Popen( +                command, +                stdout=subprocess.PIPE, +                encoding='utf8')) +        selector = stack.enter_context(selectors.DefaultSelector()) +        selector.register(proc.stdout, selectors.EVENT_READ) +        yield from _rsync_loop(queue, proc=proc, selector=selector) + +def step_extract(queue, conf, status): +    with contextlib.ExitStack() as stack: +        lzip_command = '/usr/bin/lzip' +        plzip_command = '/usr/bin/plzip' +        if os.path.exists(plzip_command): +            lzip_command = plzip_command +        lzip_proc = stack.enter_context(subprocess.Popen( +                (lzip_command, '-d'), +                stdin=subprocess.PIPE, +                stdout=subprocess.PIPE, +        )) +        tar_proc = stack.enter_context(subprocess.Popen( +                ('tar', '-C', conf.gamedir, '-xvf', '-'), +                stdin=lzip_proc.stdout, +                stdout=subprocess.PIPE, +        )) +        filepath = f'{conf.gamedir}/{conf.game_data["package_name"]}' +        filepath_status = f'{conf.gamedir}/{conf.game_data["name"]}.yaml' +        fobj = stack.enter_context(open(filepath, 'rb')) +        selector = stack.enter_context(selectors.DefaultSelector()) +        selector.register(tar_proc.stdout, selectors.EVENT_READ) +        filesize = os.stat(filepath).st_size +        def _itemscounter(): +            while ready := selector.select(0.100): +                data = tar_proc.stdout.read1() +                if len(data) == 0: +                    break +                itemscount += data.count(b'\n') +        itemscount = 0 +        while True: +            with contextlib.suppress(queue.Empty): +                if queue.get_nowait() == Stepper.Command.STOP: +                    break +            data = fobj.read(131072) # 128kib chunks +            if len(data) == 0: +                lzip_proc.stdin.close() +                break +            lzip_proc.stdin.write(data) +            yield filesize / fobj.tell() +            _itemscounter() +        _itemscounter() +        with open(filepath_status, 'wb') as fobj: +            yaml.safe_dump({ +                'installed_count': itemscount, +                'installed_version': conf.game_data['version'], +                'installed_package_name': conf.game_data['package_name'], +            }, fobj) + +def step_clean(queue, conf, status): +    os.unlink(f'{conf.gamedir}/{conf.game_data["package_name"]}') +    yield 100 +  | 
