import os import re from services.runner import run_cmd def detect_pkg_manager() -> str | None: if os.path.exists("/usr/bin/apt"): return "apt" if os.path.exists("/usr/bin/dnf"): return "dnf" if os.path.exists("/usr/bin/yum"): return "yum" if os.path.exists("/usr/bin/pacman"): return "pacman" return None async def list_updates() -> tuple[str, list[str]]: pm = detect_pkg_manager() if not pm: return "⚠️ Updates", ["No supported package manager found"] if pm == "apt": await run_cmd(["sudo", "apt", "update"], timeout=300) rc, out = await run_cmd(["apt", "list", "--upgradable"], timeout=120) if rc != 0: return "❌ Updates (apt)", [f"apt list failed: {out}"] lines = [] for line in out.splitlines(): if not line or line.startswith("Listing..."): continue if "upgradable from:" not in line: continue # Format: name/repo new_ver arch [upgradable from: old] m = re.match(r"^(\S+)\s+(\S+)\s", line) if not m: continue name_repo, new_ver = m.group(1), m.group(2) name = name_repo.split("/", 1)[0] if "/" in name_repo else name_repo old_ver = None m_old = re.search(r"upgradable from:\s*([^\]]+)", line) if m_old: old_ver = m_old.group(1).strip() if old_ver: lines.append(f"{name}: {old_ver} -> {new_ver}") else: lines.append(f"{name}: -> {new_ver}") if not lines: lines = ["No updates"] return "📦 Updates (apt)", lines if pm == "dnf": rc, out = await run_cmd(["sudo", "dnf", "check-update"], timeout=300) if rc in (0, 100): lines = out.splitlines() or ["No updates"] return "📦 Updates (dnf)", lines return "❌ Updates (dnf)", [f"dnf check-update failed: {out}"] if pm == "yum": rc, out = await run_cmd(["sudo", "yum", "check-update"], timeout=300) if rc in (0, 100): lines = out.splitlines() or ["No updates"] return "📦 Updates (yum)", lines return "❌ Updates (yum)", [f"yum check-update failed: {out}"] if pm == "pacman": rc, out = await run_cmd(["pacman", "-Qu"], timeout=120) if rc == 0: lines = out.splitlines() or ["No updates"] return "📦 Updates (pacman)", lines return "❌ Updates (pacman)", [f"pacman -Qu failed: {out}"] return "⚠️ Updates", ["Unsupported package manager"] async def apply_updates() -> str: pm = detect_pkg_manager() if not pm: return "⚠️ No supported package manager found" if pm == "apt": await run_cmd(["sudo", "apt", "update"], timeout=300) rc, out = await run_cmd(["sudo", "apt-get", "-y", "upgrade"], timeout=1800) return f"⬆️ Upgrade (apt)\n```{out}```" if rc == 0 else f"❌ apt upgrade failed\n```{out}```" if pm == "dnf": rc, out = await run_cmd(["sudo", "dnf", "-y", "upgrade"], timeout=1800) return f"⬆️ Upgrade (dnf)\n```{out}```" if rc == 0 else f"❌ dnf upgrade failed\n```{out}```" if pm == "yum": rc, out = await run_cmd(["sudo", "yum", "-y", "update"], timeout=1800) return f"⬆️ Upgrade (yum)\n```{out}```" if rc == 0 else f"❌ yum update failed\n```{out}```" if pm == "pacman": rc, out = await run_cmd(["sudo", "pacman", "-Syu", "--noconfirm"], timeout=1800) return f"⬆️ Upgrade (pacman)\n```{out}```" if rc == 0 else f"❌ pacman -Syu failed\n```{out}```" return "⚠️ Unsupported package manager"