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 + |