From 198499d28f69abd20584a2eb88d9e1dd29e9dbec Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 24 Jun 2026 15:21:51 +0200 Subject: Externalize config; mirror mountpoint+auto-mount, MIRROR_TREE, --terse Config now lives outside the script in $SBO_BATCH_CONFIG (default ~/.config/sbo-batch-tester/config), sourced at load. Script ships empty defaults plus config.example as the template; require_config (in validate_env and init_base) hard-fails a real run when the config is missing or has not set SLACKWARE_BASE / LOCAL_MIRROR_15 / SBO_TREE_ROOTS. LOCAL_MIRROR_15 is now the NFS mountpoint, not the tree root. The Slackware tree is derived as MIRROR_TREE=$LOCAL_MIRROR_15/slackware64-$VERSION (holds ChangeLog.txt, slackware64/, patches/); all mirror content reads use it. The mountpoint is checked with mountpoint -q and auto-mounted (mount $LOCAL_MIRROR_15) when the noauto fstab entry is not mounted, left mounted after the run. installpkg/upgradepkg run with --terse for cleaner output. Docs updated. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 49 +++++++++++++++++------- README.md | 19 +++++++--- config.example | 35 +++++++++++++++++ sbo-batch-test | 117 ++++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 163 insertions(+), 57 deletions(-) create mode 100644 config.example diff --git a/CLAUDE.md b/CLAUDE.md index d6ee7e9..1254ab1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,8 +30,11 @@ docs in `README.md`. local SBo tree is the single source of truth (contains unpublished personal/pentesting packages). Settled, not open for revisiting. - **overlay lowerdir must be LOCAL.** `SLACKWARE_BASE` is a local fs path. - `LOCAL_MIRROR_15` is NFS and is a package SOURCE only. overlayfs over NFS - lowerdir is fragile. The script guards against pointing base under the mirror. + `LOCAL_MIRROR_15` is the NFS mountpoint, a package SOURCE only (content read + via the derived `MIRROR_TREE`). overlayfs over NFS lowerdir is fragile. The + script guards against pointing base under the mirror. The mirror fstab entry + is `noauto`; the script checks `mountpoint -q` and auto-mounts (`mount + $LOCAL_MIRROR_15`) if absent, left mounted after the run. - **Do not wrap/drive slackrepo.** Separate pipeline, keep independent. - **Shares host kernel.** No kernel-module testing claims. - Runs as root only (overlay + chroot). @@ -42,18 +45,30 @@ docs in `README.md`. Top-to-bottom layout: -1. **CONFIG block** - `SLACKWARE_BASE`, `LOCAL_MIRROR_15`, `SBO_TREE_ROOTS[]`, - `CHROOT_LOCATION`, `LOG_ROOT`, `VERSION`. Currently placeholder paths (we are - not in the VM, no real values yet). User fills these in. +1. **CONFIG block** - empty defaults plus a `source` of the external config + file. Real values live OUTSIDE the script in `$SBO_BATCH_CONFIG` (default + `~/.config/sbo-batch-tester/config`, i.e. `/root/.config/...` since it runs + as root); `config.example` in the repo is the template. The block sets empty + defaults (`SLACKWARE_BASE LOCAL_MIRROR_15 SBO_TREE_ROOTS`) plus + `CHROOT_LOCATION=/tmp LOG_ROOT VERSION=15.0`, sources the config if present + (NOT an error if missing, so the script stays sourceable by `test-logic.sh`), + then derives `MIRROR_TREE`. `require_config` (called by `validate_env` and + `init_base`) is what hard-fails a real run when the config is absent or has + not set the required paths. `LOCAL_MIRROR_15` is the NFS mountpoint; the tree + is one level down, `MIRROR_TREE="$LOCAL_MIRROR_15/slackware64-$VERSION"` + (holds ChangeLog.txt, slackware64/, patches/). All mirror content reads use + `MIRROR_TREE`; the mountpoint check, auto-mount, and the under-mirror guard + use `LOCAL_MIRROR_15`. 2. **Globals / flags** - `USE_COLOR DRY_RUN WITH_X JOBS`, status maps (`ST_STATUS ST_REASON ST_TIME ST_README`), `ACTIVE_MOUNTS[]`. 3. **usage / init_color / parse_args / validate_env** - fail-fast startup checks with copy-pasteable hints. 4. **init_base** - `--init-base` mode only. First-time populate: `installpkg --root` the full `slackware64/*/*.t?z` set into `SLACKWARE_BASE`, seed the - `last-base-update` marker, exit. Runs its own checks (root, NFS reachable, - not under mirror) since the base does not exist yet; refuses to clobber a - populated base. Wired in `main` before `validate_env`, exits 0. + `last-base-update` marker, exit. Runs its own checks (root, mirror mounted + /auto-mount, ChangeLog.txt present, not under mirror) since the base does not + exist yet; refuses to clobber a populated base. Wired in `main` before + `validate_env`, exits 0. installpkg/upgradepkg run with `--terse`. **update_base** - patches local base from mirror's `patches/packages/` when ChangeLog head differs (reused from reference script). 5. **SBo tree lookup** - `find_slackbuild_dir`, `category_of`, `pkg_key`, @@ -93,13 +108,21 @@ not a status. VM needed): topo order, `%README%` recording, unmet-dep, cycle detection, and BLOCKED-BY-DEP propagation (`depends_on_failed`, including the transitive one-hop cascade). The check builds a fake SBo tree and sources the script with - config overridden AFTER sourcing (sourcing re-runs the CONFIG block, so test - vars must be set after the `source`). Gotchas baked in: do not name the dead + config overridden AFTER sourcing (sourcing re-runs the CONFIG block, which + resets the vars to empty defaults and may source the external config, so test + vars must be set after the `source`). The test runs as a normal user, so + `~/.config/sbo-batch-tester/config` usually does not exist and is not sourced; + even if it did, the post-source overrides win. Gotchas baked in: do not name the dead list `failed` (collides with `depends_on_failed`'s `local -n failed`), and `ok`/`bad` must `return 0` (`((x++))` returns nonzero when x was 0). -- **NOT runnable-tested** (needs the VM): overlay mounts, chroot build flow, - base patching, `--init-base` populate, installpkg. Logic mirrors the - reference script / the prior manual populate hint. +- **Verified on the build system**: `--init-base` populate ran clean against + the real mirror (mountpoint check, auto-mount of the noauto NFS entry, + `MIRROR_TREE` derivation, external config sourcing, full installpkg --terse + set). The external-config split (`require_config`, missing/incomplete errors) + was checked in isolation too. +- **NOT runnable-tested** (needs a real build run): overlay mounts, chroot + build flow, base patching (update_base), per-package installpkg. Logic mirrors + the reference script. ## Known shortcuts (ponytail: comments in source) diff --git a/README.md b/README.md index b29542c..6a15ff0 100644 --- a/README.md +++ b/README.md @@ -26,15 +26,22 @@ current-vs-15.0 drift bites. ## Prerequisites -You need three things in place, all configured in the CONFIG block at the top -of `sbo-batch-test`: +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-mounted Slackware 15.0 mirror. Used only as a - package SOURCE to populate and patch `SLACKWARE_BASE`. Read-only is fine. +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 @@ -49,8 +56,8 @@ 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 the script's CONFIG block, then -run once as root: +Set `SLACKWARE_BASE` and `LOCAL_MIRROR_15` in your config file, then run once +as root: ```sh sbo-batch-test --init-base diff --git a/config.example b/config.example new file mode 100644 index 0000000..88cc264 --- /dev/null +++ b/config.example @@ -0,0 +1,35 @@ +# sbo-batch-test config. Sourced by the script as bash. +# +# Copy to ~/.config/sbo-batch-tester/config and edit. The script runs as root, +# so that is /root/.config/sbo-batch-tester/config. Override the path with the +# $SBO_BATCH_CONFIG environment variable. + +# LOCAL (non-NFS) read-only 15.0 base install tree. This is the overlay +# lowerdir. MUST be a local filesystem (ext4/xfs). NEVER point this at the NFS +# mirror: overlayfs over NFS lowerdir is fragile and fails intermittently. +# Populate it once with: sbo-batch-test --init-base +SLACKWARE_BASE="/sbo-base/15.0" + +# NFS mountpoint for the 15.0 mirror. Point at the mount root itself; the script +# checks it with mountpoint -q and auto-mounts it (mount $LOCAL_MIRROR_15) if the +# fstab entry is noauto. The actual Slackware tree (ChangeLog.txt, slackware64/, +# patches/) is found one level down at $LOCAL_MIRROR_15/slackware64-$VERSION. +# Package SOURCE only. Read-only is fine. Never the overlay lowerdir. +LOCAL_MIRROR_15="/mnt/mirror-15.0" + +# 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. +SBO_TREE_ROOTS=( + "/var/lib/sbopkg/SBo-danix" +) + +# Where overlays are created. LOCAL, needs space. One disposable overlay per +# target lives here. +CHROOT_LOCATION="/tmp" + +# Where persistent logs are written (outside the overlay, survives teardown). +LOG_ROOT="/var/log/sbo-batch-test" + +# Slackware version, used for the mirror ChangeLog / patches path. +VERSION="15.0" diff --git a/sbo-batch-test b/sbo-batch-test index 4683eb3..0c39ed2 100755 --- a/sbo-batch-test +++ b/sbo-batch-test @@ -19,35 +19,39 @@ # No em dashes in prose by author convention. # ============================================================================= -# CONFIG (edit these for your VM) +# CONFIG # ============================================================================= - -# LOCAL (non-NFS) read-only 15.0 base install tree. This is the overlay -# lowerdir. MUST be a local filesystem (ext4/xfs). NEVER point this at the NFS -# mirror: overlayfs over NFS lowerdir is fragile and fails intermittently. -SLACKWARE_BASE="/sbo-base/15.0" - -# NFS-mounted 15.0 mirror. Package SOURCE only, used to populate/patch -# SLACKWARE_BASE. Read-only is fine. Never used as the overlay lowerdir. -LOCAL_MIRROR_15="/mnt/nfs/slackware64-15.0" - -# 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. -SBO_TREE_ROOTS=( - "/home/danix/SBo-danix" - "/home/danix/slackbuilds-15.0" -) - -# Where overlays are created. LOCAL. One disposable overlay per target lives here. +# +# Do NOT edit values here. Real config lives in an external file, sourced below. +# Default path: ~/.config/sbo-batch-tester/config (override with $SBO_BATCH_CONFIG). +# Copy config.example from the repo and edit it. Runs as root, so ~ is root's +# home (/root/.config/sbo-batch-tester/config). +# +# Config file sets these (see config.example for docs on each): +# SLACKWARE_BASE LOCAL_MIRROR_15 SBO_TREE_ROOTS CHROOT_LOCATION LOG_ROOT VERSION + +# Defaults. The config file overrides any it sets. These are placeholders, a run +# fails fast in validate_env / init_base if the config has not set real paths. +SLACKWARE_BASE="" +LOCAL_MIRROR_15="" +SBO_TREE_ROOTS=() CHROOT_LOCATION="/tmp" - -# Where persistent logs are written (outside the overlay, survives teardown). LOG_ROOT="/var/log/sbo-batch-test" - -# Slackware version, used for the mirror ChangeLog / patches path. VERSION="15.0" +# Source the external config if present. Not an error here if missing (keeps the +# script sourceable by test-logic.sh); validate_env / init_base report it. +SBO_BATCH_CONFIG="${SBO_BATCH_CONFIG:-$HOME/.config/sbo-batch-tester/config}" +if [[ -f "$SBO_BATCH_CONFIG" ]]; then + # shellcheck disable=SC1090 + source "$SBO_BATCH_CONFIG" +fi + +# Derived: the actual Slackware tree under the mirror mountpoint. Holds +# ChangeLog.txt, slackware64/, and patches/. Set after sourcing so it picks up +# the config's LOCAL_MIRROR_15 and VERSION. +MIRROR_TREE="$LOCAL_MIRROR_15/slackware64-$VERSION" + # ============================================================================= # END CONFIG # ============================================================================= @@ -151,11 +155,33 @@ parse_args() { # ============================================================================= # startup validation (fail fast, copy-pasteable hints) # ============================================================================= + +# Shared: the config file must exist and set the required paths. Called by both +# validate_env and init_base. +require_config() { + if [[ ! -f "$SBO_BATCH_CONFIG" ]]; then + cat >&2 <&2 + exit 1 + fi +} + validate_env() { if [[ $EUID -ne 0 ]]; then echo "This tool must run as root (overlay + chroot)." >&2 exit 1 fi + require_config # SLACKWARE_BASE: local, looks like a real Slackware install. if [[ ! -d "$SLACKWARE_BASE" || ! -d "$SLACKWARE_BASE/var/log/packages" ]]; then @@ -176,10 +202,18 @@ EOF exit 1 ;; esac - # LOCAL_MIRROR_15: NFS, must be mounted/reachable. - if [[ ! -d "$LOCAL_MIRROR_15" || ! -f "$LOCAL_MIRROR_15/ChangeLog.txt" ]]; then - echo "LOCAL_MIRROR_15 not reachable (NFS mount absent?): $LOCAL_MIRROR_15" >&2 - echo "Expected a Slackware 15.0 mirror with ChangeLog.txt at its root." >&2 + # LOCAL_MIRROR_15: NFS, fstab entry is noauto. Auto-mount if not mounted + # (needs the fstab entry for bare `mount `). Left mounted after the run. + if ! mountpoint -q "$LOCAL_MIRROR_15"; then + echo "LOCAL_MIRROR_15 not mounted, attempting: mount $LOCAL_MIRROR_15" >&2 + if ! mount "$LOCAL_MIRROR_15"; then + echo "Could not mount $LOCAL_MIRROR_15 (no fstab entry? mount it manually)." >&2 + exit 1 + fi + fi + if [[ ! -f "$MIRROR_TREE/ChangeLog.txt" ]]; then + echo "Mirror mounted but no ChangeLog.txt under: $MIRROR_TREE" >&2 + echo "Expected the Slackware 15.0 tree at \$LOCAL_MIRROR_15/slackware64-\$VERSION." >&2 exit 1 fi @@ -209,12 +243,19 @@ init_base() { if [[ $EUID -ne 0 ]]; then echo "--init-base must run as root (installpkg --root)." >&2; exit 1 fi + require_config case "$SLACKWARE_BASE" in "$LOCAL_MIRROR_15"*) echo "SLACKWARE_BASE must NOT live under LOCAL_MIRROR_15 (NFS)." >&2; exit 1 ;; esac - if [[ ! -d "$LOCAL_MIRROR_15" || ! -f "$LOCAL_MIRROR_15/ChangeLog.txt" ]]; then - echo "LOCAL_MIRROR_15 not reachable (NFS mount absent?): $LOCAL_MIRROR_15" >&2; exit 1 + if ! mountpoint -q "$LOCAL_MIRROR_15"; then + echo "LOCAL_MIRROR_15 not mounted, attempting: mount $LOCAL_MIRROR_15" >&2 + if ! mount "$LOCAL_MIRROR_15"; then + echo "Could not mount $LOCAL_MIRROR_15 (no fstab entry? mount it manually)." >&2; exit 1 + fi + fi + if [[ ! -f "$MIRROR_TREE/ChangeLog.txt" ]]; then + echo "Mirror mounted but no ChangeLog.txt under: $MIRROR_TREE" >&2; exit 1 fi if [[ -d "$SLACKWARE_BASE/var/log/packages" ]]; then echo "Base already populated: $SLACKWARE_BASE" >&2 @@ -224,34 +265,34 @@ init_base() { echo "Populating base from mirror into: $SLACKWARE_BASE" mkdir -p "$SLACKWARE_BASE" local p n=0 - for p in "$LOCAL_MIRROR_15"/slackware64/*/*.t?z; do + for p in "$MIRROR_TREE"/slackware64/*/*.t?z; do [[ -e "$p" ]] || continue - installpkg --root "$SLACKWARE_BASE" "$p" + installpkg --terse --root "$SLACKWARE_BASE" "$p" ((n++)); ((n % 50 == 0)) && echo " ...$n packages" done if [[ $n -eq 0 ]]; then - echo "No packages found under $LOCAL_MIRROR_15/slackware64/*/. Wrong mirror layout?" >&2 + echo "No packages found under $MIRROR_TREE/slackware64/*/. Wrong mirror layout?" >&2 exit 1 fi # Seed the update marker so the next run does not re-patch needlessly. - head -n1 "$LOCAL_MIRROR_15/ChangeLog.txt" > "$SLACKWARE_BASE/last-base-update" + head -n1 "$MIRROR_TREE/ChangeLog.txt" > "$SLACKWARE_BASE/last-base-update" echo "Base populated: $n packages installed into $SLACKWARE_BASE" } update_base() { local marker="$SLACKWARE_BASE/last-base-update" touch "$marker" - local head_now; head_now="$(head -n1 "$LOCAL_MIRROR_15/ChangeLog.txt")" + local head_now; head_now="$(head -n1 "$MIRROR_TREE/ChangeLog.txt")" if [[ "$head_now" == "$(cat "$marker")" ]]; then echo "Base is up-to-date with the mirror." return fi echo "Patching base from mirror..." local p - for p in "$LOCAL_MIRROR_15"/patches/packages/*.t?z; do + for p in "$MIRROR_TREE"/patches/packages/*.t?z; do [[ -e "$p" ]] || continue if [[ ! -e "$SLACKWARE_BASE/var/lib/pkgtools/packages/$(basename "${p%.*}")" ]]; then - ROOT="$SLACKWARE_BASE" upgradepkg --install-new "$p" + ROOT="$SLACKWARE_BASE" upgradepkg --terse --install-new "$p" fi done echo "$head_now" > "$marker" @@ -505,7 +546,7 @@ if [ -z "\$pkg" ]; then echo "No package produced in \$out" echo BUILD-FAILED > "$workroot/$prog.status"; exit 1 fi -if ! installpkg "\$pkg"; then +if ! installpkg --terse "\$pkg"; then echo INSTALL-FAILED > "$workroot/$prog.status"; exit 1 fi # Log the installed file list (from the package db) so the overlay is disposable. -- cgit v1.2.3