From 08053ef0ba47f8ad571bba007434190396c5eca8 Mon Sep 17 00:00:00 2001 From: NovusEdge Date: Mon, 10 Aug 2026 16:44:56 +0300 Subject: [PATCH] fix(recipes): retry setup-apkrepos until the apk lock frees setup-apkrepos runs apk update internally and takes no --wait flag, so another apk holding the database lock fails it with exit 99. That is the line the provision kept dying on, since --wait only covers apk add. Each Alpine recipe now retries setup-apkrepos, up to ~60s, so a brief lock hold waits the holder out instead of aborting the run. The until condition keeps set -e from tripping on a locked-out attempt. --- internal/recipes/bundled/devtools/install.sh | 10 +++++++++- internal/recipes/bundled/docker/install.sh | 10 +++++++++- internal/recipes/bundled/tailscale/install.sh | 10 +++++++++- internal/recipes/bundled/xfce/install-alpine.sh | 15 +++++++++------ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/internal/recipes/bundled/devtools/install.sh b/internal/recipes/bundled/devtools/install.sh index 8e5b954..fbd7f5d 100644 --- a/internal/recipes/bundled/devtools/install.sh +++ b/internal/recipes/bundled/devtools/install.sh @@ -6,7 +6,15 @@ set -e # -c enables community (where most of these live outside the base set); -1 # picks a mirror and refreshes indexes, so no separate `apk update`. -setup-apkrepos -c -1 +# setup-apkrepos runs apk update with no lock-wait, so another apk that holds +# the database lock fails it with exit 99. Retry until the lock frees, up to +# ~60s. +n=0 +until setup-apkrepos -c -1; do + n=$((n + 1)) + [ "$n" -ge 30 ] && { echo "apk database stayed locked; giving up" >&2; exit 1; } + sleep 2 +done # build-base is Alpine's meta-package for gcc/make/libc-dev: the equivalent of # Debian's build-essential. Alpine has no package called "build-essential". diff --git a/internal/recipes/bundled/docker/install.sh b/internal/recipes/bundled/docker/install.sh index 195b473..eb2c6d4 100644 --- a/internal/recipes/bundled/docker/install.sh +++ b/internal/recipes/bundled/docker/install.sh @@ -7,7 +7,15 @@ set -e # no docker package at all). -1 picks the fastest mirror and refreshes the # indexes, so a separate `apk update` would be redundant work that only widens # the window for a transient network drop to kill the run under `set -e`. -setup-apkrepos -c -1 +# setup-apkrepos runs apk update with no lock-wait, so another apk that holds +# the database lock fails it with exit 99. Retry until the lock frees, up to +# ~60s. +n=0 +until setup-apkrepos -c -1; do + n=$((n + 1)) + [ "$n" -ge 30 ] && { echo "apk database stayed locked; giving up" >&2; exit 1; } + sleep 2 +done # Verified against pkgs.alpinelinux.org: both are in community, and compose is # a SEPARATE package from docker (docker alone gives you no `docker compose`). diff --git a/internal/recipes/bundled/tailscale/install.sh b/internal/recipes/bundled/tailscale/install.sh index 5426130..fb3018e 100644 --- a/internal/recipes/bundled/tailscale/install.sh +++ b/internal/recipes/bundled/tailscale/install.sh @@ -11,7 +11,15 @@ set -e # -c enables community, where tailscale lives; -1 picks a mirror and refreshes # indexes, so no separate `apk update`. -setup-apkrepos -c -1 +# setup-apkrepos runs apk update with no lock-wait, so another apk that holds +# the database lock fails it with exit 99. Retry until the lock frees, up to +# ~60s. +n=0 +until setup-apkrepos -c -1; do + n=$((n + 1)) + [ "$n" -ge 30 ] && { echo "apk database stayed locked; giving up" >&2; exit 1; } + sleep 2 +done # --wait 60 makes apk wait up to 60s for the lock instead of failing with # exit 99 when another apk run holds it. diff --git a/internal/recipes/bundled/xfce/install-alpine.sh b/internal/recipes/bundled/xfce/install-alpine.sh index 1c1bc7a..00b3d61 100644 --- a/internal/recipes/bundled/xfce/install-alpine.sh +++ b/internal/recipes/bundled/xfce/install-alpine.sh @@ -2,12 +2,15 @@ # Installs XFCE and starts it. Runs as root over ssh, on a booted Alpine live VM. set -e -# setup-apkrepos -c -1 already refreshes the indexes ("Updating repository -# indexes... done"), so a separate `apk update` here is redundant work that -# only widens the window for a transient ssh/network drop under `set -e` to -# kill the whole provision. Don't add retries either: a real apk failure -# should still fail loudly. -setup-apkrepos -c -1 +# setup-apkrepos -c -1 refreshes the indexes, so no separate `apk update` runs +# here. It runs apk update with no lock-wait, so another apk that holds the +# database lock fails it with exit 99. Retry until the lock frees, up to ~60s. +n=0 +until setup-apkrepos -c -1; do + n=$((n + 1)) + [ "$n" -ge 30 ] && { echo "apk database stayed locked; giving up" >&2; exit 1; } + sleep 2 +done setup-xorg-base # --wait 60 makes apk wait up to 60s for the lock instead of failing with