Files
rigdoctor/packaging/make_deb.py
T
jessey 78cd417d0b
tests / core (pull_request) Successful in 13s
tests / gui-smoke (pull_request) Successful in 28s
feat(m9): .deb package + CI build/publish — 0.35.0
packaging/make_deb.py builds rigdoctor_<ver>_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) <noreply@anthropic.com>
2026-05-22 15:15:33 +02:00

117 lines
3.9 KiB
Python

"""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_<version>_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 <jjvanofferen@gmail.com>"
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)