diff options
| -rw-r--r-- | docs/superpowers/specs/2026-10-15-updates-design.md | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-15-updates-design.md b/docs/superpowers/specs/2026-10-15-updates-design.md new file mode 100644 index 0000000..e6a3458 --- /dev/null +++ b/docs/superpowers/specs/2026-10-15-updates-design.md @@ -0,0 +1,149 @@ +# Updates module + +A drawer tile and page that answer one question per row: what do I do next +to bring this machine up to date. It replaces two places that showed the same +facts less clearly: the conky updates card, a copy of `nvtable`'s table where +installed-behind and repo-behind share a row and are told apart by colour, +and the homepage ChangeLog widget, which shows the latest upstream -current +entry rather than what this machine is missing. + +## Sources and what they mean + +Four versions per tracked package: + +- **U, upstream**: `~/.config/nvchecker/new_ver.json`. +- **R, recipe**: `VERSION=` in `<pkg>.info` under the three repos of + `~/Programming/GIT/slackbuilds-superrepo` (`sbo-slackbuilds`, + `my-slackbuilds`, `Slackware-Pentesting-Suite`), falling back to the + SlackBuilds.org clone at `/var/lib/sbopkg/SBo-git`. A recipe from that clone + is tagged `SBo`: it is not the user's SlackBuild, and a bump of it happens + through a slackrepo `.hint` file on the build VM, which is not visible from + here. +- **B, built**: `PACKAGES.TXT` of the user's binary repo, the directory named + by the `danix` entry of `MIRRORPLUS` in `/etc/slackpkg/slackpkgplus.conf`. +- **I, installed**: `/var/log/packages`, then appimage-updater's + `installed.json`, then `flatpak list --app`, in that order, as `nvtable` + does. + +nvtable's two alias tables (stanza name to installed name, stanza name to +SlackBuild directory) are ported into the script as dicts. + +For Slackware itself, the mirror is the `file://` line active in +`/etc/slackpkg/mirrors`. Its `PACKAGES.TXT` and `ChangeLog.txt` are compared +against `/var/log/packages`. `/var/lib/slackpkg/ChangeLog.txt` is not used: +`slackpkg update` replaces it without installing anything, so a diff against +it reads "nothing pending" after an update that was never applied. + +## Sections + +In page order. A package appears in exactly one section, the first that +matches, so a row shows the next step and nothing after it. + +1. **Slackware**, three groups, all from the mirror: + - *Upgrade*: an installed package whose name is on the mirror with a + different version or build. + - *New*: the ChangeLog's newest line for the package says `Added.`, it is + on the mirror, and it is not installed. These are slackpkg's + `install-new` semantics. + - *Removed*: the ChangeLog's newest line for the package says `Removed.`, + it is installed, and it is not on the mirror. The row carries the date of + that ChangeLog entry. + + Installed packages whose build tag matches a pattern in + `/etc/slackpkg/blacklist` (`_SBo`, `_danix`, `alien` and the rest) are + skipped in all three, as slackpkg skips them; so are blacklisted names. +2. **Upgrade here**: B newer than I. The package is built and sitting in the + repo. +3. **Awaiting build**: R is the user's (not `SBo`), R equals U, and B is older + than U. The SlackBuild is bumped and only needs building. +4. **Upstream newer**: U newer than both R and B. The SlackBuild or its hint + needs bumping, then a build. `SBo` rows land here until the build appears + in B. +5. **App updates**: I comes from AppImage or flatpak and is older than U. + +"Newer" and "older" are version order, never equality: an installed 2.1.278 +against an SBo recipe of 2.1.267 is not an upgrade. A tracked package that is +not installed and whose recipe is current is not shown. + +## `updatesctl` + +`desktop/modules/updates/updatesctl`, `/usr/bin/python3`, standard library +only. It prints one JSON document: + + { + "checked": <mtime of new_ver.json, epoch seconds>, + "slackware": {"upgrade": [...], "new": [...], "removed": [...]}, + "upgrade": [...], "build": [...], "upstream": [...], "apps": [...], + "errors": {"mirror": "unreachable", ...} + } + +Rows are `{name, repo, from, to}`; Slackware rows are `{name, from, to}`, +`{name, version}` and `{name, date}`. `repo` is the label nvtable shows +(`personal`, `my-sbo`, `pentesting`, `SBo`, `AppImage`, `flatpak`). + +**Version order** is a small `vercmp`: split on non-alphanumerics, compare +numeric chunks as numbers and alphabetic chunks as strings, numbers before +letters. It replaces nvtable's normalised equality, including its rule that +an installed version extending upstream at a component boundary (a kernel +suffix, a git hash) counts as equal; that rule is kept as a prefix check +before ordering. + +**NFS reads** (`PACKAGES.TXT` of the repo and of the mirror, the mirror's +`ChangeLog.txt`) go through `subprocess.run(["cat", path], timeout=5)`. The +mount is `hard`, so a read during a NAS outage blocks; the timeout kills the +child, the script carries on, and that source is reported in `errors` while +the sections that do not need it still fill. + +**`--refresh`** runs `nvchecker -c ~/.config/nvchecker/nvchecker.toml` +synchronously first, then prints as above. That config already names the +GitHub keyfile. nvtake is not run: this model never reads `old_ver.json`. + +## Module + +`desktop/modules/updates/UpdatesModule.qml`, name `updates`. + +- **Icon**: ``, `linux-slackware`, present in Inconsolata Nerd Font. +- **Tile line**: `3 to install · 2 to build`, counting Slackware (all three + groups) plus Upgrade here as "to install", and Awaiting build as "to build". + `up to date` when both are zero. `checking…` before the first result. +- **Active** (accent) when there is anything to install. +- **Freshness**: when the tile is created, which is when the drawer opens, the + module runs `updatesctl`. If `checked` is more than 30 minutes old it then + runs `updatesctl --refresh` and the page shows `checking…` until it + returns. Nothing runs while the drawer is closed, the same gating as KDE + Connect. +- **Page**: the five sections in order, each with a heading and a count, each + hidden when empty. Rows read `name repo from → to`. A footer reads + `checked 12 min ago` and holds a refresh button that runs `--refresh`. + An entry in `errors` shows as one dim line under the section it empties, + e.g. `mirror unreachable`. Read-only: no install or build buttons. + +## Retiring conky's card + +Once the user trusts the tile, `bin/updates-sample.sh` and +`widgets/updates.lua` are deleted from `conky-theme-udt`, with the card's +slot, as a separate commit in that repo. `nvtable` stays in +`danix-nvchecker` for the shell; this repo does not depend on it. + +## Testing + +`desktop/modules/updates/test_updatesctl.py`, run with `/usr/bin/python3`: + +- `vercmp`: 1.14.10 > 1.14.9, 2.1.278 > 2.1.267, 2026_10_02 > 2026_09_12, + a kernel suffix equal to its base. +- Section assignment on fixture directories built in a temp dir, one row per + case: built and newer goes to Upgrade here, bumped and unbuilt to Awaiting + build, unbumped to Upstream newer, an SBo recipe behind upstream to + Upstream newer, an installed version ahead of SBo to nothing, an AppImage + behind to App updates. +- Slackware: a mirror with one changed build, one `Added.` package and one + `Removed.` package, plus a blacklisted `_SBo` build that must not appear. +- An NFS path that times out: a fifo with no writer, read with a short + timeout, must report `unreachable` without hanging the test. + +The tests must be shown to fail on a broken input before they are trusted. + +## Not in scope + +Installing, building, or bumping from the page. Showing upstream lag of the +NFS mirror itself. Reading slackrepo hint files from the build VM. |
