- install.sh: no-root user-local install (private venv + ~/.local/bin launchers + desktop entry); --ref <tag> to install a specific release, --uninstall to remove; auto-installs the python3-venv prerequisite with consent - packaging/make-run.sh: build a self-extracting .run installer (makeself) bundling the wheel + install.sh; release workflow builds and attaches it - M13 self-update apply: `rigdoctor update` runs an authenticated pip upgrade (rigdoctor[gui] @ git+https://oauth2:<token>@...@<tag>), token scrubbed; GUI sidebar "Update to v…" button applies it and prompts to restart - version 0.0.7, CHANGELOG, docs (M9/M13, ROADMAP, README install section) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
"""RigDoctor — modular hardware monitoring & crash diagnostics for Linux gamers."""
|
||||
|
||||
__version__ = "0.0.6"
|
||||
__version__ = "0.0.7"
|
||||
|
||||
@@ -266,8 +266,14 @@ def cmd_update(args) -> int:
|
||||
print(f"Update available: {tag} (current v{__version__}).")
|
||||
if args.check:
|
||||
return 0
|
||||
print("Self-update (apply) isn't wired yet — coming with the install script.")
|
||||
return 0
|
||||
print(f"Installing {tag}…")
|
||||
rc, out = updates.apply_update(tag)
|
||||
print(out[-2000:])
|
||||
if rc == 0:
|
||||
print(f"\nUpdated to {tag}. Restart RigDoctor to use the new version.")
|
||||
return 0
|
||||
print(f"\nUpdate failed (exit {rc}).")
|
||||
return rc
|
||||
|
||||
|
||||
def cmd_report(args) -> int:
|
||||
|
||||
@@ -9,6 +9,8 @@ handles detection and exposes a clear state for the UI.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
@@ -73,3 +75,23 @@ def update_state(timeout: float = 5.0) -> tuple[str, str | None]:
|
||||
if tag and is_newer(tag):
|
||||
return (AVAILABLE, tag)
|
||||
return (UP_TO_DATE, tag)
|
||||
|
||||
|
||||
def apply_update(tag: str) -> tuple[int, str]:
|
||||
"""Self-update the current (user-local) install to `tag` via authenticated pip.
|
||||
|
||||
Installs `rigdoctor[gui] @ git+https://oauth2:<token>@…/rigdoctor.git@<tag>` into
|
||||
the running environment. Returns (exit_code, output) with the token scrubbed.
|
||||
"""
|
||||
token = load_token()
|
||||
if not token:
|
||||
return (1, "No update token configured. Run `rigdoctor login`.")
|
||||
host = GITEA_BASE.split("://", 1)[1]
|
||||
ref = f"rigdoctor[gui] @ git+https://oauth2:{token}@{host}/{REPO}.git@{tag}"
|
||||
cmd = [sys.executable, "-m", "pip", "install", "--upgrade", ref]
|
||||
try:
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)
|
||||
out = (proc.stdout + proc.stderr).replace(token, "***")
|
||||
return (proc.returncode, out)
|
||||
except (subprocess.SubprocessError, OSError) as exc:
|
||||
return (1, str(exc).replace(token, "***"))
|
||||
|
||||
@@ -4,8 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import threading
|
||||
|
||||
from PySide6.QtCore import Qt, QUrl, Signal
|
||||
from PySide6.QtGui import QDesktopServices
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import (
|
||||
QButtonGroup,
|
||||
QFrame,
|
||||
@@ -34,7 +33,8 @@ _PLACEHOLDERS = {
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
_update_checked = Signal(object) # latest tag (str) or None
|
||||
_update_checked = Signal(object) # (state, tag)
|
||||
_update_applied = Signal(int) # pip exit code
|
||||
|
||||
def __init__(self, interval: float = 1.0) -> None:
|
||||
super().__init__()
|
||||
@@ -72,7 +72,9 @@ class MainWindow(QMainWindow):
|
||||
self._worker.start()
|
||||
|
||||
# Background update check (M13); result lands in the sidebar.
|
||||
self._latest_tag = None
|
||||
self._update_checked.connect(self._show_update_state)
|
||||
self._update_applied.connect(self._on_update_applied)
|
||||
threading.Thread(target=self._check_updates, daemon=True).start()
|
||||
|
||||
def _build_sidebar(self) -> QFrame:
|
||||
@@ -117,16 +119,33 @@ class MainWindow(QMainWindow):
|
||||
self._update_btn = QPushButton()
|
||||
self._update_btn.setObjectName("PrimaryButton")
|
||||
self._update_btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self._update_btn.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(updates.RELEASES_PAGE)))
|
||||
self._update_btn.clicked.connect(self._apply_update)
|
||||
self._update_btn.setVisible(False)
|
||||
v.addWidget(self._update_btn)
|
||||
return bar
|
||||
|
||||
def _apply_update(self) -> None:
|
||||
if not self._latest_tag:
|
||||
return
|
||||
self._update_btn.setEnabled(False)
|
||||
self._update_label.setText("updating…")
|
||||
tag = self._latest_tag
|
||||
threading.Thread(target=lambda: self._update_applied.emit(updates.apply_update(tag)[0]), daemon=True).start()
|
||||
|
||||
def _on_update_applied(self, rc: int) -> None:
|
||||
if rc == 0:
|
||||
self._update_label.setText("updated — restart RigDoctor")
|
||||
self._update_btn.setVisible(False)
|
||||
else:
|
||||
self._update_label.setText("update failed")
|
||||
self._update_btn.setEnabled(True)
|
||||
|
||||
def _check_updates(self) -> None:
|
||||
self._update_checked.emit(updates.update_state())
|
||||
|
||||
def _show_update_state(self, result) -> None:
|
||||
state, tag = result
|
||||
self._latest_tag = tag
|
||||
self._update_btn.setVisible(False)
|
||||
if state == updates.NO_TOKEN:
|
||||
self._update_label.setText("connect to update server")
|
||||
|
||||
Reference in New Issue
Block a user