From 1352620c3d68ed695ea0bda40828dc30d2205f0e Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 18 Mar 2026 06:30:17 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20ci=5Ffix=5Fcount/ci=5Ffix=5Fincrement=20?= =?UTF-8?q?not=20atomic=20=E2=80=94=20potential=20race=20under=20concurren?= =?UTF-8?q?t=20polls=20(#118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap ci_fix_count(), ci_fix_increment(), and ci_fix_reset() with flock on a shared lockfile to prevent concurrent modification of the JSON tracker. Uses flock(1) in command-wrapping mode so each Python process holds an exclusive lock for the duration of its read-modify-write cycle. Co-Authored-By: Claude Opus 4.6 --- dev/dev-poll.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dev/dev-poll.sh b/dev/dev-poll.sh index 5a6c505..a04e14f 100755 --- a/dev/dev-poll.sh +++ b/dev/dev-poll.sh @@ -22,13 +22,14 @@ source "$(dirname "$0")/../lib/ci-helpers.sh" # Track CI fix attempts per PR to avoid infinite respawn loops CI_FIX_TRACKER="${FACTORY_ROOT}/dev/ci-fixes-${PROJECT_NAME:-harb}.json" +CI_FIX_LOCK="${CI_FIX_TRACKER}.lock" ci_fix_count() { local pr="$1" - python3 -c "import json,sys;d=json.load(open('$CI_FIX_TRACKER')) if __import__('os').path.exists('$CI_FIX_TRACKER') else {};print(d.get(str($pr),0))" 2>/dev/null || echo 0 + flock "$CI_FIX_LOCK" python3 -c "import json,sys;d=json.load(open('$CI_FIX_TRACKER')) if __import__('os').path.exists('$CI_FIX_TRACKER') else {};print(d.get(str($pr),0))" 2>/dev/null || echo 0 } ci_fix_increment() { local pr="$1" - python3 -c " + flock "$CI_FIX_LOCK" python3 -c " import json,os f='$CI_FIX_TRACKER' d=json.load(open(f)) if os.path.exists(f) else {} @@ -38,7 +39,7 @@ json.dump(d,open(f,'w')) } ci_fix_reset() { local pr="$1" - python3 -c " + flock "$CI_FIX_LOCK" python3 -c " import json,os f='$CI_FIX_TRACKER' d=json.load(open(f)) if os.path.exists(f) else {}