Skip locally built images by their build: stanza instead of a list of names

update-containers kept LOCAL_BUILD, a hand-written list of services whose
images are built on the machine, so it would not try to pull them. The
compose files already say this. A service with a build: stanza names a
local image, and there is nothing in a registry to pull.

images_for now drops the image of any compose service that has a build:
stanza. Those services resolve no images, and the run skips them without a
log line. The list, and the app names it carried, are gone. On the VPS,
--list resolves the same images as before for every other service.
This commit is contained in:
Gabriel Brown committed 2026-10-06 10:52:47 -04:00
1 parent 913a57f8a6
commit f4fca1797f
2 files changed
+24 -13

No files matched your search

+2 -2
View File
@@ -183,8 +183,8 @@ currently parked (dir present, unit not active) — imported anyway, noted as
parked in the catalog. parked in the catalog.
**Excluded:** Watchtower (retired — replaced by the update timer after it took **Excluded:** Watchtower (retired — replaced by the update timer after it took
gitea down; its dir stays on junior until cleanup), Completeuphoria, gitea down; its dir stays on junior until cleanup), the app source
sierraandtyler, Bang (source checkouts deployed by CI from their own repos — checkouts such as sierraandtyler and Bang (deployed by CI from their own repos —
Panama owning a second copy of their compose would drift), `ci/`, `logs/`, Panama owning a second copy of their compose would drift), `ci/`, `logs/`,
and junior's loose scripts other than `update-containers.sh` (§4). and junior's loose scripts other than `update-containers.sh` (§4).
+22 -11
View File
@@ -46,10 +46,6 @@ set -uo pipefail
# podman pull <image> && systemctl --user restart podman-<name>.service # podman pull <image> && systemctl --user restart podman-<name>.service
SKIP=(postgresql authentik watchtower) SKIP=(postgresql authentik watchtower)
# Services built locally rather than pulled. There is no registry image to check,
# so pulling is pointless — they are skipped automatically when no `image:` line
# resolves, but listing them here keeps the log quiet and the intent obvious.
LOCAL_BUILD=(bang completeuphoria sierraandtyler)
# ⚠️ NOTE ON GITEA: it is deliberately NOT in SKIP. The three-day outage was caused # ⚠️ NOTE ON GITEA: it is deliberately NOT in SKIP. The three-day outage was caused
# by watchtower's recreate mechanism, not by the act of updating — restarting the # by watchtower's recreate mechanism, not by the act of updating — restarting the
@@ -90,8 +86,10 @@ list_units() {
| sort | sort
} }
# Images declared in a service's compose.yml. Ignores commented lines and the # Images declared in a service's compose.yml. Ignores commented lines, and the
# `build:` stanza, which has no pullable image. # image of any compose service that has a `build:` stanza: that tag names a
# locally built image, so there is nothing in a registry to pull. Those
# services therefore resolve no images and are skipped without a log line.
# #
# Image tags frequently reference variables from the service's .env, e.g. # Image tags frequently reference variables from the service's .env, e.g.
# ghcr.io/get-convex/convex-backend:${BACKEND_TAG:-latest} # ghcr.io/get-convex/convex-backend:${BACKEND_TAG:-latest}
@@ -102,8 +100,22 @@ list_units() {
images_for() { images_for() {
local dir="$1" local dir="$1"
[ -f "$dir/compose.yml" ] || return 0 [ -f "$dir/compose.yml" ] || return 0
grep -oE '^[[:space:]]*image:[[:space:]]*[^[:space:]#]+' "$dir/compose.yml" 2>/dev/null \ awk '
| sed -E 's/^[[:space:]]*image:[[:space:]]*//' \ function flush() { if (img != "" && !build) print img; img = ""; build = 0 }
/^[[:space:]]*#/ { next }
/^services:[[:space:]]*$/ { in_services = 1; indent = -1; next }
in_services && /^[^[:space:]]/ { flush(); in_services = 0 }
in_services && NF {
match($0, /^ */); here = RLENGTH
if (indent < 0) indent = here
if (here == indent && $0 ~ /^ *[A-Za-z0-9_.-]+:[[:space:]]*$/) { flush(); next }
if ($0 ~ /^[[:space:]]+build:/) build = 1
if (match($0, /^[[:space:]]+image:[[:space:]]*[^[:space:]#]+/)) {
img = substr($0, RSTART, RLENGTH); sub(/^[[:space:]]+image:[[:space:]]*/, "", img)
}
}
END { flush() }
' "$dir/compose.yml" 2>/dev/null \
| while IFS= read -r img; do | while IFS= read -r img; do
case "$img" in case "$img" in
*'${'*) ( set -a; [ -f "$dir/.env" ] && . "$dir/.env" >/dev/null 2>&1; set +a *'${'*) ( set -a; [ -f "$dir/.env" ] && . "$dir/.env" >/dev/null 2>&1; set +a
@@ -124,8 +136,8 @@ if [ "${LIST:-0}" = "1" ]; then
for svc in $(list_units); do for svc in $(list_units); do
printf '%-22s' "$svc" printf '%-22s' "$svc"
if in_list "$svc" "${SKIP[@]}"; then echo "SKIP (frozen)"; continue; fi if in_list "$svc" "${SKIP[@]}"; then echo "SKIP (frozen)"; continue; fi
if in_list "$svc" "${LOCAL_BUILD[@]}"; then echo "SKIP (built locally)"; continue; fi imgs="$(images_for "$(workdir_for "$svc")" | tr '\n' ' ')"
echo "$(images_for "$(workdir_for "$svc")" | tr '\n' ' ')" echo "${imgs:-(built locally, nothing to pull)}"
done done
exit 0 exit 0
fi fi
@@ -142,7 +154,6 @@ for svc in $(list_units); do
[ -n "$ONLY" ] && [ "$svc" != "$ONLY" ] && continue [ -n "$ONLY" ] && [ "$svc" != "$ONLY" ] && continue
if [ -z "$ONLY" ]; then if [ -z "$ONLY" ]; then
in_list "$svc" "${SKIP[@]}" && { log " $svc: frozen (SKIP list)"; continue; } in_list "$svc" "${SKIP[@]}" && { log " $svc: frozen (SKIP list)"; continue; }
in_list "$svc" "${LOCAL_BUILD[@]}" && continue
fi fi
# ⚠️ NEVER resurrect a service that is deliberately stopped. `systemctl restart` # ⚠️ NEVER resurrect a service that is deliberately stopped. `systemctl restart`