From 78cd417d0bf3f33bd05cdefaf4e5c321a961b155 Mon Sep 17 00:00:00 2001 From: Jessey van Offeren Date: Fri, 22 May 2026 15:15:33 +0200 Subject: [PATCH] =?UTF-8?q?feat(m9):=20.deb=20package=20+=20CI=20build/pub?= =?UTF-8?q?lish=20=E2=80=94=200.35.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit packaging/make_deb.py builds rigdoctor__all.deb (Architecture: all) via dpkg-deb, no debhelper: Depends python3; Recommends python3-pyside6/pyte (GUI by default, --no-install-recommends = CLI only). Installs the package, both launchers, desktop entry + icon; postinst refreshes the desktop database. release.yml builds it as a release asset and optionally pushes to the Gitea apt registry (REGISTRY_TOKEN). Verified locally: valid .deb, packaged launcher runs 'rigdoctor --version'. Docs/README/ROADMAP/MODULES updated; M9 complete. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitea/workflows/release.yml | 20 ++++++ CHANGELOG.md | 10 +++ README.md | 20 ++++++ docs/MODULES.md | 2 +- docs/ROADMAP.md | 9 ++- packaging/make_deb.py | 116 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/rigdoctor/__init__.py | 2 +- 8 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 packaging/make_deb.py diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 9937413..c681e58 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -43,6 +43,9 @@ jobs: - name: Build self-extracting installer (.run) run: python packaging/make_run.py + - name: Build .deb + run: python packaging/make_deb.py + - name: Read version id: ver run: | @@ -103,3 +106,20 @@ jobs: "${API}/releases/${rid}/assets?name=$(basename "$f")" >/dev/null done echo "Published ${TAG}." + + - name: Publish .deb to the Gitea apt registry (optional — needs REGISTRY_TOKEN) + env: + PKG_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + set -euo pipefail + if [ -z "${PKG_TOKEN:-}" ]; then + echo "PACKAGES_TOKEN not set — skipping apt publish (the .deb is still a release asset)." + exit 0 + fi + OWNER="${{ github.repository_owner }}" + URL="${{ github.server_url }}/api/packages/${OWNER}/debian/pool/stable/main/upload" + for f in dist/*.deb; do + echo "Uploading $(basename "$f") to the apt registry…" + curl -sS --fail --user "${OWNER}:${PKG_TOKEN}" --upload-file "$f" "$URL" + done + echo "apt source: deb ${{ github.server_url }}/api/packages/${OWNER}/debian stable main" diff --git a/CHANGELOG.md b/CHANGELOG.md index 35783a7..965490e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ 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.35.0] - 2026-05-22 +### Added +- **`.deb` package (M9 / D8)** — `packaging/make_deb.py` builds a `rigdoctor__all.deb` + (pure-Python, `Architecture: all`) via `dpkg-deb`: `Depends: python3`, with the GUI deps + (`python3-pyside6`, `python3-pyte`) as **Recommends** so `sudo apt install ./rigdoctor_*.deb` + gives the full app and `--no-install-recommends` gives CLI-only. Installs the package, both + launchers, the desktop entry, and the icon. CI (`release.yml`) builds it as a **release asset** + every release, and optionally publishes it to the Gitea **apt registry** (set a `REGISTRY_TOKEN` + secret) for `sudo apt install rigdoctor`. **M9 is now complete.** + ## [0.34.0] - 2026-05-22 ### Added - **Event-based alerts (M8).** Beyond temperature + GPU-lost, RigDoctor now notifies on diff --git a/README.md b/README.md index b4cd1fb..d59663d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,26 @@ also ships a one-file **`.run`** installer (download, `chmod +x`, run). Updates accounts on the Git server (a Personal Access Token); save one via the GUI **Setup → Update access** panel or `rigdoctor login`, then `rigdoctor update` (or the sidebar button). +## Install (`.deb`, system-wide) + +Each release also ships a **`.deb`** (`Architecture: all`, M9/D8). Download it from the release +and install with apt (pulls the GUI deps — PySide6/pyte — via Recommends): + +```bash +sudo apt install ./rigdoctor__all.deb # CLI-only: add --no-install-recommends +``` + +When the apt registry is enabled on the server, you can instead add it as a source and +`sudo apt update && sudo apt install rigdoctor` (with `apt upgrade` for updates): + +```bash +curl -fsSL https://git.jesseyvanofferen.com/api/packages/jessey/debian/repository.key \ + | sudo tee /etc/apt/keyrings/gitea-rigdoctor.asc > /dev/null +echo "deb [signed-by=/etc/apt/keyrings/gitea-rigdoctor.asc] \ + https://git.jesseyvanofferen.com/api/packages/jessey/debian stable main" \ + | sudo tee /etc/apt/sources.list.d/rigdoctor.list +``` + ## Run it (dev) Stdlib-only, no install needed (target is Python ≥ 3.11; tested on 3.14): diff --git a/docs/MODULES.md b/docs/MODULES.md index d3a5720..a8c9003 100644 --- a/docs/MODULES.md +++ b/docs/MODULES.md @@ -18,7 +18,7 @@ Status: ⬜ not started · 🟦 designing · 🟨 in progress · ✅ done | M6 | Gaming env checks | Diagnostics | none | all | P2 | 🟨 | | M10 | Desktop GUI | Desktop UI | **python3-pyside6** | all | P2 | ✅ | | M11 | Tray / menu-bar applet | Desktop UI | **python3-pyside6** (+ AppIndicator on GNOME) | all | P2 | ✅ | -| M9 | Installer | (meta) | none | all | P1 | 🟨 | +| M9 | Installer (+ `.deb`) | (meta) | none | all | P1 | ✅ | | M12 | Session sharing (shared terminal) | Sharing | none (relay) | all | P3 | ✅ | | M13 | Auto-update | (core) | none (stdlib; user-local file swap) | all | P3 | ✅ | | M14 | AI assistant (explain diagnostics) | (optional) | none (stdlib urllib; Ollama or Claude) | all | P3 | ✅ | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index fe61e5b..288314f 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -67,9 +67,12 @@ Ubuntu + NVIDIA first; `.deb` distribution (see `DECISIONS.md`). Settings "Recording trigger") incl. the zero-config **game-launch watcher** (`core/watcher.py`, `rigdoctor watch`); and a **graphical first-run setup wizard** (`gui/setup_wizard.py`): environment → dependency-bundle selection → install → recording - trigger → readiness, auto-launched by install.sh and re-runnable from Settings. - *Pending:* `.deb` packaging (next bullet). -- [ ] `.deb` packaging (D8) declaring per-bundle deps incl. python3-pyside6 for Desktop UI + trigger → readiness, auto-launched by install.sh and re-runnable from Settings; and a + **`.deb`** (`packaging/make_deb.py`, `Architecture: all`, `Depends: python3`, + `Recommends: python3-pyside6/pyte`) built + published in CI (release asset + optional + Gitea apt registry). **M9 complete.** +- [x] `.deb` packaging (D8) — built via `dpkg-deb` (no debhelper); GUI deps as Recommends so + `apt install rigdoctor` includes the Desktop UI, `--no-install-recommends` = CLI only. ## Phase 5 — Breadth (later) - [ ] AMD GPU support in M1 (Steam Deck / Radeon) diff --git a/packaging/make_deb.py b/packaging/make_deb.py new file mode 100644 index 0000000..24e2c6a --- /dev/null +++ b/packaging/make_deb.py @@ -0,0 +1,116 @@ +"""Build a `.deb` for RigDoctor (M9 / D8) — dependency-light, no debhelper. + +Pure-Python app, so it's `Architecture: all`: we stage the package into dist-packages, drop the +two launchers in /usr/bin, install the desktop entry + icon, write a DEBIAN/control, and call +`dpkg-deb`. The core is stdlib (`Depends: python3`); the GUI/tray deps are **Recommends** +(`python3-pyside6`, `python3-pyte`) so `apt install rigdoctor` gives the full app by default, +while `--no-install-recommends` yields a CLI-only install. + +Run: `python packaging/make_deb.py` → `dist/rigdoctor__all.deb`. +""" + +from __future__ import annotations + +import shutil +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +DIST = ROOT / "dist" +MAINTAINER = "Jessey van Offeren " +HOMEPAGE = "https://git.jesseyvanofferen.com/jessey/rigdoctor" + + +def _version() -> str: + text = (ROOT / "src" / "rigdoctor" / "__init__.py").read_text(encoding="utf-8") + for line in text.splitlines(): + if line.startswith("__version__"): + return line.split('"')[1] + raise SystemExit("could not read __version__") + + +_LAUNCHER = """\ +#!/usr/bin/python3 +import sys +from {module} import main +sys.exit(main()) +""" + +_DESKTOP = """\ +[Desktop Entry] +Type=Application +Name=RigDoctor +Comment=Hardware monitoring & crash diagnostics for Linux gamers +Exec=rigdoctor-gui +Icon=rigdoctor +Terminal=false +Categories=System;Monitor;Utility; +StartupWMClass=rigdoctor +""" + +_CONTROL = """\ +Package: rigdoctor +Version: {version} +Architecture: all +Maintainer: {maintainer} +Section: utils +Priority: optional +Depends: python3 (>= 3.11) +Recommends: python3-pyside6, python3-pyte +Homepage: {homepage} +Description: Hardware monitoring & crash diagnostics for Linux gamers + RigDoctor monitors GPU/CPU temperatures, load, and sensors, captures crash + diagnostics while gaming, scans logs (Xid/SMART/kernel) for problems, and can + explain them in plain language. The CLI and background daemon are pure Python + (stdlib only); the optional desktop GUI and system-tray applet use PySide6, + pulled in via Recommends. Install with --no-install-recommends for CLI only. +""" + + +def _write(path: Path, text: str, mode: int = 0o644) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + path.chmod(mode) + + +def build() -> Path: + version = _version() + DIST.mkdir(exist_ok=True) + stage = DIST / f"rigdoctor_{version}_all" + if stage.exists(): + shutil.rmtree(stage) + + # Python package → dist-packages (importable system-wide), minus bytecode. + pkg_dst = stage / "usr/lib/python3/dist-packages/rigdoctor" + shutil.copytree(ROOT / "src" / "rigdoctor", pkg_dst, + ignore=shutil.ignore_patterns("__pycache__", "*.pyc")) + + # Launchers. + _write(stage / "usr/bin/rigdoctor", _LAUNCHER.format(module="rigdoctor.cli"), 0o755) + _write(stage / "usr/bin/rigdoctor-gui", _LAUNCHER.format(module="rigdoctor.gui.app"), 0o755) + + # Desktop entry + icon. + _write(stage / "usr/share/applications/rigdoctor.desktop", _DESKTOP) + icon = ROOT / "src" / "rigdoctor" / "gui" / "assets" / "rigdoctor.svg" + _write(stage / "usr/share/icons/hicolor/scalable/apps/rigdoctor.svg", + icon.read_text(encoding="utf-8")) + + # Refresh the desktop database on install/remove (best-effort). + _write(stage / "DEBIAN/postinst", + "#!/bin/sh\nset -e\nupdate-desktop-database -q 2>/dev/null || true\n", 0o755) + _write(stage / "DEBIAN/postrm", + "#!/bin/sh\nset -e\nupdate-desktop-database -q 2>/dev/null || true\n", 0o755) + _write(stage / "DEBIAN/control", + _CONTROL.format(version=version, maintainer=MAINTAINER, homepage=HOMEPAGE)) + + out = DIST / f"rigdoctor_{version}_all.deb" + subprocess.run(["dpkg-deb", "--root-owner-group", "--build", str(stage), str(out)], check=True) + shutil.rmtree(stage) + return out + + +if __name__ == "__main__": + path = build() + print(f"built {path}") + sys.exit(0) diff --git a/pyproject.toml b/pyproject.toml index c53d25a..e5d656c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "rigdoctor" -version = "0.34.0" +version = "0.35.0" description = "Modular hardware monitoring & crash diagnostics for Linux gamers." readme = "README.md" requires-python = ">=3.11" diff --git a/src/rigdoctor/__init__.py b/src/rigdoctor/__init__.py index 4b835db..7f57a7a 100644 --- a/src/rigdoctor/__init__.py +++ b/src/rigdoctor/__init__.py @@ -1,3 +1,3 @@ """RigDoctor — modular hardware monitoring & crash diagnostics for Linux gamers.""" -__version__ = "0.34.0" +__version__ = "0.35.0" -- 2.52.0