aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/superpowers/specs/2026-10-15-updates-design.md149
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.