技能以前只寫成功路徑。腳本回非 0 時,模型得自己猜下一步,猜錯就是靜靜 往下走。現在每一支腳本在檔頭宣告自己的結束碼,技能也逐碼寫明要停、要 問、還是要改參數再呼叫一次。兩支技能補上連線變數的解析步驟,讓缺值在 第一步就浮出來,而不是在中途撞出一行英文錯誤。 流程也拉平了。取內容、選範本、問輸出位置這幾件事彼此不相依,改成同一批 送出;存取庫批次同步從逐一處理改成各存取庫同時進行,一個 owner 底下有 上百個存取庫時差距最明顯。 相依的技能組下限寫進外掛設定,版本推進。
135 lines
5.7 KiB
Bash
Executable File
135 lines
5.7 KiB
Bash
Executable File
#!/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
|