fix(hooks): 補齊稽核缺失並修掉護欄失效
What:依 jsc-meta:skill-check 的稽核結果修正技能與工具——補上每個步驟的可檢核完成條件、 把留在內文的標準輸入輸出流程下放 tools/、修正查表與退碼路由造成的誤判。 Why:稽核發現這些缺失會讓技能在實際執行時走錯分支或靜默通過。 完成條件缺漏是最常被違反的一項;退碼誤判與查表錯誤則會讓良性狀況被當成失敗。 How:逐項對照 references/guidelines.md 的審核檢查清單修正,新增的工具都有 documented exit codes,並以真實執行驗證每條路徑。 Who:jsc-meta:skill-check 例行稽核(2026-08-25)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env sh
|
||||
# report-error.sh — 失敗回報流程:把一筆 hook 或工具異常寫成 wiki 的 ERROR_{HASH},
|
||||
# 並在 ERROR_CONTENTS 附上一列索引。頁面內容套用 templates/ 的兩份範本,
|
||||
# 範本是文案的唯一來源,本腳本只填欄位。
|
||||
#
|
||||
# 用法:
|
||||
# report-error.sh --hook {名稱} --exit {碼} --summary {摘要}
|
||||
# [--repo {owner}/{repo}] [--cli {名稱}] [--session {id}]
|
||||
# [--source {stdin|env|command}] [--symptom {現象}]
|
||||
# [--cause {可能原因}] [--action {處理結果}]
|
||||
# 相關輸出(stdout/stderr 摘要)由標準輸入讀入,可省略。
|
||||
#
|
||||
# 輸出:
|
||||
# 成功印出「{頁名} {網址}」一行。
|
||||
# wiki 位置解析不出來(JSC_WIKI_REPO_ERROR 與 JSC_WIKI_REPO 都沒設,或找不到
|
||||
# gitea.sh)時安靜降級:不輸出、exit 0。回報失敗不該再變成一次失敗。
|
||||
# 寫入 wiki 失敗才以 exit 4 回報,訊息走 stderr。
|
||||
#
|
||||
# 頁名:
|
||||
# ERROR_{HASH},HASH 取「{owner}/{repo} {hook} {時間}」的 SHA-1 前 8 碼(共用 hash 規則)。
|
||||
# 時間放進 hash:同一種失敗再發生時要另開新頁,不覆寫舊紀錄。
|
||||
#
|
||||
# 誰來呼叫:
|
||||
# 由操作者手動執行,或由技能步驟執行(`jsc-hooks:hooks-install` 在 wire-cli.sh 回報
|
||||
# status=failed 時呼叫)。**不接在失敗的 hook 上自動觸發**:hook 一律安靜 exit 0,
|
||||
# 而且自我回報要走網路寫 wiki,失敗的 hook 再去回報自己會疊出迴圈。
|
||||
set -u
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
ROOT=$(cd "$HERE/.." && pwd)
|
||||
. "$ROOT/hooks/lib.sh"
|
||||
STDIN_JSON="" # 本腳本的標準輸入是錯誤輸出摘要,不是 JSON
|
||||
|
||||
hook=""; code=""; summary=""; repo=""; cli=""; session=""
|
||||
source_kind=""; symptom=""; cause=""; action=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--hook) hook="${2:-}"; shift 2 ;;
|
||||
--exit) code="${2:-}"; shift 2 ;;
|
||||
--summary) summary="${2:-}"; shift 2 ;;
|
||||
--repo) repo="${2:-}"; shift 2 ;;
|
||||
--cli) cli="${2:-}"; shift 2 ;;
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
--source) source_kind="${2:-}"; shift 2 ;;
|
||||
--symptom) symptom="${2:-}"; shift 2 ;;
|
||||
--cause) cause="${2:-}"; shift 2 ;;
|
||||
--action) action="${2:-}"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
if [ -z "$hook" ] || [ -z "$summary" ]; then
|
||||
echo "用法:report-error.sh --hook {名稱} --exit {碼} --summary {摘要} [...]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
gsh=$(jsc_gitea_sh) || exit 0
|
||||
wrepo=$(sh "$gsh" wiki-repo ERROR 2>/dev/null) || exit 0
|
||||
[ -n "$wrepo" ] || exit 0
|
||||
|
||||
# 存取庫名稱未指定就取工作目錄的 origin(只用來標記異常屬於哪個存取庫)
|
||||
if [ -z "$repo" ]; then
|
||||
origin=$(git config --get remote.origin.url 2>/dev/null || true)
|
||||
repo=$(printf '%s' "$origin" \
|
||||
| sed -n 's#.*[/:]\([^/]*\)/\([^/]*\)$#\1/\2#p' | sed 's/\.git$//')
|
||||
fi
|
||||
[ -n "$repo" ] || repo="-"
|
||||
[ -n "$cli" ] || cli=$(cli_name)
|
||||
[ -n "$session" ] || session=$(session_id)
|
||||
[ -n "$code" ] || code="-"
|
||||
[ -n "$source_kind" ] || source_kind="command"
|
||||
[ -n "$symptom" ] || symptom="$summary"
|
||||
[ -n "$cause" ] || cause="待查"
|
||||
[ -n "$action" ] || action="待處理"
|
||||
|
||||
ts=$(date +'%Y-%m-%d %H:%M:%S')
|
||||
ticket_ts=$(date +'%Y%m%d_%H%M%S')
|
||||
if [ -t 0 ]; then detail=""; else detail=$(cat 2>/dev/null | tr '\n' ' ' | cut -c1-500); fi
|
||||
[ -n "$detail" ] || detail="(無)"
|
||||
# 摘要與相關輸出都落在 markdown 表格欄位裡,半形 | 會把欄位切斷,改成全形
|
||||
detail=$(printf '%s' "$detail" | sed 's/|/|/g')
|
||||
summary=$(printf '%s' "$summary" | sed 's/|/|/g')
|
||||
|
||||
hash=$(sh "$gsh" hash-id "$repo $hook $ts" 2>/dev/null) || exit 0
|
||||
[ -n "$hash" ] || exit 0
|
||||
page="ERROR_$hash"
|
||||
|
||||
# sed 取代值要先轉義:& 與分隔字元 | 會被 sed 當語法,換行會整行斷掉
|
||||
esc() { printf '%s' "$1" | tr '\n' ' ' | sed 's/[\\&|]/\\&/g'; }
|
||||
fill() { sed "s|$1|$(esc "$2")|g"; }
|
||||
|
||||
tmp_page=$(mktemp) || exit 0
|
||||
tmp_list=$(mktemp) || { rm -f "$tmp_page"; exit 0; }
|
||||
trap 'rm -f "$tmp_page" "$tmp_list"' EXIT
|
||||
|
||||
fill '{HASH}' "$hash" < "$ROOT/templates/error-page.md" \
|
||||
| fill '{yyyy-MM-dd HH:mm:ss}' "$ts" \
|
||||
| fill '{owner}/{repo}' "$repo" \
|
||||
| fill '{cli}' "$cli" \
|
||||
| fill '{session_id}' "$session" \
|
||||
| fill '{hook_name}' "$hook" \
|
||||
| fill '{exit_code}' "$code" \
|
||||
| fill '{error_summary}' "$summary" \
|
||||
| fill '{現象描述}' "$symptom" \
|
||||
| fill '{可能原因}' "$cause" \
|
||||
| fill '{處理方式}' "$action" \
|
||||
| fill '{stdin / env / command}' "$source_kind" \
|
||||
| fill '{stdout / stderr 摘要}' "$detail" \
|
||||
| fill '{yyyyMMdd}_{HHmmss}' "$ticket_ts" > "$tmp_page"
|
||||
|
||||
row=$(printf '| %s | [[%s|%s]] | %s | %s | %s | %s |' \
|
||||
"$ts" "$hook 異常 $ts" "$page" "$repo" "$hook" "$code" "$summary")
|
||||
|
||||
# 目錄頁:已存在就把新列附在文末(最新一筆在最後);不存在就用範本建立
|
||||
if sh "$gsh" wiki-get "$wrepo" ERROR_CONTENTS > "$tmp_list" 2>/dev/null \
|
||||
&& [ -s "$tmp_list" ]; then
|
||||
printf '%s\n' "$row" >> "$tmp_list"
|
||||
else
|
||||
fill '{yyyy-MM-dd HH:mm:ss}' "$ts" < "$ROOT/templates/error-contents.md" \
|
||||
| fill '{HASH}' "$hash" \
|
||||
| fill '{error title}' "$hook 異常 $ts" \
|
||||
| fill '{owner}/{repo}' "$repo" \
|
||||
| fill '{hook_name}' "$hook" \
|
||||
| fill '{exit_code}' "$code" \
|
||||
| fill '{error_summary}' "$summary" > "$tmp_list"
|
||||
fi
|
||||
|
||||
if ! sh "$gsh" wiki-put "$wrepo" "$page" "$tmp_page" >/dev/null 2>&1; then
|
||||
echo "[jsc] 寫入 $page 失敗($wrepo)。" >&2
|
||||
exit 4
|
||||
fi
|
||||
if ! sh "$gsh" wiki-put "$wrepo" ERROR_CONTENTS "$tmp_list" >/dev/null 2>&1; then
|
||||
echo "[jsc] 寫入 ERROR_CONTENTS 失敗($wrepo),$page 已建立。" >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
url=$(sh "$gsh" wiki-url "$wrepo" "$page" 2>/dev/null || true)
|
||||
printf '%s %s\n' "$page" "$url"
|
||||
Reference in New Issue
Block a user