from __future__ import annotations from pathlib import Path from app.core.command_runner import CommandRunner def _parse_deb_control_output(output: str) -> dict[str, str]: metadata: dict[str, str] = {} for line in output.splitlines(): key, separator, value = line.partition(":") if not separator: continue normalized_key = key.strip().lower() if normalized_key in {"package", "version", "architecture"}: metadata[normalized_key] = value.strip() return metadata class DebInstaller: def __init__(self, command_runner: CommandRunner) -> None: self.command_runner = command_runner def get_deb_metadata(self, file_path: Path) -> dict[str, str]: result = self.command_runner.run([ "dpkg-deb", "-f", str(file_path), "Package", "Version", "Architecture", ]) metadata = _parse_deb_control_output(result.stdout) missing_fields = [ field for field in ("package", "version", "architecture") if not metadata.get(field) ] if missing_fields: raise ValueError( "Downloaded .deb is missing metadata fields: " f"{', '.join(missing_fields)}" ) return metadata def install_deb(self, file_path: Path) -> None: self.command_runner.run(["apt", "install", "-y", str(file_path)]) def remove_package(self, package_name: str, purge: bool = False) -> None: action = "purge" if purge else "remove" self.command_runner.run(["apt", action, "-y", package_name]) def get_package_version(self, package_name: str) -> str | None: result = self.command_runner.run(["dpkg-query", "-W", "-f=${Version}", package_name]) version = result.stdout.strip() return version or None def check_package_installed(self, package_name: str) -> bool: try: self.get_package_version(package_name) return True except Exception: return False