diff options
Diffstat (limited to 'docs/superpowers/specs/2026-07-11-repo-setup-update-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-07-11-repo-setup-update-design.md | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-07-11-repo-setup-update-design.md b/docs/superpowers/specs/2026-07-11-repo-setup-update-design.md new file mode 100644 index 0000000..0902d42 --- /dev/null +++ b/docs/superpowers/specs/2026-07-11-repo-setup-update-design.md @@ -0,0 +1,215 @@ +# Repo setup + update: `--setup-repo` and `--update` + +Date: 2026-07-11 +Status: approved, ready for implementation + +## Problem + +`sbo-batch-test` builds against a local SBo tree (`SBO_TREE_ROOTS[0]`). On the +build VM that tree is `/var/lib/sbopkg/SBo/15.0`, kept fresh with rsync from +slackbuilds.org (NOT a git repo). The maintainer's own packages live in two +personal subtrees inside that tree, `personal/` and `pentesting/`, cloned from +GitHub. Those subtrees must: + +- be created from scratch on first setup (tree rsync'd fresh, repos cloned in), +- be refreshed per run when wanted (git pull in place), +- shadow any same-named upstream package elsewhere in the tree (the personal + version wins, the upstream duplicate is removed). + +Today the tool does none of this; setup is a separate hand-run script written +for the -current git repo (`slackrepo_setup_SBo-danix.sh`), whose `git rm` +shadow logic does not apply to the rsync'd 15.0 tree. + +This adds two modes: `--setup-repo` (full first-time build of the tree) and +`--update` (per-run refresh), sharing one shadow routine. + +## Scope + +In scope: +- `--setup-repo`: rm -rf the tree root, rsync fresh, clone the two personal + repos in, shadow upstream duplicates. Standalone exit mode (like + `--init-base`), never combined with a build. +- `--update`: git pull each personal subtree in place, shadow. Runs inline + before a normal build. +- A shared `shadow_scan` routine (interactive confirm, `rm -r`, no git). +- New config: `SBO_RSYNC_URL`, `SBO_PERSONAL_TREES` (assoc array dir -> URL). +- Self-check for the shadow-detection logic. +- README + CLAUDE.md docs. + +Out of scope (explicitly): +- No `git subtree` on 15.0 (tree is not git; subtrees are plain clones). +- No `mkhint -F` (that is -current-only territory). +- No touching `LOCAL_MIRROR_15` (the NFS package source) or the overlay base. + `SBO_RSYNC_URL` (public slackbuilds.org) is a distinct thing from the NFS + mirror; the two never interact. +- Resolution stays LOCAL-tree-only (unchanged hard constraint). + +## Config + +Two new variables. CONFIG block sets empty defaults; `config.example` documents +real values. + +CONFIG block (empty defaults, alongside the existing empties): +```bash +SBO_RSYNC_URL="" +declare -A SBO_PERSONAL_TREES=() +``` + +`config.example` (real values): +```bash +# rsync source for the SBo tree. --setup-repo rm -rf's SBO_TREE_ROOTS[0] and +# rsyncs this into it fresh (same source sbopkg uses). Empty disables +# --setup-repo's rsync step (it will refuse to run). Package SOURCE for the +# tree only; unrelated to LOCAL_MIRROR_15 (the NFS package mirror). +SBO_RSYNC_URL="slackbuilds.org::slackbuilds/15.0" + +# Personal SBo subtrees: directory name (under SBO_TREE_ROOTS[0]) -> git clone +# URL. --setup-repo clones each after the rsync; --update git-pulls each in +# place. Both then shadow any same-named upstream package found elsewhere in the +# tree (the personal version wins, the upstream duplicate is rm -r'd). Empty = +# no personal trees, both modes become no-ops on the subtree/shadow steps. +declare -A SBO_PERSONAL_TREES=( + [personal]="https://github.com/danixland/my-slackbuilds.git" + [pentesting]="https://github.com/danixland/Slackware-Pentesting-Suite.git" +) +``` + +Note: `SBO_PERSONAL_TREES` is an associative array. The CONFIG block must +`declare -A` it before sourcing the external config so the config's `declare -A` +(or bare assignment) lands as global. Both modes resolve dir names under +`SBO_TREE_ROOTS[0]` only (single-root VM setup, settled). + +## Flags and wiring + +- `--setup-repo` sets `DO_SETUP=1`. Standalone exit mode. Wired in `main` + BEFORE `validate_env` (like `--init-base`): runs its own checks, does its + work, exits 0. Mutually exclusive with a build; if a target is also given, + `--setup-repo` still just sets up and exits (document this, do not build). +- `--update` sets `DO_UPDATE=1`. Inline, not an exit mode. Wired in `main` + after `validate_env` and near `update_base`, before targets are collected and + run. Falls through to the normal build. + +`parse_args` learns both flags; `usage` documents both. + +## `--setup-repo` (first-time full build of the tree) + +Function `setup_repo`. Runs its own preconditions since the tree may not exist: + +1. Require root. +2. `require_config`; refuse if `SBO_RSYNC_URL` is empty (print the config hint). +3. `root="${SBO_TREE_ROOTS[0]}"`. Refuse if empty. +4. Destructive confirm: print `root` and warn it will be removed and rsynced + fresh; `read -rp "Proceed? [y/N]"`. Abort on anything but y/Y. +5. `rm -rf "$root"`. +6. `rsync -a --delete --no-owner --no-group "$SBO_RSYNC_URL/" "$root/"`. + Flags match sbopkg's own rsync (keeps files root:root, avoids ownership + churn). Hard-fail on rsync nonzero. +7. For each `dir` in `SBO_PERSONAL_TREES` keys: + `git clone "${SBO_PERSONAL_TREES[$dir]}" "$root/$dir"`. Hard-fail on clone + nonzero (a half-set-up tree is worse than none). +8. `shadow_scan "$root"`. +9. Exit 0. + +No mkhint. No build. + +## `--update` (per-run refresh) + +Function `update_personal_trees`. Assumes the tree already exists: + +1. `root="${SBO_TREE_ROOTS[0]}"`. +2. For each `dir` in `SBO_PERSONAL_TREES` keys: + - `d="$root/$dir"`. If `! -d "$d/.git"`: warn "not a git clone, skipping" + and continue (fail-soft; do not abort the whole run). + - `git -C "$d" pull`. On nonzero: warn and continue (fail-soft; a pull + failure should not kill a build session). +3. `shadow_scan "$root"`. +4. Return; `main` continues into the normal build. + +Plain `git pull` (subtrees are read-only mirrors, no local commits, nothing to +rebase or fast-forward-guard; a merge can never happen because the working tree +is never modified locally). Documented so no one "improves" it to `--ff-only` +without cause. + +## `shadow_scan` (shared) + +Adapted from `slackrepo_setup_SBo-danix.sh` lines 38-80, with two changes for +the non-git 15.0 tree: `rm -r` instead of `git rm -r`, and no `git commit`. + +Signature: `shadow_scan <root>`. + +1. Build `TO_SHADOW=()`: + - Split the shadow-detection into a pure helper `collect_shadows <root>` + that ECHOES the newline-separated list of tree-relative dirs to remove, so + it is unit-testable without touching the filesystem destructively. It only + reads (find), never removes. + - For each personal tree dir present under `root` (from + `SBO_PERSONAL_TREES` keys, skip if the dir is absent): for each immediate + subdir `pkgdir` that has a matching `pkgdir/<pkg>.SlackBuild`, find + same-named dirs elsewhere under `root` at depth 2, excluding every + personal tree path. Emit each as a `root`-relative path. + - Exclusions are built from the actual `SBO_PERSONAL_TREES` keys (not the + literals `personal`/`pentesting`), so adding a third tree Just Works. +2. If `TO_SHADOW` empty: print "No upstream packages match your personal + subtrees. Nothing to shadow." and return. +3. Else: print the list under "The following upstream packages will be shadowed + (rm -r):", one indented per line. `read -rp "Proceed? [y/N]"`. On non-y: + print "Aborted shadowing; no packages were removed." and return. On y: for + each, print "Shadowing: <item>" and `rm -r "$root/$item"`. + +Interactive confirm is required (user wants to see removals before they happen) +in both `--setup-repo` and `--update`. + +## Precedence and ordering (settled) + +- Personal packages win. An upstream package with the same name as one in a + personal tree is the one removed. +- Shadow runs AFTER clone (`--setup-repo`) or pull (`--update`), so the shadow + set reflects freshly-fetched personal packages. + +## Error handling + +- `--setup-repo`: rsync and clone failures are hard (exit nonzero) — a + partially built tree is unusable. The destructive `rm -rf` is gated behind an + interactive confirm. +- `--update`: git-pull and non-git-dir problems are fail-soft (warn, continue), + so a transient GitHub hiccup does not abort a build session. Shadow's `rm -r` + is gated behind the confirm. +- Missing config (`SBO_RSYNC_URL` for setup) fails fast with the config hint. + +## Testing + +`test-logic.sh` gains one case for `collect_shadows`: +- Build a fake tree: `academic/foo`, `network/bar` (upstream), plus + `personal/foo` and `pentesting/bar` (each with a matching `.SlackBuild`), and + a decoy `personal/nomatch` with no upstream twin. +- Assert `collect_shadows <root>` returns exactly `academic/foo` and + `network/bar` (order-insensitive), and does NOT return anything under the + personal trees, and does NOT invent a match for `nomatch`. +- Assert a personal subdir without a `.SlackBuild` is ignored. + +rsync/clone/pull/rm are I/O against external tools, no stub (mirrors the +existing `update_base` / `lint_pkg` precedent: pure host I/O, no branch logic +worth a fake). `collect_shadows` is the only branchy piece, so it is the only +thing tested. + +Self-check must stay runnable as a normal user: the fake tree is built under a +temp dir, no root, no real rsync/git. + +## Docs + +- `config.example`: the two new vars, documented as above. +- `README.md`: a "Repository setup and refresh" section covering `--setup-repo` + (first-time) and `--update` (per-run), the shadow behavior, and the config + vars. Note the rsync source is public slackbuilds.org, distinct from the NFS + mirror. +- `CLAUDE.md`: add `setup_repo`, `update_personal_trees`, `shadow_scan`, + `collect_shadows` to the architecture list; add the two config vars to the + CONFIG-block description; add a verified/not-verified note (shadow logic + self-checked; rsync/clone/pull not yet run on hardware). + +## What is NOT verified until hardware + +- The actual rsync of the tree, the two clones, and a real git pull (network + + root, VM-only). Logic mirrors the reference setup script and sbopkg's rsync + flags. `collect_shadows` is self-checked; the destructive `rm -r` path and the + interactive confirm are not (trivial, no branch logic beyond the confirm). |
