# sbo-dockerbuild Docker-based toolchain for test-building [SlackBuilds.org (SBo)](https://slackbuilds.org) packages on Slackware, on both `-current` and `15.0`, in throwaway containers. Extracted from the `.extras/` dir of the **sbo-slackbuilds** package repo (danix's SBo maintenance mirror) once it grew from a helper into a standalone tool. Git history starts fresh here; the original 41-commit log is preserved in [HISTORY.md](HISTORY.md) (those hashes refer to sbo-slackbuilds, not this repo). ## What this repo is (and is not) This is the **tooling**, not packages. It builds and consumes docker images to verify that a SlackBuild still builds; it does not itself contain SlackBuilds. The packages live in the maintenance repo (sbo-slackbuilds) and other repos. Two cooperating tools, one shared set of images: - **`test-build`** — verify a single SlackBuild builds cleanly in a container. - **`image-builder/`** — build the images `test-build` consumes. ## Layout ``` test-build the test-build tool (single bash script) install.sh copy test-build into ~/bin (BINDIR= to override) test-logic.sh pure-logic self-check for test-build (no docker) test-build-config.example copy to ~/.config/sbo-testbuild/config and edit overrides.example current-vs-stable dep overrides (optional) image-builder/ bootstrap.sh, build-full-image.sh, build-sbo-testbuild.sh, lib.sh, config, test-image-builder.sh, README docs/specs, docs/plans design specs and implementation plans ``` Everything is at the repo root (flat), not under `.extras/` — that nesting was a sbo-slackbuilds convention and does not apply here. ## test-build Resolves and builds a SlackBuild in a disposable Slackware container, then lints it. The **target** is the local SlackBuild under edit; its **dependencies** come from a configured SBo tree. These are two different sources — do not conflate them (an earlier bug resolved the target from the tree, testing the published version instead of the edit). ### Target resolution (`resolve_target_dir`) Repo-agnostic, driven by where you are: - a **path** (absolute, `./x`, or containing `/`): that directory - a **bare name**: `.//` under CWD, else CWD itself if it *is* `/` So `cd pkgdir && test-build `, `test-build /path/to/pkg`, and `test-build ./pkg` all work. ### Dependency resolution Deps are resolved from the SBo tree in the config (`SBO_TREE_CURRENT` / `SBO_TREE_STABLE`), topologically sorted, cycle-checked. A dep is satisfied by: 1. building it from the tree (recursively), or 2. `installed_in_base` — already present in the full-install image (queried once per run from the image's `/var/log/packages`), or 3. an override (see below). Deps build first and are cached; the target always rebuilds (that is the test). ### current-vs-stable overrides `~/.config/sbo-testbuild/overrides` (see `overrides.example`), applied **only on -current** (15.0 is the SBo baseline). Verbs: - `drop: ` — a 15.0-only SBo dep whose payload the -current base already ships (e.g. `rust-opt`, `google-go-lang`, `cmake-opt`, `llvm-opt`, `python3-installer`, `python3-build`). Satisfied at resolution: not built, not UNMET. - `rename: -> ` — dep renamed on -current. - `fetch: ` — dep removed from the -current tree; built via `sbopkg` in the container. ### The build container One `docker run --rm -i` per package (the `-i` is required — without it `bash -s` gets no stdin and the heredoc is silently discarded). Inside, as root: installs already-built deps, sources `/etc/profile.d/*.sh` (so dep-provided env like google-go-lang's `GOROOT`/`PATH` is live in the non-login shell), builds the SlackBuild, `installpkg`s it, and runs `sbopkglint` **in-container** (it shells out to sudo, which only works as root here; never lint on the host). The host reads a `LINT-CLEAN`/`LINT-FINDINGS` marker from the log. ### Dep cache Host dir (`PKG_CACHE`), namespaced `-/`. An image rebuild changes the digest and self-invalidates the cache. Each run prunes superseded digests **of the same variant only**, so a `--current` run does not wipe the `15.0` cache (both live caches survive; testing both trees back-to-back reuses built deps). Only **deps** are cached, never the target. ### Flags `--stable` (15.0), `--dry-run`, `--yes`, `--no-cache`, `--keep` (copy the built target `.txz` to `/kept/` so it can be installed on the host, e.g. to regenerate post-install artifacts), `--no-color`. ## image-builder Three chained scripts build the images, each self-gated so an unchanged night is a cheap no-op: - `bootstrap.sh` → `sbo-base:{ver}` FROM scratch (gated on the mirror's `ChangeLog.txt` hash) - `build-full-image.sh` → `sbo-full:{ver}` all series (gated on base digest; `--force` prunes the build cache first) - `build-sbo-testbuild.sh` → `sbo-testbuild:{ver}` + sbopkg + maintainer-tools (gated on full digest + tools `.txz` hash) All settings in `image-builder/config`. Full setup (NFS mirror mounts, LAN registry, prebuilt pkgs, cron) is in `image-builder/README`. **Main-loop gotcha:** each script runs its `build_variant` in an `if ! (...)` condition, which suppresses `set -e` inside the subshell. So `docker build` / `docker push` failures are caught with **explicit** `if ! ...; return 1` checks, not by relying on `set -e`. Keep that pattern when editing. ## Testing Pure-logic self-checks, no docker, no network: ```bash bash test-logic.sh # test-build resolver/overrides/cache bash image-builder/test-image-builder.sh # image-builder lib.sh logic ``` Non-trivial logic changes get a check here. The docker build path itself is verified by running `test-build` against a real image. ## Conventions - Plain bash, single-file scripts where practical, minimal dependencies. - `set -euo pipefail` in executables; guard `set -e` around subshell conditions (see the image-builder gotcha). - GPLv2-only. Every source file carries a short GPL header notice. - No em dashes in prose. ## Deployment The image-builder scripts run on the docker host VM (`docker.noland.dnx`, reached via the `docker` SSH host alias, root login), deployed under `/opt/sbo-testbuild/image-builder/`. A nightly cron rebuilds both variants after the repo syncs. Treat the VM as outward-facing: confirm before deploying, and inspect a target before overwriting it. After editing a script here, sync it to the VM so the cron runs the current version.