1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# CLAUDE.md
Working notes for Claude Code on this project. Read before editing.
## What this is
`sbo-batch-test`: a single self-contained bash script that batch-tests
SlackBuilds against a clean Slackware 15.0 overlay chroot. The user is an SBo
(SlackBuilds.org) maintainer on Slackware64-current who needs to verify builds
against 15.0 stable without booting a VM.
Full spec lives in `claude-code-prompt-sbo-batch-tester.txt`. Reference mount
machinery in `overlay-chroot.sh` (Jeremy Hansen / bassmadrigal). User-facing
docs in `README.md`.
## Conventions (the user cares about these)
- **No em dash character** in any prose, comments, README, or commit messages.
Use commas or periods. This is a hard rule.
- This session runs with ponytail (laziest working solution) and caveman
(terse replies) modes. Code stays normal; chat is terse.
- Build incrementally. The user wants to review the dep resolver and the
mount/teardown core before trusting the rest. Do not balloon scope.
- Do NOT default to agreement. If a choice is fragile or wrong, say so and
propose better with reasoning.
## Hard constraints (do not violate)
- **Resolution is LOCAL-tree-only.** Never add network/sbopkg resolution. The
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 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).
- Per-target disposable overlay: each category target tests against pristine
15.0. Built packages are throwaway.
## Architecture (single file: sbo-batch-test)
Top-to-bottom layout:
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 PKG_CACHE='' 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, 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`,
`read_requires`.
6. **Dependency resolution** - `resolve_target` -> `_resolve_visit` (DFS topo
sort + cycle detection). Outputs `RESOLVED_ORDER[]`, records `UNMET[]`,
`CYCLES[]`, `HAS_README[]`. `installed_in_base` checks base package db.
7. **Overlay lifecycle** - `setup_overlay` (echoes tmpdir, mounts overlay +
binds), `teardown_overlay` (idempotent, correct reverse order), `cleanup_trap`
(EXIT/INT/TERM, unwinds all live overlays).
8. **build_one** - copies SlackBuild into overlay, runs download/md5/build/
installpkg INSIDE the chroot non-interactively via heredoc, reads back a
status token file, sets status maps. Logs resolved .info env up front and
the installed file list (from /var/log/packages) after installpkg, so the
overlay is fully disposable.
9. **run_target** - fresh overlay per target, builds resolved chain in order,
marks `BLOCKED-BY-DEP` on dependents of failures, tears down.
10. **print_summary** - color screen recap + plain `summary.log`.
11. **main** - parse, validate, make `RUN_DIR`, update base, collect targets
(single-package or category-folder), run each.
## Teardown order (do not reorder)
pts -> dev/proc/sys -> resolv.conf -> dbus machine-id -> overlay last.
Matches the reference script. Idempotent (mountpoint-guarded), trap-registered
so a mid-target abort still unwinds. `ACTIVE_MOUNTS[]` tracks live overlays.
## Status values
`SUCCESS CACHED DOWNLOAD-FAILED MD5-MISMATCH BUILD-FAILED INSTALL-FAILED
BLOCKED-BY-DEP UNMET-DEP`. `CACHED` = a dependency installed from the persistent
package cache instead of being rebuilt (target is never CACHED). `%README%`
recorded separately as a reminder flag, not a status.
## What is verified vs not
- **Verified by self-check** (`test-logic.sh` in repo, `bash test-logic.sh`, no
VM needed): topo order, `%README%` recording, unmet-dep, cycle detection, and
BLOCKED-BY-DEP propagation (`depends_on_failed`, including the transitive
one-hop cascade), and the package-cache logic
(`cache_decision`/`cache_path`/`cache_store`/`version_of`). The check builds a
fake SBo tree and sources the script with
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).
- **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)
- `depends_on_failed`: direct-REQUIRES check only. Transitive blocking still
works because the loop runs in topo order, so a failure propagates one hop per
package.
## Open items / TODO
- **`--keep` removed (resolved).** No keep-overlay option. Overlay is always
torn down. Rationale: per-package logs now capture full build/install output,
resolved `.info` env, and the installed file list, so the overlay holds
nothing worth retaining. Do not re-add `--keep` without a reason logs cannot
cover.
- **Package cache (DONE).** Persistent `$PKG_CACHE` (config var, empty =
disabled), SBo-tree layout `<cat>/<prog>/<prog>-<ver>-...txz`, one .txz per
prog. Key = prog+version (build/arch/tag ignored). The named target always
builds fresh and refreshes the cache; deps install from cache on a version
match (status `CACHED`), else build and cache. Cache wiped when `update_base`
patches. Pure `cache_decision`/`cache_path`/`cache_store`/`version_of` covered
by `test-logic.sh`. Spec: docs/superpowers/specs/2026-06-24-package-cache-design.md.
- **"all" mode**: build every package across all SBo roots. Extension point in
`main` where `targets` is populated. TODO marker in source.
- **queue/list-file mode**: build a named list. Same extension point.
- **`-j` parallelism**: flag parsed, currently no-op. TODO marker in source.
## How to run the self-check
```sh
bash test-logic.sh
```
Covers resolution + BLOCKED-BY-DEP. Extend it (not /tmp scratch files) when
adding logic. Anything VM-dependent (overlay, chroot, installpkg) is out of its
reach by design.
|