116 lines
3.2 KiB
Python
116 lines
3.2 KiB
Python
import subprocess
|
|
import logging
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PACMAN_LOCK = Path("/var/lib/pacman/db.lck")
|
|
|
|
|
|
@dataclass
|
|
class Package:
|
|
name: str
|
|
old_version: str
|
|
new_version: str
|
|
|
|
|
|
@dataclass
|
|
class InstallResult:
|
|
success: bool
|
|
installed: list = field(default_factory=list)
|
|
failed: list = field(default_factory=list)
|
|
error: Optional[str] = None
|
|
|
|
|
|
def is_pacman_locked() -> bool:
|
|
return PACMAN_LOCK.exists()
|
|
|
|
|
|
def check_updates() -> list[Package]:
|
|
"""Vérifie les mises à jour disponibles via checkupdates (sans droits root)."""
|
|
result = subprocess.run(
|
|
["checkupdates"],
|
|
capture_output=True, text=True, timeout=120
|
|
)
|
|
packages = []
|
|
for line in result.stdout.strip().splitlines():
|
|
parts = line.split()
|
|
# Format attendu : "nom ancienne_version -> nouvelle_version"
|
|
if len(parts) >= 4 and parts[2] == "->":
|
|
packages.append(Package(
|
|
name=parts[0],
|
|
old_version=parts[1],
|
|
new_version=parts[3],
|
|
))
|
|
return packages
|
|
|
|
|
|
def download_packages(names: list[str]) -> bool:
|
|
"""Télécharge les paquets dans le cache sans les installer."""
|
|
if not names:
|
|
return True
|
|
result = subprocess.run(
|
|
["pacman", "-Syw", "--noconfirm", "--noprogressbar"] + names,
|
|
capture_output=True, text=True, timeout=600
|
|
)
|
|
if result.returncode != 0:
|
|
logger.error(f"Téléchargement échoué : {result.stderr[-500:]}")
|
|
return False
|
|
return True
|
|
|
|
|
|
def sync_db() -> bool:
|
|
"""Synchronise la base de données des paquets."""
|
|
result = subprocess.run(
|
|
["pacman", "-Sy", "--noconfirm"],
|
|
capture_output=True, text=True, timeout=120
|
|
)
|
|
return result.returncode == 0
|
|
|
|
|
|
def install_packages(names: list[str]) -> InstallResult:
|
|
"""Installe des paquets spécifiques depuis le cache."""
|
|
if not names:
|
|
return InstallResult(success=True)
|
|
|
|
result = subprocess.run(
|
|
["pacman", "-S", "--noconfirm", "--needed", "--noprogressbar"] + names,
|
|
capture_output=True, text=True, timeout=600
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
return InstallResult(success=True, installed=names)
|
|
else:
|
|
return InstallResult(
|
|
success=False,
|
|
failed=names,
|
|
error=result.stderr[-800:],
|
|
)
|
|
|
|
|
|
def upgrade_cached_packages(expected_names: list[str]) -> InstallResult:
|
|
"""Installe toutes les mises à jour présentes dans le cache pacman.
|
|
|
|
Utilisé à l'extinction pour éviter les échecs de dépendances liés aux
|
|
mises à jour partielles : pacman -Su installe l'ensemble cohérent des
|
|
paquets déjà téléchargés plutôt qu'une liste isolée.
|
|
"""
|
|
if not expected_names:
|
|
return InstallResult(success=True)
|
|
|
|
result = subprocess.run(
|
|
["pacman", "-Su", "--noconfirm", "--noprogressbar"],
|
|
capture_output=True, text=True, timeout=600
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
return InstallResult(success=True, installed=expected_names)
|
|
else:
|
|
return InstallResult(
|
|
success=False,
|
|
failed=expected_names,
|
|
error=result.stderr[-800:],
|
|
)
|