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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
# sb-builder phase 4: hints
Date: 2026-10-20
Status: draft
Builds on: `2026-10-19-sb-builder-phase1-design.md` (helper, job model, GUI shell),
`2026-10-19-sb-builder-phase2-design.md` (inventory, hint view, slackrepo jobs)
## Goal
Manage slackrepo hint files from sb-builder: check upstream versions, update
version hints, create and delete hints, sweep `DELREQUIRES` for -current
phantom deps, and see a diff of every change with a way to revert it.
Direction set by the user: the repo is run from the desktop, the VM shell is
for upgrading Slackware only. The VM-side tools were built for a terminal; a
tool may lose its in-VM usability if that makes sb-builder simpler. So the
hint logic of mkhint moves into `sbb-remote` (Python, JSON answers) and the
upstream check moves to the desktop. mkhint is not extended.
## Facts this design rests on
Verified on the VM, in the mkhint source (`~/Programming/GIT/mkhintfile/mkhint`,
1.4.0, 2291 lines) and in slackrepo (`../GITHUB/slackrepo`):
- The VM has 607 hints, all in `hintdir`, none in subdirectories and none in
the SBo tree. 594 carry `DELREQUIRES` (from `mkhint -F`), 13 carry
`VERSION` + `ARCH` + `DOWNLOAD` + `MD5SUM`, one carries `OPTIONS`. No hint
carries `NODOWNLOAD` or a `DOWNLOAD_x86_64`/`MD5SUM_x86_64` line.
- slackrepo reads `DOWNLOAD="no"` as "no download" and ignores `NODOWNLOAD`.
`DOWNLOAD_$ARCH` replaces `DOWNLOAD` when set
(`libexec/functions.d/parsefunctions.sh:457`), so `DOWNLOAD="no"` next to a
`DOWNLOAD_x86_64` line has no effect.
- mkhint's version update: back up to `.bak`, replace the old version by the
new one everywhere in the file, also the `_` -> `-` form when either version
contains `_`, also the dotless form of a dotted version inside URLs as a
whole number (`_swap_dotless_url_version`), then for `DOWNLOAD`/`MD5SUM` and
`DOWNLOAD_x86_64`/`MD5SUM_x86_64`: download the first URL and recompute its
md5, keep the md5 of continuation URLs unless the user typed a new URL.
`UNSUPPORTED`/`UNTESTED` values are skipped.
- mkhint's new hint from `.info`: copy the `.info`, drop `PRGNAM`, `HOMEPAGE`,
`MAINTAINER`, `EMAIL`, comment out `REQUIRES`, set `ARCH="x86_64"`, append
`DELREQUIRES` for phantom deps in `REQUIRES`, then apply a version as above
when one is given.
- mkhint's `-F`: for every `.info` in the repo whose `REQUIRES` contains a
phantom dep, merge those deps into the hint's `DELREQUIRES` (union, first
seen order, `.bak` only when something changes, minimal hint created when
absent).
- The VM's nvchecker config has 65 stanzas named after the packages, plus a
`[__config__]` with oldver, newver and keyfile. nvchecker v2.22 is
installed on the desktop, with a GitHub token in
`~/.config/nvchecker/keys.toml`. Its newver file is
`{"version": 2, "data": {"<name>": {"version": "..."}, ...}}`.
- The phantom deps list on the VM (`/root/.config/mkhint/phantom-deps`) has
18 entries.
- Two packages use bundled-dep manifests: neovim (url mode) and openvino
(sha mode, GitHub submodules). That code is about 470 lines of mkhint.
## Scope
In: upstream check, version update, new hint, delete hint, fix-current,
`--no-dl` as `DOWNLOAD="no"`, diff and revert.
Out, for good (the Hints screen covers them): mkhint `--list`, `--versions`,
`--review`, `--strip-version`, `--info`, `--clean`.
Out, later: bundled-dep reconcile for neovim and openvino (phase 4b, which
then removes mkhint and nvchecker from the VM), generating nvchecker stanzas
for new packages (add them to `nvchecker.toml` by hand), free text hint
editing, migration of `NODOWNLOAD=yes` lines (none exist).
## Part 1: upstream check, on the desktop
- `nvchecker.toml` in the repo root: the VM's 65 stanzas, without
`[__config__]`. Stanza name = package name. Git-tracked, edited by hand.
- `sb_builder/upstream.py` runs nvchecker through `QProcess` (as `remote.py`
runs `virsh`) with a fixed argv: `nvchecker -c <tmp>/nvchecker.toml`. The
temp dir holds a generated config: a `[__config__]` with `keyfile` (from
the GUI config) and `newver = "<tmp>/new.json"`, followed by the repo
file's text. No oldver, no `nvtake`: the comparison is against the hints.
- Result: `{pkg: version}` from the newver file. Failure: the exit status and
stderr, shown as a `nvchecker error` state.
- A package is outdated when its upstream version differs from the hint
`VERSION`, or from the `.info` `VERSION` when the hint has none. Versions
are compared as strings; no ordering is attempted.
GUI config gains `nvchecker_keyfile`, default
`~/.config/nvchecker/keys.toml` (expanded). A missing keyfile is not an
error: nvchecker runs without a token.
## Part 2: helper (`sbb-remote` 0.5.0)
### Config
New key `phantom_deps`, list of strings, default `[]`. The VM config gets
the 18 entries at deploy. `remote/config.example.toml` shows the key.
### Arguments
- package: `PKGNAME` and present in the repo (as for slackrepo jobs).
- version: `^[A-Za-z0-9._+-]+$`, at most 64 characters.
- hint path: always `<hintdir>/<pkg>.hint`. If `find_hint` returns a
different file for the package (a hint earlier in slackrepo's search
order), every hint command refuses: "hint lives at <path>, not managed".
- packages with a bundled-dep manifest are named in config key
`manifest_packages`, default `["neovim", "openvino"]`. `hint-update` and
`hint-new` with a version refuse them: "bundled deps: use mkhint until
phase 4b".
### Job kinds
- `hint-update <pkg>=<ver>... [--no-dl]`: per package, in order: back up the
hint to `.bak`, apply the version (below), write atomically. A failure
restores the hint from the `.bak` it just wrote, logs `<pkg>: failed: <why>`
and continues with the next package. The last log line is
`SBB-RESULT {"ok": [...], "failed": [...]}`. Exit status 1 when any
package failed.
- `hint-new <pkg> [<ver>] [--no-dl]`: refuses when the hint exists. Builds
the hint from the `.info` as mkhint does, phantom deps from
`phantom_deps`, then applies the version when given. No `.bak` (nothing to
back up); revert means delete.
- `fix-current`: mkhint's `-F` with `phantom_deps`. Logs one line per
changed hint, then `SBB-RESULT {"ok": [...], "failed": []}`.
These jobs never sign, so they do not check the signing key.
### Applying a version
1. Old version = hint `VERSION`, else `.info` `VERSION`.
2. Replace old by new everywhere in the file. When either contains `_`, also
the `-` forms. When old contains `.`, also its dotless form inside URLs,
as a whole number (no digit before or after), so md5 hex is untouched.
3. Without `--no-dl`: for `DOWNLOAD`/`MD5SUM` and
`DOWNLOAD_x86_64`/`MD5SUM_x86_64`, split the values on whitespace (hint
values may continue over lines with `\`). Skip a pair whose value is empty,
`UNSUPPORTED` or `UNTESTED`. Download the first URL with `urllib`
(timeout 60 s, streamed into `hashlib.md5`, nothing kept on disk) and
replace its md5; keep the md5s of the other URLs. Write values back one
URL per line with ` \` continuation, as mkhint does.
4. With `--no-dl`: nothing is downloaded. Every `DOWNLOAD*` and `MD5SUM*`
assignment is removed (whole multi-line values) and `DOWNLOAD="no"` is
written, because `DOWNLOAD_$ARCH` would override it.
Hint text is edited as text, not regenerated, so comments, order and other
keys stay as they are. Assignments are located with the rules the helper's
`parse_assignments` already uses (`KEY="..."` at line start, values may span
lines inside the quotes).
### Commands (answer at once, not jobs)
- `hint-diff <pkg>`: `{"bak": text|null, "current": text|null}`. Both null
is an error ("no backup").
- `hint-revert <pkg>`: when a `.bak` exists, move it over the hint (also
recreating a deleted hint); when there is no `.bak` and the hint exists,
delete the hint (reverting a `hint-new`). Returns `{"ok": true}`.
- `hint-delete <pkg>`: move the hint to `.bak`. Errors when there is no hint.
`.bak` is one level deep and overwritten by each change, the mkhint
convention.
### Version
`VERSION` 0.5.0 in `remote/sbb-remote` and `sb_builder/__init__.py`.
## Part 3: GUI
### Hints screen (`sb_builder/hints_tab.py`, ribbon entry and `Open > Hints`)
- Rows: every package with a stanza in `nvchecker.toml` or a hint in
`hintdir`. Columns: checkbox, package, hint version, `.info` version,
upstream, state. Versions come from the existing `inventory` call.
- States: `outdated`, `current`, `no stanza`, `not checked`,
`nvchecker error` (stderr in the tooltip). Filter chips per state and a
text filter on the name, as in the Packages tab.
- Buttons:
- **Check upstream**: runs Part 1 over the whole file, fills the upstream
column, checks the outdated rows. Results live for the session.
- **Update selected**: starts `hint-update pkg=ver ...` for the checked
rows that have an upstream version; a **No download** checkbox adds
`--no-dl`.
- **Fix current**: starts `fix-current`.
- **Delete hint**: confirm dialog, then `hint-delete` for the current row.
- Jobs go through the Jobs tab like every other job.
### After a hint job
When a `hint-update`, `hint-new` or `fix-current` job ends, the GUI reads
its `SBB-RESULT` line and opens a dialog with one entry per changed package:
a unified diff (`difflib`) of `hint-diff`, and a **Revert** button per
package. Failed packages are listed with their reason. The dialog ends with
**Build these**, which starts the usual slackrepo preview for the changed
packages (`update` when built, `build` otherwise, as the Packages tab
decides), and **Close**. After Revert or Delete the inventory is reloaded.
For `fix-current` the dialog lists the packages without opening every diff
(it can touch hundreds); clicking one shows its diff.
### Packages detail pane
- No hint: **New hint**, with an optional version field and a **No
download** checkbox, starts `hint-new`.
- Hint present: **Open in Hints** selects the package in the Hints screen.
## Error handling
- Bad arguments, unmanaged hint paths and manifest packages are refused by
the helper before a job starts; the GUI shows the message.
- A download failure (HTTP status, timeout, DNS) fails that package only and
leaves its hint as it was.
- Writes are atomic (temp file in the same directory, `os.replace`).
- nvchecker missing or failing on the desktop affects only the upstream
column; the rest of the screen works.
- A job without an `SBB-RESULT` line (killed, lost) opens no dialog; the
log shows what happened and `hint-diff` still works per package.
## Testing
- `test_remote.py`: an `http.server` thread serves fixture files for md5.
Version swap forms (plain, `_`/`-`, dotless in URL only, md5 hex not
touched), multi-line `DOWNLOAD`/`MD5SUM` with continuation md5s kept,
`UNSUPPORTED` skipped, `--no-dl` removing `DOWNLOAD_x86_64` and writing
`DOWNLOAD="no"`, a download failure restoring the hint while the next
package still updates, `SBB-RESULT` contents, `hint-new` from `.info`
with phantom deps, `fix-current` merge and idempotence (second run
changes nothing and writes no `.bak`), new -> revert, update -> diff ->
revert, delete -> revert, manifest package refusal, unmanaged hint path
refusal, bad version and package rejection.
- `test_upstream.py`: generated config text, newver parsing from a fixture,
outdated comparison (hint version, `.info` fallback). No network.
- `test_gui.py`: Hints screen with a fake remote and injected upstream
results (states, checked rows, Update selected argv), diff dialog Revert
call.
## Deploy (asks first)
1. `./install.sh --remote` with helper 0.5.0.
2. Add `phantom_deps` (the VM's 18 entries) to `/etc/sb-builder/config.toml`.
3. mkhint, nvchecker and `/root/.config/{mkhint,nvchecker}` stay on the VM
until phase 4b.
|