"""Gestion des mises à jour AUR. Les paquets AUR sont toujours installés immédiatement (jamais reportés à l'extinction) car le build requiert un utilisateur actif et un accès réseau. """ import logging import os import pwd import shutil import subprocess from pathlib import Path from package_manager import Package, InstallResult logger = logging.getLogger(__name__) _AUR_HELPERS = ["paru", "yay"] def detect_aur_helper() -> str | None: for helper in _AUR_HELPERS: if shutil.which(helper): return helper return None def detect_active_user() -> str | None: """Retourne le nom de l'utilisateur avec une session graphique active.""" try: result = subprocess.run( ["loginctl", "list-sessions", "--no-legend"], capture_output=True, text=True, timeout=5, ) for line in result.stdout.strip().splitlines(): parts = line.split() if len(parts) < 3: continue session_id, _, user = parts[0], parts[1], parts[2] if user == "root": continue session_type = subprocess.run( ["loginctl", "show-session", session_id, "-p", "Type", "--value"], capture_output=True, text=True, timeout=3, ).stdout.strip() if session_type in ("x11", "wayland", "mir"): return user except Exception as e: logger.debug(f"Impossible de détecter l'utilisateur actif : {e}") return None def _user_env(user: str) -> dict: pw = pwd.getpwnam(user) uid = pw.pw_uid env = { "HOME": pw.pw_dir, "USER": user, "LOGNAME": user, "XDG_RUNTIME_DIR": f"/run/user/{uid}", "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", # Évite que sudo bloque sans TTY si le helper essaie d'appeler sudo pacman "SUDO_ASKPASS": "/bin/false", } # Socket DBUS de session (requis par paru/yay pour le build) dbus_socket = Path(f"/run/user/{uid}/bus") if dbus_socket.exists(): env["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path={dbus_socket}" return env def check_aur_updates(user: str, helper: str) -> list[Package]: """Liste les mises à jour AUR disponibles sans rien installer.""" try: result = subprocess.run( ["sudo", "-u", user, "-H", helper, "-Qua", "--color", "never"], capture_output=True, text=True, timeout=120, env=_user_env(user), ) packages = [] for line in result.stdout.strip().splitlines(): parts = line.split() if len(parts) >= 4 and parts[2] == "->": packages.append(Package( name=parts[0], old_version=parts[1], new_version=parts[3], )) return packages except Exception as e: logger.error(f"Échec de la vérification AUR : {e}") return [] def install_aur_packages(user: str, helper: str, names: list[str]) -> InstallResult: """Installe des paquets AUR spécifiques en tant qu'utilisateur actif.""" if not names: return InstallResult(success=True) try: result = subprocess.run( [ "sudo", "-u", user, "-H", helper, "-S", "--noconfirm", "--noprogressbar", "--color", "never", ] + names, capture_output=True, text=True, timeout=900, env=_user_env(user), ) if result.returncode == 0: return InstallResult(success=True, installed=names) return InstallResult( success=False, failed=names, error=result.stderr[-800:], ) except Exception as e: logger.exception(f"Erreur lors de l'installation AUR : {e}") return InstallResult(success=False, failed=names, error=str(e))