fix(gui): show the real reason an Environment Apply/Install failed — 0.10.2

Thread the command output through to the status line and classify it: cancelled
at the password prompt vs. the system rejecting the change (e.g. a BIOS/kernel-
locked PCIe ASPM policy), instead of a vague "cancelled, or needs privileges".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 08:21:01 +02:00
parent 25b7a58e3c
commit 8b1083a29b
4 changed files with 26 additions and 9 deletions
+6
View File
@@ -5,6 +5,12 @@ All notable changes to RigDoctor are recorded here. Format follows
(`MAJOR.MINOR.PATCH`, pre-1.0). `__version__` and `pyproject.toml` must match the git
release tag (so the auto-updater, D18, can compare versions).
## [0.10.2] - 2026-05-22
### Changed
- When an Environment **Apply**/**Install** fails, the status now shows the **real reason**
(cancelled at the password prompt vs. the system rejecting the change, e.g. a BIOS/kernel-
locked PCIe ASPM policy) instead of a vague "cancelled, or needs privileges".
## [0.10.1] - 2026-05-22
### Fixed
- **Environment-page contrast.** The combo-box **drop-down list** was rendering light-on-light
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rigdoctor"
version = "0.10.1"
version = "0.10.2"
description = "Modular hardware monitoring & crash diagnostics for Linux gamers."
readme = "README.md"
requires-python = ">=3.11"
+1 -1
View File
@@ -1,3 +1,3 @@
"""RigDoctor — modular hardware monitoring & crash diagnostics for Linux gamers."""
__version__ = "0.10.1"
__version__ = "0.10.2"
+18 -7
View File
@@ -19,9 +19,20 @@ from PySide6.QtWidgets import (
from .widgets import finding_card
def _fail_reason(out: str) -> str:
"""Turn the failed command's output into a short, human reason."""
low = (out or "").lower()
if "not authorized" in low or "dismissed" in low or "authentication" in low:
return "cancelled at the password prompt"
if "operation not permitted" in low or "invalid argument" in low or "permission denied" in low:
return "the system rejected the change (it may be locked by BIOS/kernel)"
last = next((ln.strip() for ln in reversed((out or "").splitlines()) if ln.strip()), "")
return (last[:80] or "no privileges, or cancelled")
class EnvironmentPage(QWidget):
_result = Signal(object) # list[Finding]
_action_done = Signal(object) # (label, rc) — install or apply finished
_action_done = Signal(object) # (label, rc, output) — install or apply finished
def __init__(self) -> None:
super().__init__()
@@ -117,8 +128,8 @@ class EnvironmentPage(QWidget):
def _work_install(self, component) -> None:
from ..core import installer
rc, _out = installer.install_packages(list(component.apt))
self._action_done.emit((component.name, rc))
rc, out = installer.install_packages(list(component.apt))
self._action_done.emit((component.name, rc, out))
def _apply(self, fix_id: str, value: str) -> None:
if self._busy:
@@ -131,15 +142,15 @@ class EnvironmentPage(QWidget):
def _work_apply(self, fix_id: str, value: str) -> None:
from ..core import fixes
rc, _out = fixes.apply(fix_id, value)
self._action_done.emit((value, rc))
rc, out = fixes.apply(fix_id, value)
self._action_done.emit((value, rc, out))
def _on_action_done(self, result) -> None:
label, rc = result
label, rc, out = result
self._busy = False
if rc == 0:
self._status.setText(f"{label} applied — re-checking…")
self._run() # re-run so the finding reflects the new state
else:
self._run_btn.setEnabled(True)
self._status.setText(f"'{label}' failed (cancelled, or needs privileges)")
self._status.setText(f"'{label}' failed {_fail_reason(out)}")