工程+规则(CI回执等待异步化·用户令): ci_watch.sh新件(认领判据=回执commit为合并提交或后代+回执时刻晚于合并,半截JSON重试,900s超时防挂死,经git公共目录恒读主区回执)+process-conventions§1.3回执等待纪律+AGENTS.md运行与验证一句+iter技能总收口步换代——合回落地先呈报总结,回执等待一律run_in_background后台任务,禁前台sleep/轮询;两会话实证(合并→呈报阻塞20.5分钟/全天前台sleep等闸)
This commit is contained in:
1 parent
de5e31ed55
commit
e340f14e87
3 files changed
+59
-2
No files matched your search
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# 回执等待件——合回后等 CI 回执的后台轮询器(2026-11-02 用户裁;规则面=process-conventions §1 第 3 条「回执等待纪律」)。
|
||||
# 用法: tools/ci_watch.sh <合并提交hash> [超时秒,默认 900]
|
||||
# 调用面: 合回落地先向用户呈报本批总结,随即以 run_in_background 后台任务运行本件;命令结束时
|
||||
# harness 唤醒会话——绿→删区收口;红→呈报红闸名单走修复批;超时→呈报「回执未落」(CI 可能崩溃)。
|
||||
# 「本次回执已落」认领判据(三条全满足,防认错轮):
|
||||
# ① 回执可整读——CI 写回执非原子,半截/读空静默重试;
|
||||
# ② 回执 commit 为传入提交或其后代——另一会话合回推进尖端触发补跑覆盖时按后代认领(coalesce 语义见 ci_runner.sh);
|
||||
# ③ 回执 timestamp 晚于合并提交时刻——排除合并前盘上的旧回执(旧尖端是合并提交的祖先,只查②会误领旧回执)。
|
||||
# 退出码: 0=绿 1=红 2=超时/回执未落 3=参数错误
|
||||
set -u
|
||||
# 主工作区定位:CI 只在主工作区跑(post-merge 钩子限主区),从任务 worktree 内调用也要读主区回执——
|
||||
# 经 git 公共目录(.git)的上级定位主仓根,主区调用与 worktree 调用同一读数
|
||||
COMMON="$(git -C "$(dirname "$0")" rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" \
|
||||
|| { echo "不在 git 仓库内"; exit 3; }
|
||||
REPO="$(dirname "$COMMON")"
|
||||
STATUS="$REPO/game/tests/.gates/ci-status.json"
|
||||
BASE="${1:-}"
|
||||
TIMEOUT="${2:-900}"
|
||||
INTERVAL=30
|
||||
[ -n "$BASE" ] || { echo "用法: ci_watch.sh <合并提交hash> [超时秒]"; exit 3; }
|
||||
BASE="$(git -C "$REPO" rev-parse --verify "${BASE}^{commit}")" || { echo "提交不存在: $BASE"; exit 3; }
|
||||
BASE_CT="$(git -C "$REPO" log -1 --format=%ct "$BASE")"
|
||||
_deadline=$(( $(date +%s) + TIMEOUT ))
|
||||
while :; do
|
||||
_read="$(python -I -c "
|
||||
import json, sys, datetime
|
||||
try:
|
||||
d = json.load(open(sys.argv[1], encoding='utf-8'))
|
||||
ts = int(datetime.datetime.fromisoformat(d['timestamp']).timestamp())
|
||||
print(d.get('commit', ''), d.get('verdict', ''), ts)
|
||||
except Exception:
|
||||
pass
|
||||
" "$STATUS")"
|
||||
_com=""; _verdict=""; _ts=""
|
||||
if [ -n "$_read" ]; then
|
||||
set -- $_read
|
||||
[ $# -eq 3 ] && { _com="$1"; _verdict="$2"; _ts="$3"; }
|
||||
fi
|
||||
if [ -n "$_com" ] && [ -n "$_verdict" ] && [ -n "$_ts" ] \
|
||||
&& [ "$_ts" -gt "$BASE_CT" ] \
|
||||
&& git -C "$REPO" merge-base --is-ancestor "$BASE" "$_com" 2>/dev/null; then
|
||||
python -I -c "
|
||||
import json, sys
|
||||
d = json.load(open(sys.argv[1], encoding='utf-8'))
|
||||
print('CI回执:', d.get('verdict'), '| commit', d.get('commit', '')[:8], '| 耗时', d.get('duration_s'), 's')
|
||||
if d.get('note'): print('note:', d['note'])
|
||||
for g in d.get('red_gates', []): print('红闸:', g)
|
||||
for l in d.get('key_lines', []): print('key:', l)
|
||||
" "$STATUS"
|
||||
[ "$_verdict" = "green" ] && exit 0
|
||||
exit 1
|
||||
fi
|
||||
_now=$(date +%s); _left=$((_deadline - _now))
|
||||
[ "$_left" -le 0 ] && { echo "超时(${TIMEOUT}s)回执未落——CI 可能崩溃或未触发,查 game/tests/.gates/ 的 ci.lock 与 ci.log"; exit 2; }
|
||||
sleep $(( _left < INTERVAL ? _left : INTERVAL ))
|
||||
done
|
||||
Reference in new issue
Block a user