Files
gitea/tools/pr-watch.sh
T
jiantw83 72ba7b8801 chore(gitea): 技能依結束碼分流,可併行的步驟改成同時跑
技能以前只寫成功路徑。腳本回非 0 時,模型得自己猜下一步,猜錯就是靜靜
往下走。現在每一支腳本在檔頭宣告自己的結束碼,技能也逐碼寫明要停、要
問、還是要改參數再呼叫一次。兩支技能補上連線變數的解析步驟,讓缺值在
第一步就浮出來,而不是在中途撞出一行英文錯誤。

流程也拉平了。取內容、選範本、問輸出位置這幾件事彼此不相依,改成同一批
送出;存取庫批次同步從逐一處理改成各存取庫同時進行,一個 owner 底下有
上百個存取庫時差距最明顯。

相依的技能組下限寫進外掛設定,版本推進。
2026-08-31 11:13:32 +08:00

135 lines
5.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env sh
# pr-watch.sh — 盯著一支 PR,直到它合併或關閉。
# 用法: pr-watch.sh <owner>/<repo> <pr-index> [state-file]
# 規則: 資料一律經同目錄的 gitea.sh(pr-status、pr-comments),不自行拼 API。
# 輪詢間隔預設 60 秒,JSC_PR_WATCH_INTERVAL 可覆寫,單位是秒的正整數。
# 不自動逾時退場,盯到 PR 合併或關閉為止。
# 已回報過的留言不重複回報:最後處理過的留言時間戳寫進狀態檔,
# 預設 $JSC_HOME/pr-watch/{owner}-{repo}-{index}.seen,JSC_HOME 未設定時用 ~/.jsc。
# 狀態檔還不存在時,把既有留言整批當成新留言回報一次。寧可重複回報,
# 也不要漏掉開始盯之前就留下的審查意見。
# 每輪先看 PR 狀態,再看留言。PR 收尾那一輪若還有沒回報的留言,
# 照樣印出來再退出,退出碼仍是 0:留言不會因為 PR 剛好合併就消失。
# 輸出: 全部繁中,留言行維持 gitea.sh pr-comments 的原格式
# 「{時間}<TAB>{作者}<TAB>{類型}<TAB>{內容}」,呼叫端可直接用 cut -f 取欄位。
# 結束碼: 0=PR 已合併或關閉,最終狀態印在 stdout。盯完了,不用再盯一次。
# 2=用法或環境問題:參數個數不對、第一個參數不是 {owner}/{repo}、PR 編號不是數字、
# JSC_PR_WATCH_INTERVAL 不是正整數秒、建不出暫存檔,或第一輪就連不上 Gitea。
# 照 stderr 的訊息修參數或 GITEA_HOST、GITEA_TOKEN,再重新盯。
# 3=查不到該 PR。確認存取庫與 PR 編號再重新盯,不要原樣重試——
# 編號打錯會變成永遠輪詢一支不存在的 PR。
# 10=有新留言,內容印在 stdout。接手跑決策樹處理完,再重新盯同一支 PR。
# 130=收到 INT 或 TERM 訊號而中止。狀態檔留著,重新盯會從上次那則留言接下去。
# 護欄: gitea.sh pr-status 對不存在的 PR 會印「? none none」並且 exit 0。
# 所以 state 只認 open 與 closed、merged 只認 true 與 false,對不上就回 3。
# 少了這道白名單,PR 編號打錯會變成永遠輪詢一支不存在的 PR。
# 第一輪查詢失敗算環境沒接好,直接回 2;之後才失敗算連線抖動,警告一句繼續輪詢。
set -u
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
GITEA="$script_dir/gitea.sh"
usage() {
echo "用法: pr-watch.sh <owner>/<repo> <pr-index> [state-file]" >&2
exit 2
}
[ "$#" -ge 2 ] && [ "$#" -le 3 ] || usage
REPO="$1"; INDEX="$2"; STATE="${3:-}"
case "$REPO" in
*/*) ;;
*) echo "錯誤: 第一個參數要寫成 {owner}/{repo},收到「$REPO」。" >&2; exit 2 ;;
esac
case "$INDEX" in
''|*[!0-9]*) echo "錯誤: PR 編號要是數字,收到「$INDEX」。" >&2; exit 2 ;;
esac
INTERVAL="${JSC_PR_WATCH_INTERVAL:-60}"
case "$INTERVAL" in
''|*[!0-9]*) echo "錯誤: JSC_PR_WATCH_INTERVAL 要是正整數秒,收到「$INTERVAL」。" >&2; exit 2 ;;
esac
[ "$INTERVAL" -gt 0 ] || { echo "錯誤: JSC_PR_WATCH_INTERVAL 要大於 0。" >&2; exit 2; }
if [ -z "$STATE" ]; then
jsc_home="${JSC_HOME:-$HOME/.jsc}"
owner=${REPO%%/*}; name=${REPO#*/}
STATE="$jsc_home/pr-watch/$owner-$name-$INDEX.seen"
fi
TMP_NEW=$(mktemp) || { echo "錯誤: 無法建立暫存檔。" >&2; exit 2; }
trap 'rm -f "$TMP_NEW"' EXIT
trap 'rm -f "$TMP_NEW"; exit 130' INT TERM
SEEN=""
[ -f "$STATE" ] && SEEN=$(head -n1 "$STATE" 2>/dev/null)
save_seen() { # 時間戳 -> 寫回狀態檔
ts="$1"
[ -n "$ts" ] || return 0
dir=$(dirname -- "$STATE")
mkdir -p "$dir" 2>/dev/null || { echo "警告: 建不出狀態檔目錄 $dir,這批留言下次會重複回報。" >&2; return 0; }
printf '%s\n' "$ts" > "$STATE" 2>/dev/null \
|| echo "警告: 寫不進狀態檔 $STATE,這批留言下次會重複回報。" >&2
SEEN="$ts"
}
collect_new() { # 新留言寫進 $TMP_NEW;有東西才回 0
# 時間戳一律由同一台 Gitea 產生,格式與時區都一致,所以字串比大小就夠用;
# pr-comments 自己也是照這個字串排序的。
"$GITEA" pr-comments "$REPO" "$INDEX" 2>/dev/null \
| awk -F '\t' -v seen="$SEEN" 'NF >= 4 && ($1 "") > (seen "")' > "$TMP_NEW"
[ -s "$TMP_NEW" ]
}
flush_new() { # 印出新留言並記住最後一筆的時間戳
cat "$TMP_NEW"
save_seen "$(awk -F '\t' 'END { print $1 }' "$TMP_NEW")"
}
first_round=1
while :; do
if ! status=$("$GITEA" pr-status "$REPO" "$INDEX" 2>/dev/null); then
if [ "$first_round" -eq 1 ]; then
echo "錯誤: 連不上 Gitea 或 gitea.sh 執行失敗。請確認 GITEA_HOST 與 GITEA_TOKEN,再重新盯。" >&2
exit 2
fi
echo "警告: 這一輪查不到 PR 狀態,$INTERVAL 秒後再試。" >&2
sleep "$INTERVAL"
continue
fi
first_round=0
state=$(printf '%s\n' "$status" | awk '{ print $1 }')
merged=$(printf '%s\n' "$status" | awk '{ print $2 }')
case "$state" in
open|closed) ;;
*) echo "錯誤: 查不到 PR $REPO#$INDEX。請確認存取庫與 PR 編號。" >&2; exit 3 ;;
esac
case "$merged" in
true|false) ;;
*) echo "錯誤: 查不到 PR $REPO#$INDEX。請確認存取庫與 PR 編號。" >&2; exit 3 ;;
esac
if [ "$state" = closed ]; then
if [ "$merged" = true ]; then
printf '最終狀態: PR %s#%s 已合併。\n' "$REPO" "$INDEX"
else
printf '最終狀態: PR %s#%s 已關閉,沒有合併。\n' "$REPO" "$INDEX"
fi
if collect_new; then
echo "收尾前還有沒回報的留言:"
flush_new
fi
exit 0
fi
if collect_new; then
printf '新留言 %s 則(%s#%s),請接手跑決策樹:\n' "$(wc -l < "$TMP_NEW" | tr -d ' ')" "$REPO" "$INDEX"
flush_new
exit 10
fi
sleep "$INTERVAL"
done