"""Guarded helpers for the dedicated Proton test prefix.""" from __future__ import annotations from pathlib import Path from .config import AppConfig from .runner import CommandLogger, CommandResult class PrefixSafetyError(RuntimeError): """Raised when a cleanup target is not provably the dedicated test prefix.""" def validate_dedicated_prefix(config: AppConfig) -> Path: prefix = config.proton7.prefix.resolve(strict=False) compat = config.proton7.compat.resolve(strict=False) live_prefix = config.paths.live_prefix.resolve(strict=False) if prefix == live_prefix: raise PrefixSafetyError("Dedicated prefix resolves to the live launcher prefix") try: prefix.relative_to(compat) except ValueError as exc: raise PrefixSafetyError( f"Dedicated prefix {prefix} is not inside compat directory {compat}" ) from exc if prefix == compat: raise PrefixSafetyError("Dedicated prefix must be a child of the compat directory") return prefix def resolve_proton_wineserver(config: AppConfig) -> Path: candidates = proton_wineserver_candidates(config) for candidate in candidates: if candidate.is_file(): return candidate raise FileNotFoundError( "Proton wineserver was not found in dist/bin or files/bin under " f"{config.proton7.proton.parent}" ) def proton_wineserver_candidates(config: AppConfig) -> tuple[Path, Path]: proton_directory = config.proton7.proton.parent return ( proton_directory / "dist/bin/wineserver", proton_directory / "files/bin/wineserver", ) def cleanup_dedicated_wineserver( config: AppConfig, command_logger: CommandLogger, *, timeout_seconds: float = 15.0, dry_run: bool = False, ) -> CommandResult: prefix = validate_dedicated_prefix(config) wineserver = resolve_proton_wineserver(config) return command_logger.run( [str(wineserver), "-k"], env_overrides={"WINEPREFIX": str(prefix), "WINEDEBUG": "-all"}, timeout_seconds=timeout_seconds, dry_run=dry_run, )