aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-10-15-updates-design.md
blob: e6a34580d05b0becc596ebab6057808e4f567312 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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.