aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md13
-rw-r--r--README.md20
-rw-r--r--docs/superpowers/specs/2026-10-20-sb-builder-phase4-design.md132
3 files changed, 104 insertions, 61 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 9113e34..9a8918c 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -17,7 +17,8 @@ Design and phasing: `docs/superpowers/specs/`. Phase 1 (core) is
- Every GUI action is `ssh <target> sbb-remote <cmd> [args]`; the helper
answers with JSON. `sb_builder/remote.py` is the only code that runs ssh
- (and local `virsh`).
+ (and local `virsh`). `sb_builder/upstream.py` runs the local `nvchecker`
+ over the repo's `nvchecker.toml`; the VM never sees the GitHub token.
- Trust boundary: the helper runs only a fixed table of job kinds and builds
argv itself from validated args. Never pass shell strings or paths from the
GUI to the helper.
@@ -32,10 +33,16 @@ Design and phasing: `docs/superpowers/specs/`. Phase 1 (core) is
- Hints are parsed (`shlex`), never sourced. `sb_builder/hints.py` mirrors
slackrepo's own rules (`../GITHUB/slackrepo/libexec/functions.d/parsefunctions.sh`);
check that file when changing it.
+- Hint editing lives in the helper (`hint-update`, `hint-new` jobs run the
+ helper's own `_hint` command): text edits, `.bak` before each
+ write, a final `SBB-RESULT {json}` log line read back with `job-result`.
+ It replaces mkhint, which stays on the VM for neovim and openvino
+ (bundled deps, `manifest_packages`) and for the `DELREQUIRES` hints
+ sbshadow regenerates with `mkhint -F` after every sync, until phase 4b.
- Jobs never get input. Any tool run as a job must be non-interactive
(`--yes`/`--no` style flags). If a tool needs a flag it lacks, add it in
- that tool's own repo (e.g. `slackrepo_setup` for sbshadow, `mkhintfile`
- for mkhint) rather than working around it here.
+ that tool's own repo (e.g. `slackrepo_setup` for sbshadow) rather than
+ working around it here.
## Conventions
diff --git a/README.md b/README.md
index e6434c9..e1f93cc 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# sb-builder
Desktop GUI for managing a Slackware SlackBuilds build VM from one place. It
-runs slackrepo builds, overlay syncs (sbpull, sbshadow), hintfile work
-(mkhint) and SBo orphan discovery on the VM over ssh, as background jobs that
+runs slackrepo builds, overlay syncs (sbpull, sbshadow), hint file updates
+and SBo orphan discovery on the VM over ssh, as background jobs that
keep running when the GUI disconnects and can be reattached later. A
dashboard shows VM and repository state, and the GUI can start the VM through
libvirt when it is off.
@@ -12,10 +12,9 @@ Work in progress. See `docs/superpowers/specs/` for the design.
## Requirements
- Desktop: Python 3.11 or newer, PyQt6 (with QtDBus), ssh access to the VM,
- optionally `virsh` for starting the VM
+ optionally `virsh` for starting the VM and `nvchecker` for upstream checks
- VM: Python 3.11 or newer, root ssh login, the
[slackrepo_setup](https://forge.slackware.nl/danix/slackrepo_setup) tools (`sbpull`, `sbshadow`)
- and mkhint
## Install
@@ -58,6 +57,19 @@ Every key is optional. `File > Reload config` rereads the GUI config.
the package's last attempt (slackrepo keeps one per command: `build.log`,
`update.log`, `rebuild.log`, `remove.log`), where it reports the failure,
with its `resource.log` (load, memory, tmp over the build).
+- **Hints** (ribbon): every package with a hint or an nvchecker stanza, with
+ hint, `.info` and upstream version. **Check upstream** runs nvchecker on the
+ desktop over `nvchecker.toml` in this repo (stanza name = package name, edit
+ it by hand to track a package; `hints.nvchecker_keyfile` gives it a GitHub
+ token) and ticks the outdated packages. **Update selected** rewrites their
+ hints on the VM (version, URLs, md5 of the downloaded source; **no download**
+ writes `DOWNLOAD="no"` instead; a hint sbshadow wrote, `DELREQUIRES` only,
+ gets the `.info` version lines added). **Delete hint** keeps a `.bak`. The Packages detail pane has **New hint** for a package
+ without one.
+- After a hint job a dialog shows the diff of each changed hint, with
+ **Revert** per package and **Build these** (a slackrepo update preview).
+ neovim and openvino are refused for now: their bundled deps still need
+ mkhint on the VM.
- Real runs sign the repository. When the package signing key is locked
(status bar 🔒), the GUI asks for its passphrase first and hands it to the
VM's gpg-agent; it is never stored.
diff --git a/docs/superpowers/specs/2026-10-20-sb-builder-phase4-design.md b/docs/superpowers/specs/2026-10-20-sb-builder-phase4-design.md
index df360eb..c45c720 100644
--- a/docs/superpowers/specs/2026-10-20-sb-builder-phase4-design.md
+++ b/docs/superpowers/specs/2026-10-20-sb-builder-phase4-design.md
@@ -1,15 +1,15 @@
# sb-builder phase 4: hints
Date: 2026-10-20
-Status: draft
+Status: approved
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.
+version hints, create and delete hints, 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
@@ -23,9 +23,11 @@ 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
+ the SBo tree. 594 carry only `DELREQUIRES`, 13 carry
`VERSION` + `ARCH` + `DOWNLOAD` + `MD5SUM`, one carries `OPTIONS`. No hint
- carries `NODOWNLOAD` or a `DOWNLOAD_x86_64`/`MD5SUM_x86_64` line.
+ carries `NODOWNLOAD`. Pinned hints carry `DOWNLOAD_x86_64`/`MD5SUM_x86_64`
+ lines, mostly empty (`claude-code`: `DOWNLOAD="UNSUPPORTED"` with the URL in
+ `DOWNLOAD_x86_64`), and `ARCH="x86_64"`.
- 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
@@ -44,12 +46,24 @@ Verified on the VM, in the mkhint source (`~/Programming/GIT/mkhintfile/mkhint`,
- 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
+ absent). It edits only the `DELREQUIRES` line. sbshadow
+ (slackrepo_setup) runs it after every sync: that is where the 594
+ DELREQUIRES-only hints come from, and why they exist (slackrepo must not
+ build deps -current does not need). Until now sbshadow ran it on `--no`
+ previews too.
+- The VM's nvchecker config has 64 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": "..."}, ...}}`.
+ `~/.config/nvchecker/keys.toml`. With `--logger json` it prints one JSON
+ object per line on stdout: `{"name", "version", "event": "updated"}` per
+ result, `{"name", "error", "level": "error"}` per failure; it exits 0 when
+ entries fail and needs no oldver or newver to run.
+- A DELREQUIRES-only hint has no `VERSION`; mkhint's update cannot bump it.
+ Updating one pins it from the `.info` first; the next `mkhint -F` keeps the
+ pin. A package with a phantom dep always has such a hint, so `hint-new`
+ never sees one.
+- slackrepo `update <item>` builds an item that exists, built or not
+ (`update_command` calls `build_command`).
- 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
@@ -57,33 +71,37 @@ Verified on the VM, in the mkhint source (`~/Programming/GIT/mkhintfile/mkhint`,
## Scope
-In: upstream check, version update, new hint, delete hint, fix-current,
+In: upstream check, version update, new hint, delete hint,
`--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
+Out, later (phase 4b, which then removes mkhint and nvchecker from the VM):
+bundled-dep reconcile for neovim and openvino, and a helper `fix-current`
+that sbshadow calls in place of `mkhint -F`, with the phantom deps list
+moved into the helper config as its single source. Also out: 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
+- `nvchecker.toml` in the repo root: the VM's 64 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.
+ runs `virsh`) with a fixed argv: `nvchecker -c <tmp>/nvchecker.toml
+ --logger json`. The temp dir holds a generated config: a `[__config__]`
+ with `keyfile` (from the GUI config, when the file exists), followed by the
+ repo file's text. No oldver, newver or `nvtake`: the comparison is against
+ the hints.
+- Result: `{pkg: version}` and `{pkg: error}` from the JSON log. A package
+ without a result, or a failed run (exit status, stderr), shows as
+ `nvchecker error` with the reason in the tooltip.
- 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
+GUI config gains `[hints] nvchecker_keyfile`, default
`~/.config/nvchecker/keys.toml` (expanded). A missing keyfile is not an
error: nvchecker runs without a token.
@@ -91,8 +109,7 @@ error: nvchecker runs without a token.
### 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.
+New key `manifest_packages` (below). `remote/config.example.toml` shows it.
### Arguments
@@ -108,27 +125,36 @@ the 18 entries at deploy. `remote/config.example.toml` shows the key.
### 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-update <pkg>=<ver>... [--no-dl]`: per package, in order: compute the
+ new text (apply the version, below), then back the hint up to `.bak` and
+ write atomically. A failure (download, missing `.info` VERSION) leaves the
+ hint and its old `.bak` untouched, logs `<pkg>: failed: <why>` and
+ continues with the next package. A package without a hint gets a new one
+ built as `hint-new` does. The last log line is
+ `SBB-RESULT {"ok": [pkg...], "failed": [[pkg, why]...]}`. 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": []}`.
+ the hint from the `.info` as mkhint does, without phantom deps (see the
+ facts), then applies the version when given. No `.bak` (a stale
+ one is removed); revert means delete.
-These jobs never sign, so they do not check the signing key.
+These jobs never sign, so they do not check the signing key. Their argv is
+the helper itself: `python3 sbb-remote _hint <kind> <args>`, validated again
+there. Job ids now allow a hyphenated kind (`...-hint-update`).
+
+`job-result <id>`: the last `SBB-RESULT` object of a job's log.
### 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.
+1. A hint without `VERSION` is first pinned from the `.info`: its
+ `VERSION`, `DOWNLOAD*` and `MD5SUM*` lines, plus `ARCH="x86_64"` when the
+ hint has no `ARCH`, are put in front of the hint's text. Old version = the
+ hint's `VERSION`. With no version given (`hint-new <pkg>`), steps 2 and 3
+ are skipped.
+2. Replace old by new everywhere in the file except inside `MD5SUM*`
+ values. When either contains `_`, also the `-` forms. When old contains
+ `.`, also its dotless form inside URLs, as a whole number (no digit before
+ or after).
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,
@@ -141,9 +167,8 @@ These jobs never sign, so they do not check the signing key.
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).
+keys stay as they are. An assignment is `KEY="..."` (the value may span
+lines inside the quotes) or `KEY=word`, at line start.
### Commands (answer at once, not jobs)
@@ -177,22 +202,21 @@ convention.
- **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
+When a `hint-update` or `hint-new` 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.
+**Build these**, which starts `slackrepo update --preview` for the changed
+packages that were not reverted (update builds unbuilt items too), and
+**Close**. Revert is offered once per package: a second one would delete
+the restored hint. 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.
+Each diff is fetched when its package is selected.
### Packages detail pane
@@ -219,13 +243,13 @@ For `fix-current` the dialog lists the packages without opening every diff
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 ->
+ package still updates, `SBB-RESULT` contents, `hint-new` from `.info`,
+ a DELREQUIRES-only hint pinned from the `.info`, 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_upstream.py`: generated config, JSON log parsing, states (hint
+ version, `.info` fallback), the repo file has no `[__config__]`, `Checker`
+ against a fake `nvchecker` on `PATH` and a missing one. 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.
@@ -233,6 +257,6 @@ For `fix-current` the dialog lists the packages without opening every diff
## 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`.
+2. slackrepo_setup's sbshadow, which no longer runs `mkhint -F` on `--no`.
3. mkhint, nvchecker and `/root/.config/{mkhint,nvchecker}` stay on the VM
until phase 4b.