From 00553e69c9089501e4e758cff583c2910d1179f6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 19 Jun 2026 00:54:30 +0800 Subject: [PATCH] =?UTF-8?q?ci(cd):=20=E4=BF=AE=E6=AD=A3=20Docker=20build?= =?UTF-8?q?=20lock=20=E7=A9=BA=E9=8E=96=E8=87=AA=E6=B8=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/cd.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index af5a819b..d50294a5 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -391,9 +391,12 @@ jobs: # date -d 不接受奈秒小數點與末尾時區縮寫(CST/MST 等),導致 CREATED_EPOCH=0 → stale 永不觸發 # 2026-06-18 Codex: act-runner 容器可能沒有 GNU date / python3; # node 由 bootstrap 安裝,作為 Docker CreatedAt 的穩定解析 fallback。 + # 2026-06-19 Codex: Docker / Gitea runner 可能回傳 ISO + # `2026-06-18T16:20:00.123456789Z`;若 CREATED_EPOCH=0, + # empty lock 永遠不會自清,下一輪 deploy 會卡滿 30 分鐘。 CREATED_CLEAN=$(echo "$CREATED_AT" | sed 's/\.[0-9]*//' | sed 's/ [A-Z][A-Z]*$//') CREATED_EPOCH=$(date -d "$CREATED_CLEAN" +%s 2>/dev/null || \ - node -e 'const raw = process.argv[1] || ""; const cleaned = raw.replace(/\.\d+/, "").replace(/\s+[A-Z]{2,4}$/, ""); const m = cleaned.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+([+-]\d{2})(\d{2})$/); if (!m) process.exit(1); const ms = Date.parse(`${m[1]}T${m[2]}${m[3]}:${m[4]}`); if (!Number.isFinite(ms)) process.exit(1); console.log(Math.floor(ms / 1000));' \ + node -e 'const raw = process.argv[1] || ""; const base = raw.replace(/\.\d+/, "").replace(/\s+[A-Z]{2,4}$/, ""); const spaced = base.replace(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+([+-]\d{2})(\d{2})$/, "$1T$2$3:$4"); const iso = base.replace(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})(Z|[+-]\d{2}:?\d{2})$/, "$1T$2$3"); const candidates = [raw, base, spaced, iso]; for (const candidate of candidates) { const ms = Date.parse(candidate); if (Number.isFinite(ms)) { console.log(Math.floor(ms / 1000)); process.exit(0); } } process.exit(1);' \ "$CREATED_AT" 2>/dev/null || \ python3 -c "import sys, datetime, re; ts = re.sub(r'\\.\d+', '', sys.argv[1]); ts = re.sub(r'\\s+[A-Z]{2,4}$', '', ts.strip()); print(int(datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S %z').timestamp()))" \ "$CREATED_AT" 2>/dev/null || echo 0) @@ -412,6 +415,13 @@ jobs: $0 !~ /awk/ && $0 !~ /ps -eo pid,args/ {print} ' || true) + if [ "$CREATED_EPOCH" -eq 0 ] && \ + [ $((attempt * 10)) -gt $((EMPTY_LOCK_SECONDS * 2)) ] && \ + [ -z "$ACTIVE_DOCKER_WORK" ]; then + echo "⚠️ Docker build lock has unparsable CreatedAt (${CREATED_AT}) and no active docker build/push after $((attempt * 10))s, removing ${LOCK_NAME}" + docker network rm "$LOCK_NAME" >/dev/null 2>&1 || true + continue + fi if [ "$CREATED_EPOCH" -gt 0 ] && \ [ "$LOCK_AGE" -gt "$EMPTY_LOCK_SECONDS" ] && \ [ -z "$ACTIVE_DOCKER_WORK" ]; then