# sb-builder: named builds and per-package results Date: 2026-10-21 Status: approved ## Goal Three fixes to how slackrepo jobs run: 1. A never-built package can be built from the GUI by name. 2. A job that builds several packages says which ones failed, and its status reflects that. 3. Packages whose SlackBuild is gone can be removed without an Update all. One job keeps running every package given to it. Splitting into one job per package was considered and rejected: slackrepo already orders deps across the whole set, builds a shared dep once and goes on past a failed item. Splitting would add one preview, one gpg unlock and one publish per package, for nothing but the ability to cancel a single package. ## 1. Named packages run `build`, not `update` slackrepo looks up the items named to `update` in its package database only (`find_items -p`, `parsefunctions.sh`), so a package never built before ends `bad: No matches found for update command`. Deps of named items use `-ps` (database, then the SlackBuild tree) and are found. `build` on named items does what `update` would: `build_command` sets an outdated item to `update` and rebuilds it, a new one to `add` and builds it, an up-to-date one to `ok` and skips it. The one thing only `update` does is remove the packages of an item whose SlackBuild is gone (e.g. moved to the official tree). That cannot happen for a named item: `kind_slackrepo` accepts only names with a SlackBuild dir, and the Packages tab lists only those. Changes (GUI only, the helper is unchanged): - `HintResultDialog.build_requested` (`mainwindow.py`) starts `slackrepo build --preview ` instead of `update`. - The Packages tab drops its per-row **Update** action: **Build** now does the same thing. - **Update all** stays `slackrepo update` with no items. It still removes packages whose SlackBuild is gone, but that is no longer the only way: see section 3. ## 2. Per-package results of a job slackrepo exits 0 after a full run whatever its items did. It exits non-zero only on setup errors. Today a run where some packages failed shows as `ok`. slackrepo records each item's outcome in its database: `buildresults(itemid, time, result)`, `time` in epoch seconds, one row per item (insert or replace), written by `log_itemfinish` for build, rebuild and update. Results: `ok`, `skipped`, `unsupported`, `failed`, `aborted` (skipped because a dep failed), `bad`, `removed`. ### Helper (`sbb-remote`) In `cmd_run`, after a real slackrepo job ends (`signs(kind, args)`, i.e. not `--preview`), read the rows with `time >= meta["start"]` from `slackrepo_db` (read-only, as `cmd_results`) into `meta["results"] = {itemid: result}`. If any result is `failed`, `aborted` or `bad` and the exit was 0, the job's status is `failed`. A database error leaves `results` unset and the status as the exit code says: the build already happened, a missing summary must not lose it. No new job status: `failed` is already handled everywhere (Jobs tab, notifications, badge). `job-list` carries `results` with the rest of the meta. The flock serializes jobs, so rows newer than the job's start are this job's. `rebuild-tree` does not touch `buildresults` and is unaffected. `publish` is a separate job and keeps its own exit status. ### GUI - Jobs tab: a slackrepo job with `results` shows a one-line count in its details, e.g. `3 ok, 1 failed, 1 aborted`. - Notification body (`fmt.outcome`): the same count, plus the names of the failed items, so a failed build says what failed without opening the app. - Next step (UI guideline 5): when a real slackrepo job started this session ends with any non-`ok` result, a non-modal `BuildResultDialog` lists every item with its result chip. Per row: **Log** opens the existing `results.LogViewer` (`build-log `). Bottom: **Retry failed** starts `slackrepo build --preview` on the `failed` and `aborted` items (not `bad`: those need a fix first), **Close**. It can be reopened from the Jobs tab ribbon for the highlighted job, like a preview's next step. ## 3. Prune: remove built packages whose SlackBuild is gone When SBo drops a SlackBuild (e.g. the package moved to the official tree), the built package stays in the repository until an Update all removes it. Prune does just that removal, without building anything. ### Helper - New slackrepo action `prune` (`slackrepo prune [--preview]`). It takes no package names: the helper computes the list itself, so no name crosses the trust boundary. Items: `select distinct itemid from packages` in `slackrepo_db` (read-only), keeping those with no `//.SlackBuild`. argv: `slackrepo --repo=... remove [--preview] ...`. slackrepo's `remove` looks items up in the same `packages` table (`find_items -p`) and honours `--preview`. - An empty list is an error at `job-start` ("nothing to prune"): no job. - `stats` gains `prunable`, the length of that list, for the GUI to enable the action. - `signs()`: a real prune ends in `gen_repos_files.sh` like any slackrepo run, so it signs: `kind == "slackrepo"` already covers it. ### GUI - Packages tab ribbon: **Prune** after **Update all**, disabled with reason "no built package lost its SlackBuild" when `prunable` is 0. It starts `slackrepo prune --preview`. The preview lists what would be removed; the NextStep runs `slackrepo prune` for real (the helper recomputes the list, same result unless the tree changed in between). - The Dashboard's Built tile shows `N to prune` in its detail when `prunable` is non-zero. ## Tests - `test_remote.py`: `cmd_run` on a fake slackrepo that writes rows to a temp `buildresults` db and exits 0: `results` filled; status `failed` when one row is `failed`, `ok` when all are `ok`; rows older than the job's start ignored; a preview job gets no `results`. - `test_remote.py`: `kind_slackrepo(["prune"])` on a temp db with one item whose SlackBuild dir exists and one whose dir is gone: argv names only the gone one; `--preview` passes through; package names are rejected; an empty list raises. - `test_gui.py`: Packages tab has no Update action; the hint result's build request starts `build`; a finished job with a failed result opens `BuildResultDialog`, and Retry failed starts `build --preview` on the failed and aborted items only.