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>
This commit is contained in:
@@ -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_<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)
|
||||
Reference in New Issue
Block a user