Make a pull that updates the puller take effect on that pull

git installs the new file by rename, so the running bash finishes on the inode
it started with and every step after the sync used the previous version.
This commit is contained in:
Gmer4Lfe
2026-08-08 13:21:14 -04:00
parent e0cf83c944
commit 934c0b56be
+40
View File
@@ -52,6 +52,14 @@
# acquire_lock — prevents concurrent pulls
# detect_hosts() — MY_ID required to build correct sparse checkout rules
# Docker check — Gitea container status probed before any SSH attempt
# Self re-exec — a pull that replaces this file restarts the run once, so the steps after
# the sync are the pulled ones rather than the ones already in memory
#
# The re-exec is the non-obvious one. This script lives in the repo it pulls, and git installs
# an updated file by rename — the running bash holds the old inode and finishes the run on the
# old logic. Nothing looks wrong; the change simply takes effect one run late. Bounded by
# VV_PULL_REEXECED so it can restart at most once, skipped entirely when the checksum is
# unchanged, and the lock is released by hand first because exec does not fire EXIT traps.
#
# ==============================================================================================
# CONFIGURATION
@@ -86,6 +94,12 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# This script lives inside the repo it pulls, so a pull can replace it mid-run. Recorded before
# anything happens; compared again once the pull lands. See the re-exec block after the sync.
_VV_SELF="$SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}")"
_VV_SELF_ARGS=("$@")
_VV_SELF_SUM="$(md5sum "$_VV_SELF" 2>/dev/null | cut -d' ' -f1)"
# Root level script — load_config.sh is in the same directory
source "$SCRIPT_DIR/load_config.sh"
@@ -284,6 +298,32 @@ else
fi
fi
# ── Re-exec if the pull replaced this script ─────────────────────────────
#
# git installs an updated file by rename, so the running bash keeps reading the inode it
# started with. That is the benign half — no torn parse, no garbage — but it means every
# step below this line runs the version that was on disk when the run began, not the one
# just pulled. Observed 2026-08-08: a pull carrying a fix to the conf-upgrade call executed
# the old call anyway, and the fix only took effect on the following run.
#
# Re-exec makes the pull self-applying. Guarded three ways:
# VV_PULL_REEXECED — set before exec, so the new image can never re-exec again. One
# restart per run, whatever the checksums say.
# checksum compare — no change means no restart, so the common case costs one md5sum.
# lock released — exec keeps the PID but does NOT fire the EXIT trap, so the lock file
# would survive into the new image, which would then find a live PID
# holding its own name and exit 1 under strict mode. Dropped by hand
# first; the new image re-acquires it immediately.
if [[ -z "${VV_PULL_REEXECED:-}" ]]; then
_vv_self_now="$(md5sum "$_VV_SELF" 2>/dev/null | cut -d' ' -f1)"
if [[ -n "$_vv_self_now" && -n "$_VV_SELF_SUM" && "$_vv_self_now" != "$_VV_SELF_SUM" ]]; then
echo " Pull updated this script — restarting so the rest of the run uses it"
_release_all_locks
export VV_PULL_REEXECED=1
exec bash "$_VV_SELF" "${_VV_SELF_ARGS[@]}"
fi
fi
# ── Permissions ──────────────────────────────────────────────────────────
echo ""
echo "━━━ $ICON_GEAR Permissions ━━━"