From f46e321ab4aabd7e643be0ee7c294c953bc470fa Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Tue, 18 Aug 2026 16:46:15 -0700 Subject: [PATCH] =?UTF-8?q?fix(settings-hook):=20GNU-first=20stat=20in=20t?= =?UTF-8?q?he=20lock=20stale=20check=20=E2=80=94=20Linux=20abort=20on=20he?= =?UTF-8?q?ld=20locks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Linux, BSD-style `stat -f %m` prints a multi-line FILESYSTEM block to stdout before exiting 1, so the BSD-first || chain captured that garbage concatenated with the real `stat -c %Y` epoch. The non-numeric mtime made `$(( now - mtime ))` a syntax error and set -e killed the binary with exit 1 whenever a lock dir already existed — every contention path (stale takeover, give-up, concurrent writers) broke on CI while staying green on macOS, where BSD stat -f succeeds cleanly. GNU `stat -c %Y` now goes first (BSD stat rejects -c with no stdout, so macOS falls through cleanly), and a numeric guard blanks any residual garbage so a future platform quirk degrades to the normal give-up path instead of an arithmetic abort. Same defect class as gstack-repo-mode's GNU-first ordering (#2195). Verified in an oven/bun Linux container: the four CI-failing lock tests now pass (62/62 across both files). Co-Authored-By: Claude Fable 5 --- bin/gstack-settings-hook | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bin/gstack-settings-hook b/bin/gstack-settings-hook index 56dd28828..6fd8b121a 100755 --- a/bin/gstack-settings-hook +++ b/bin/gstack-settings-hook @@ -265,7 +265,14 @@ _acquire_lock() { fi # Stale takeover: atomic rename means exactly one contender wins; the # loser loops and re-contends against the winner's fresh mkdir. - mtime=$(stat -f %m "$_LOCK_DIR" 2>/dev/null || stat -c %Y "$_LOCK_DIR" 2>/dev/null || echo "") + # GNU stat (-c %Y) first: on Linux, BSD-style `stat -f %m` prints a + # multi-line FILESYSTEM block to stdout before failing, and the || chain + # would capture that garbage alongside the real epoch. BSD stat rejects + # -c with no stdout, so macOS falls through cleanly. The numeric guard + # below makes any residual garbage inert (no takeover, normal give-up) + # instead of an arithmetic abort under set -e. + mtime=$(stat -c %Y "$_LOCK_DIR" 2>/dev/null || stat -f %m "$_LOCK_DIR" 2>/dev/null || echo "") + case "$mtime" in *[!0-9]*) mtime="" ;; esac now=$(date +%s) if [ -n "$mtime" ] && [ $(( now - mtime )) -gt "$stale_after_s" ]; then stale="$_LOCK_DIR.stale.$$-$RANDOM"