aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-25 10:05:08 +0200
committerDanilo M. <danix@danix.xyz>2026-09-25 10:05:08 +0200
commitded593dc529916f1bf23f2df38f975db1c1e69e8 (patch)
tree7f3c60069cddd61789c068d30578cb69755dfeec
parent2fd4fa399e980e3ea351df435d0f0eb2841679d8 (diff)
downloadsbo-dockerbuild-ded593dc529916f1bf23f2df38f975db1c1e69e8.tar.gz
sbo-dockerbuild-ded593dc529916f1bf23f2df38f975db1c1e69e8.zip
test-build: add --pull to refresh a stale local image
require_image pulled only when the image was missing, so once the image-builder rebuilt a registry tag the local copy went stale with no warning. --pull runs `docker pull` first, which compares digests itself and costs one manifest request when nothing changed. If the registry is unreachable it falls back to the local copy with a warning, and still fails when there is none. The README documents a daily cron pull for machines that run test-build, and the skill notes that local images are not refreshed automatically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--CLAUDE.md4
-rw-r--r--README.md12
-rw-r--r--skills/test-build-slackbuild/SKILL.md3
-rwxr-xr-xtest-build27
-rw-r--r--test-logic.sh30
5 files changed, 68 insertions, 8 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 14eed5d..cab6c4d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -114,7 +114,9 @@ built deps). Only **deps** are cached, never the target.
target `.txz` to `<data>/kept/` so it can be installed on the host, e.g. to
regenerate post-install artifacts), `--local-deps` (also resolve deps from the
target's own repo, its grandparent dir, prepended before the SBo tree, for
-packages whose deps are siblings in the same repo and not on SBo), `--no-color`.
+packages whose deps are siblings in the same repo and not on SBo), `--pull`
+(refresh the image from the registry first; a local image is otherwise pulled
+only when missing, so it goes stale), `--no-color`.
## image-builder
diff --git a/README.md b/README.md
index 574832d..ca45672 100644
--- a/README.md
+++ b/README.md
@@ -50,12 +50,24 @@ cd some-slackbuild-dir && test-build <name> # -current
test-build --stable <name> # 15.0
test-build --keep <name> # also keep the built .txz
test-build --local-deps <name> # deps live in the same repo
+test-build --pull <name> # refresh the image first
```
`--local-deps` also searches the target's own repo (its grandparent dir) for
deps, for packages whose deps are siblings in the same repo and not on SBo. The
configured SBo tree is still searched after.
+A local image is pulled only when missing, so it goes stale when the registry
+tag is rebuilt. `test-build --pull <name>` refreshes it for one run. To keep
+every machine that runs `test-build` current, add a daily cron entry after
+the image chain finishes (~08:00). `docker pull` compares digests itself and
+is a no-op when the image is up to date; the prune drops the image a pull
+leaves untagged:
+
+```
+30 9 * * * docker pull -q docker.noland.dnx:5000/sbo-testbuild:current && docker pull -q docker.noland.dnx:5000/sbo-testbuild:15.0 && docker image prune -f
+```
+
Build the images (on the docker host; see `image-builder/README`):
```bash
diff --git a/skills/test-build-slackbuild/SKILL.md b/skills/test-build-slackbuild/SKILL.md
index e8b553c..dc23896 100644
--- a/skills/test-build-slackbuild/SKILL.md
+++ b/skills/test-build-slackbuild/SKILL.md
@@ -73,6 +73,7 @@ The local deps must themselves be valid, buildable SlackBuilds. Without the flag
| `--no-cache` | Rebuild all deps this run. |
| `--keep` | Copy the built target `.txz` to `kept/` so it can be installed on the host. |
| `--local-deps` | Also resolve deps from the target's own repo (grandparent dir), for deps that are siblings in the same repo and not on SBo. |
+| `--pull` | Refresh the image from the registry before building (no-op if already current; falls back to the local copy if the registry is down). Use when the image-builder rebuilt the tag since the last pull. |
| `--no-color` | Disable ANSI color (auto-off when not a TTY). |
| `-V`, `--version` | Print version. |
@@ -91,7 +92,7 @@ Lint runs in-container on the target only. Findings are **fail-soft**: `LINT-FIN
## Setup that must exist
- `~/.config/sbo-testbuild/config` (from `test-build-config.example`): trees, image tags, `LOG_ROOT`, `PKG_CACHE`. Missing config = hard exit with a copy-the-example message.
-- Docker reachable, and the `sbo-testbuild:{current,15.0}` images built by the image-builder (pulled from the LAN registry if not local). A pure `--dry-run` skips the image check.
+- Docker reachable, and the `sbo-testbuild:{current,15.0}` images built by the image-builder (pulled from the LAN registry if not local). A local copy is **not** refreshed automatically: it goes stale when the registry tag is rebuilt, unless a daily cron pull is set up (see README) or you pass `--pull`. A pure `--dry-run` skips the image check.
- `~/.config/sbo-testbuild/overrides` only if you need override rules.
## Do not
diff --git a/test-build b/test-build
index 6899c05..56cc291 100755
--- a/test-build
+++ b/test-build
@@ -54,6 +54,7 @@ ASSUME_YES=0 # --yes: skip the confirm prompt (still prints the order)
USE_CACHE=1 # --no-cache disables the dep cache for one run
KEEP_TARGET=0 # --keep: copy the built target package out to KEEP_DIR
LOCAL_DEPS=0 # --local-deps: also resolve deps from the target's own repo
+PULL_IMAGE=0 # --pull: refresh the image from the registry before building
VERSION_ID="current" # "current" | "15.0"; set by --stable
TARGET_ARG=""
@@ -89,6 +90,10 @@ OPTIONS:
--local-deps Also resolve deps from the target's own repo (its grandparent
dir), for packages whose deps are siblings in the same repo and
not on SBo. The configured SBo tree is still searched after.
+ --pull Pull the image from the registry before building, so a local
+ copy older than the registry's tag is refreshed. A no-op when
+ it is already current. Falls back to the local copy if the
+ registry is unreachable.
--no-color Disable ANSI color (auto-disabled when stdout is not a TTY).
EOF
}
@@ -104,6 +109,7 @@ parse_args() {
--no-cache) USE_CACHE=0; shift ;;
--keep) KEEP_TARGET=1; shift ;;
--local-deps) LOCAL_DEPS=1; shift ;;
+ --pull) PULL_IMAGE=1; shift ;;
--no-color) USE_COLOR=0; shift ;;
-*) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
*)
@@ -850,13 +856,22 @@ print_summary() {
# Ensure the selected image is available locally. It is built by a separate job
# (the image-builder) and pushed to the LAN registry; this script only consumes
# it. If it is not already local, pull it once (ACTIVE_IMAGE is a fully-qualified
-# registry ref). A stale local tag is not refreshed here.
-# ponytail: pull-if-missing only. If the registry's :current is rebuilt, the
-# local copy goes stale silently. Add a --pull force-flag if that bites.
+# registry ref). A stale local tag is refreshed only with --pull (or by the
+# daily cron pull in the README); `docker pull` itself does the digest check,
+# so an up-to-date image costs one manifest request.
require_image() {
- docker image inspect "$ACTIVE_IMAGE" >/dev/null 2>&1 && return 0
- echo "Image not present locally, pulling: $ACTIVE_IMAGE" >&2
- docker pull "$ACTIVE_IMAGE" >&2 && return 0
+ if [[ $PULL_IMAGE -eq 1 ]]; then
+ echo "Pulling: $ACTIVE_IMAGE" >&2
+ docker pull "$ACTIVE_IMAGE" >&2 && return 0
+ if docker image inspect "$ACTIVE_IMAGE" >/dev/null 2>&1; then
+ echo "WARN: pull failed; using the local copy, which may be stale." >&2
+ return 0
+ fi
+ else
+ docker image inspect "$ACTIVE_IMAGE" >/dev/null 2>&1 && return 0
+ echo "Image not present locally, pulling: $ACTIVE_IMAGE" >&2
+ docker pull "$ACTIVE_IMAGE" >&2 && return 0
+ fi
cat >&2 <<EOF
Image not found and pull failed: $ACTIVE_IMAGE
It is produced by the image-builder job (full Slackware $VERSION_ID +
diff --git a/test-logic.sh b/test-logic.sh
index 563eb30..a46b582 100644
--- a/test-logic.sh
+++ b/test-logic.sh
@@ -234,6 +234,36 @@ if depends_on_failed "$T/cat/a" dead; then bad "a blocked with empty dead"; else
dead=("%README%")
if depends_on_failed "$T/cat/a" dead; then bad "%README% treated as dep"; else ok "%README% not treated as dep"; fi
+# --- require_image / --pull ---------------------------------------------------
+# Stub docker: records "pull"/"inspect" calls; STUB_PULL / STUB_LOCAL set outcomes.
+echo "require_image:"
+docker() {
+ CALLS+="$1 "
+ case "$1" in
+ pull) [[ $STUB_PULL -eq 0 ]] ;;
+ image) [[ $STUB_LOCAL -eq 1 ]] ;;
+ esac
+}
+ACTIVE_IMAGE="reg/img:current"
+PULL_IMAGE=0; parse_args --pull pkg >/dev/null 2>&1
+[[ $PULL_IMAGE -eq 1 ]] && ok "--pull sets PULL_IMAGE" || bad "--pull not parsed"
+TARGET_ARG=""
+
+PULL_IMAGE=0; STUB_LOCAL=1; STUB_PULL=0; CALLS=""
+require_image 2>/dev/null
+[[ $CALLS == "image " ]] && ok "no --pull + local: no pull" || bad "no --pull + local: calls '$CALLS'"
+
+PULL_IMAGE=1; STUB_LOCAL=1; STUB_PULL=0; CALLS=""
+require_image 2>/dev/null
+[[ $CALLS == "pull " ]] && ok "--pull: pulls even when local" || bad "--pull: calls '$CALLS'"
+
+PULL_IMAGE=1; STUB_LOCAL=1; STUB_PULL=1; CALLS=""
+( require_image ) 2>/dev/null && ok "--pull fails + local: falls back" || bad "--pull fails + local: exited"
+
+PULL_IMAGE=1; STUB_LOCAL=0; STUB_PULL=1; CALLS=""
+( require_image ) 2>/dev/null && bad "--pull fails + no local: should exit" || ok "--pull fails + no local: exits"
+unset -f docker; PULL_IMAGE=0
+
echo
echo "$pass passed, $fail failed"
[[ $fail -eq 0 ]] || exit 1