"""Render the installed command wrapper with persistent application paths.""" from __future__ import annotations import argparse import os from pathlib import Path import sys from typing import Sequence APP_ROOT_TOKEN = "@PTLINUX_APP_ROOT@" INSTALL_DIRECTORY_TOKEN = "@PTLINUX_INSTALL_DIR@" class LauncherWrapperError(ValueError): """Raised when a safe launcher wrapper cannot be rendered.""" def quote_shell_word(value: str) -> str: """Quote one value as a literal POSIX shell word.""" if not value or any(character in value for character in ("\x00", "\n", "\r")): raise LauncherWrapperError("launcher path contains invalid characters") return "'" + value.replace("'", "'\"'\"'") + "'" def _absolute_directory(path: Path, label: str) -> Path: directory = path.expanduser() if not directory.is_absolute(): raise LauncherWrapperError(f"{label} must be absolute") return directory def render_launcher_wrapper( template: str, app_root: Path, install_directory: Path, ) -> str: """Embed the selected install layout in one command-wrapper template.""" for token in (APP_ROOT_TOKEN, INSTALL_DIRECTORY_TOKEN): if template.count(token) != 1: raise LauncherWrapperError( f"launcher template must contain exactly one {token} token" ) application = _absolute_directory(app_root, "application directory") installation = _absolute_directory(install_directory, "install directory") if not any( (candidate / "ptlinux").is_dir() for candidate in ( application / "current", application / "previous", application, ) ): raise LauncherWrapperError(f"application files are missing: {application}") rendered = template.replace(APP_ROOT_TOKEN, quote_shell_word(str(application))) rendered = rendered.replace( INSTALL_DIRECTORY_TOKEN, quote_shell_word(str(installation)), ) return rendered if rendered.endswith("\n") else rendered + "\n" def install_launcher_wrapper( template: Path, app_root: Path, install_directory: Path, destination: Path, ) -> None: """Render and atomically install one executable command wrapper.""" rendered = render_launcher_wrapper( template.read_text(encoding="utf-8"), app_root, install_directory, ) destination.parent.mkdir(parents=True, exist_ok=True) temporary = destination.with_name(f".{destination.name}.{os.getpid()}.tmp") try: temporary.write_text(rendered, encoding="utf-8") temporary.chmod(0o755) temporary.replace(destination) except BaseException: temporary.unlink(missing_ok=True) raise def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser( description="install the PristonTale EU command wrapper" ) parser.add_argument("template", type=Path) parser.add_argument("app_root", type=Path) parser.add_argument("install_directory", type=Path) parser.add_argument("destination", type=Path) arguments = parser.parse_args(argv) try: install_launcher_wrapper( arguments.template, arguments.app_root, arguments.install_directory, arguments.destination, ) except (LauncherWrapperError, OSError) as exc: print(f"Could not install command wrapper: {exc}", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())