# sbo-batch-test Batch-test SlackBuilds against a clean Slackware 15.0 overlay chroot. Resolves the full SBo dependency tree (locally, never network), topologically sorts it, builds and installs every package in a fresh disposable overlay over a read-only 15.0 base, captures persistent per-package logs, and prints a color-coded summary. Built for an SBo maintainer whose daily driver is Slackware64-current but whose packages target 15.0 stable. It verifies that SlackBuilds BUILD and install cleanly against the 15.0 userland/toolchain/libraries, which is where current-vs-15.0 drift bites. ## Scope and limits - Shares the host kernel (the current VM's kernel, not a 15.0 kernel). It does NOT test kernel-module packages or anything tied to the running kernel version. Those still want a real 15.0 VM. - Resolution is LOCAL-tree-only. No network, no sbopkg. The local SBo tree is the single source of truth (it contains unpublished personal/pentesting packages that do not exist upstream). - Does not integrate with, wrap, or drive slackrepo. Separate, independent tool. - Built packages are throwaway. The point is "does it build on clean 15.0", not producing redistributable output. ## Prerequisites Configuration lives in an external file, not in the script. Copy `config.example` to `~/.config/sbo-batch-tester/config` and edit it (the tool runs as root, so that is `/root/.config/sbo-batch-tester/config`). Override the path with the `SBO_BATCH_CONFIG` environment variable. You need three things set in it: 1. **`SLACKWARE_BASE`** - a LOCAL (non-NFS) full Slackware 15.0 install tree. This is the overlay lowerdir. It MUST be local (ext4/xfs). overlayfs over an NFS lowerdir is fragile and a known source of intermittent failures. 2. **`LOCAL_MIRROR_15`** - the NFS mountpoint for the Slackware 15.0 mirror. Point it at the mount root itself; the actual tree (ChangeLog.txt, slackware64/, patches/) is found one level down at `$LOCAL_MIRROR_15/slackware64-$VERSION`. Used only as a package SOURCE to populate and patch `SLACKWARE_BASE`. Read-only is fine. If the fstab entry is `noauto`, the script auto-mounts it (`mount $LOCAL_MIRROR_15`) when absent and leaves it mounted after the run. 3. **`SBO_TREE_ROOTS`** - one or more LOCAL SBo tree roots, resolved in order (first match wins). Standard SBo layout `///{prog.SlackBuild,prog.info,...}`. Read in place, never copied or synced. Also: run as root (overlay + chroot require it). Optional: install `system/sbo-maintainer-tools` to get `sbopkglint`. If present, the target's built package is linted as a final check (see below). If absent, the step is skipped with a one-line note. ## Populating SLACKWARE_BASE Populate from the NFS mirror with the FULL package set, not a minimal install. A minimal base causes false "missing dependency" results that would not happen on a normal user's full Slackware install. Set `SLACKWARE_BASE` and `LOCAL_MIRROR_15` in your config file, then run once as root: ```sh sbo-batch-test --init-base ``` This installs the full `slackware64/*/*.t?z` set into `SLACKWARE_BASE` and exits. It refuses to clobber an already-populated base (remove the directory first to re-init). The tool will not auto-bootstrap the base during a build; if the base is missing or incomplete a build fails fast and points you at `--init-base`. The base is kept patched automatically on each run: when the mirror's ChangeLog head differs from the recorded marker, new packages from `patches/packages/` are applied to the local base via `upgradepkg`. ## Repository setup and refresh Two config vars manage the SBo tree itself, on top of the read-in-place lookup described above: - **`SBO_RSYNC_URL`** - rsync source for the tree, the same source `sbopkg` uses (e.g. `slackbuilds.org::slackbuilds/15.0`). This is the public slackbuilds.org rsync tree, distinct from `LOCAL_MIRROR_15` (the NFS package mirror). Leave empty and `--setup-repo` refuses to run; `--update` skips the rsync and only pulls + shadows. - **`SBO_PERSONAL_TREES`** - a bash associative array, directory name (created under `SBO_TREE_ROOTS[0]`) to git clone URL, for your own SlackBuilds that live outside upstream (unpublished/personal/pentesting packages). ### First-time setup: `--setup-repo` Builds `SBO_TREE_ROOTS[0]` from scratch. Runs as root, asks for confirmation, then REMOVES the existing tree root, rsyncs it fresh from `SBO_RSYNC_URL`, clones each `SBO_PERSONAL_TREES` subtree into it, then shadows (see below, with its own confirmation). Exits afterward, does not build. Hard-fails on an rsync or clone error. ```sh sbo-batch-test --setup-repo ``` ### Per-run refresh: `--update` Runs inline before a build, refreshing the tree without wiping personal work. rsyncs the tree with `--delete` to pull in upstream changes and removals (personal subtrees are excluded from the rsync, so they are never touched by `--delete`), which also brings back any upstream package that a previous shadow removed. Then git-pulls each personal subtree, then re-shadows. Fail-soft: a network or git error prints a warning and the run continues. ```sh sbo-batch-test --update playwright-cli ``` ### Shadowing Your personal packages win. When a package under a `SBO_PERSONAL_TREES` subtree shares a name with a package elsewhere in the upstream tree, the upstream copy is `rm -r`'d so lookup (first match wins per `SBO_TREE_ROOTS` order, but within one root the personal copy would otherwise collide) resolves to yours. Both `--setup-repo` and `--update` run this scan, and both ask for confirmation before removing anything. This self-heals: if you later drop a package from a personal repo, the next `--update` rsync brings the upstream copy back, since nothing shadows it anymore. ## Package cache Set `PKG_CACHE` in your config to a local directory to cache built packages across runs (leave empty to disable). Layout mirrors the SBo tree (`//--...txz`), one package per prog. The named target always builds fresh, it is the package under test. Its dependencies are installed from the cache when their version is unchanged (reported as `CACHED`), otherwise they are built and cached. A build order line shows the outcome per package: `cached (1.1)`, `rebuild: 1.0 -> 1.1`, or `build (new)`; `--dry-run` shows the same without building. The whole cache is wiped automatically when the base is patched, since cached packages were built against the previous base. ## Usage ``` sbo-batch-test [OPTIONS] sbo-batch-test [OPTIONS] ``` ### Single-package mode Resolve a program's full dep tree, build+install every dep in order, then the target. The whole chain shares ONE overlay, torn down at the end. ```sh sbo-batch-test playwright-cli ``` ### Category-folder mode Every SlackBuild dir inside the folder is an independent target. Each target gets its OWN fresh overlay against pristine 15.0, so one target's installed deps never leak into the next. Deps may live in other categories. ```sh sbo-batch-test ./network sbo-batch-test /path/to/SBo-danix/pentesting ``` ### Dry run Resolve and print the build order, build nothing. ```sh sbo-batch-test --dry-run playwright-cli ``` ### Options | Option | Effect | |-------------------|--------------------------------------------------------------| | `-h`, `--help` | Usage text. | | `--no-color` | Disable ANSI color (auto-disabled when stdout is not a TTY). | | `--dry-run` | Resolve and print build order, do not build. | | `--with-x` | Enable X passthrough (`xhost +local:hosts`). Headless by default. Security caveat: allows local non-network connections to your X server. | | `-j`, `--jobs N` | Reserved. No-op stub today (builds are serial). | | `--init-base` | First-time populate `SLACKWARE_BASE` from the mirror, then exit. Run once before the first build. Refuses to clobber an existing base. | | `--setup-repo` | First-time build of the SBo tree from `SBO_RSYNC_URL` and `SBO_PERSONAL_TREES`. See below. Exits, does not build. | | `--update` | Before building, refresh the SBo tree (rsync + git pull + shadow). See below. | ## Per-package status values | Status | Meaning | |------------------|-----------------------------------------------------------| | `SUCCESS` | Built and installed. | | `CACHED` | Dependency installed from the package cache, not rebuilt. The target is never CACHED. | | `DOWNLOAD-FAILED`| Source download failed. | | `MD5-MISMATCH` | Source MD5 did not match the .info. Hard failure. | | `BUILD-FAILED` | The .SlackBuild failed or produced no package. | | `INSTALL-FAILED` | `installpkg` of the resulting package failed. | | `BLOCKED-BY-DEP` | A dependency failed, so this was not attempted. | | `UNMET-DEP` | A required package is neither in the SBo tree nor in base. Also covers dependency cycles. | Packages whose REQUIRES carry `%README%` are flagged in the summary as a reminder to check manual/optional configuration steps. `%README%` is not a package and is skipped for build ordering. ## Package lint (sbopkglint) After a target builds successfully, if `sbopkglint` (from `system/sbo-maintainer-tools`) is on the host PATH, the harness runs it against the target's built `.txz` as a final verification. It catches common SBo policy problems that a clean build does not (ownership, doc-dir contents, slack-desc, etc). The check is host-side, target only (dependencies are vetted SBo packages and would only add noise), and fail-soft: findings are reported to the screen and the package log but never change the build's `SUCCESS` status, and a missing tool just prints a skip note. The one-line result is color-coded (green `clean`, red `findings`); on findings the full sbopkglint output is also printed to the screen, indented, with a complete copy in the per-package log. ## Logs Written outside the overlay so they survive teardown, under `LOG_ROOT`: ``` $LOG_ROOT/2026-06-22_14-30-05/ _.log per-package full build/install output summary.log plain-text recap (same facts as the screen summary, no color) build-order.txt the resolved topological order actually used ``` Each per-package log captures the full build/install output, the resolved `.info` build context (PRGNAM/VERSION/BUILD/TAG/REQUIRES), and the installed file list from the package db. That is everything worth inspecting, so the overlay is always disposed and there is no keep-overlay option. ## Design tradeoffs (on the record) - **Shared deps are rebuilt per target unless cached.** Each category target starts from a clean base, so a dep shared by N targets would be rebuilt N times. Correct for isolation, slow for heavy shared deps (qt5, boost). The optional package cache (see above) mitigates this: a dep is installed from cache when its version is unchanged instead of rebuilt. The named target always builds fresh regardless. - **overlayfs sharp edges.** A build that fails ONLY in the overlay but works on bare 15.0 is more likely an overlayfs quirk (rename/whiteout) than a real build bug. Noted near the build step in the source. ## Credits Some parts of this tool are inspired by `overlay-chroot.sh` by Slackware user bassmadrigal (Jeremy Hansen): the overlayfs setup, the system bind mounts (dev, proc, sys, dev/pts, resolv.conf, dbus machine-id), the base patching from the local mirror, and the teardown ordering (pts, dev/proc/sys, resolv.conf, dbus machine-id, overlay last), which is preserved deliberately.