feat(link): 連結一律寫成 [文字](絕對網址),寫入前先驗證連得到

取消 [[頁名]] 與 [[顯示文字|頁名]] 兩種同 wiki 寫法,不再分「同存取庫」與
「跨存取庫」兩條規則。那種寫法只在自己那個 wiki 內解析,寫錯不報錯,畫面上
看起來像普通文字或死連結,巡不到也修不了。

連結寫進頁面前先過 jsc-gitea 的 link-check.sh,結束碼 0 才寫。驗證一律走 API,
不看網頁狀態碼:私有存取庫的網頁網址對未登入請求一律回 404,拿狀態碼判會把
好連結判成壞的。認證失敗回 7,與死連結的 1 分開,免得金鑰一過期就把還在的頁
整批判死。
This commit is contained in:
2026-09-02 14:27:18 +08:00
parent fc4e4a00e2
commit f4e489ceb4
17 changed files with 214 additions and 56 deletions
+84 -11
View File
@@ -25,12 +25,22 @@
# --target-branch NAME 目標分支(省略時等同來源分支)
#
# 輸出: 繁體中文 markdown 階段回報,直接貼給使用者。
# 結束碼: 0=回報完整 1=有警告(缺工作日誌,內容已暫存) 2=用法錯誤 3=相依工具找不到
# 結束碼: 0=回報完整 1=有警告(缺工作日誌或有連不到的連結) 2=用法錯誤 3=相依工具找不到
#
# 連結驗證:
# 本檔輸出的是一張給人點的 wiki 連結清單,所以印之前先把這些網址交給
# jsc-gitea/tools/link-check.sh 驗一次,逐列標出「通過、連不到、未驗證」。
# 驗證走 API 不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404。
# 這裡的驗證只註記、不阻擋——頁面在呼叫本檔之前就寫完了,擋下去只會讓使用者連回報都拿不到。
# 呼叫端該做的事在寫入之前:每個要放進頁面的連結先過 link-check.sh,結束碼 0 才寫入,
# 有 DEAD 就不寫並回報。本檔是最後一道複查,不是那一道關卡的替代品。
#
# 陷阱:
# - 結束碼 1 是警告,不是阻擋:階段的工作已經做完了,擋下去只會讓使用者拿不到回報。
# - 缺工作日誌時一定要給 --pending-file 與 --log-hash,否則內容留在對話裡,換一個工作階段就沒了。
# - 模型閘門那一列只轉述 sdlc-gate.sh report 的結果,不自己判定標籤。
# - 金鑰失效(link-check.sh 結束碼 7)標成「未驗證」,不標成「連不到」:把金鑰問題寫成死連結,
# 下一手就會照著去刪還活著的頁。
set -u
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
@@ -83,6 +93,13 @@ sdlc_gate_sh() {
resolve_dep hooks hooks/sdlc-gate.sh
}
link_check_sh() {
if [ -n "${JSC_GITEA_TOOLS:-}" ] && [ -f "$JSC_GITEA_TOOLS/link-check.sh" ]; then
printf '%s\n' "$JSC_GITEA_TOOLS/link-check.sh"; return 0
fi
resolve_dep gitea tools/link-check.sh
}
stage="${1:-}"
case "$stage" in
plan|analyze|implement|maintain) shift ;;
@@ -250,6 +267,56 @@ EOF
)
fi
# ---- 連結驗證 ----
# 先把頁名全部解成網址收在 page_rows,再一次餵給 link-check.sh:一頁一次呼叫是白付的往返成本。
TAB=$(printf '\t')
page_rows=''
url_list=''
if [ -n "$pages" ]; then
page_rows=$(printf '%s' "$pages" | while IFS= read -r item; do
[ -n "$item" ] || continue
type=${item%%:*}
page=${item#*:}
url=$(page_url "$type" "$page" || true)
printf '%s%s%s\n' "$page" "$TAB" "${url:-}"
done)
url_list=$(printf '%s\n' "$page_rows" | awk -F"$TAB" 'NF>1 && $2!=""{print $2}')
fi
[ -n "$worklog_cell" ] && [ -n "${wl_url:-}" ] && url_list=$(printf '%s\n%s' "$url_list" "$wl_url")
LINKCHECK=''
link_note=''
dead_urls=''
skip_urls=''
if [ -n "$url_list" ]; then
LINKCHECK=$(link_check_sh || true)
if [ -z "$LINKCHECK" ]; then
link_note='找不到 jsc-gitea/tools/link-check.sh,這次的連結沒有驗證過。'
else
lc_out=$(printf '%s\n' "$url_list" | "$LINKCHECK" 2>/dev/null)
lc_rc=$?
case "$lc_rc" in
0|1)
dead_urls=$(printf '%s\n' "$lc_out" | awk -F"$TAB" '$1=="DEAD"{print $2}')
# SKIP 是「沒有可查的端點」,不影響 link-check.sh 的結束碼,所以分開標、不進警告。
skip_urls=$(printf '%s\n' "$lc_out" | awk -F"$TAB" '$1=="SKIP"{print $2}')
;;
3) link_note='GITEA_HOST 沒設定,連結沒有驗證過;設好變數再跑一次。' ;;
7) link_note='Gitea 金鑰失效(401、403),連結沒有驗證過。這不代表連結是死的,先處理金鑰。' ;;
*) link_note="連結驗證沒跑完(link-check.sh 結束碼 $lc_rc)。" ;;
esac
fi
fi
[ -n "$dead_urls" ] && warn=1
verify_cell() { # $1=網址 -> 驗證結果欄
[ -n "$1" ] || { printf '未驗證'; return 0; }
if [ -z "$LINKCHECK" ] || [ -n "$link_note" ]; then printf '未驗證'; return 0; fi
if printf '%s\n' "$dead_urls" | grep -Fxq -- "$1"; then printf '**連不到**'
elif printf '%s\n' "$skip_urls" | grep -Fxq -- "$1"; then printf '無可查端點'
else printf '通過'; fi
}
# ---- 輸出 ----
printf '## 階段回報:%s\n\n' "$(stage_zh "$stage")"
printf '| 項目 | 內容 |\n| --- | --- |\n'
@@ -257,23 +324,29 @@ printf '| 模型能力標籤 | %s |\n' "$gate_result"
printf '| 工作日誌 | %s |\n' "$worklog_cell"
[ -n "$branch_rows" ] && printf '%s\n' "$branch_rows"
printf '\n### 寫入的 wiki 頁\n\n'
if [ -n "$pages" ]; then
printf '| 頁面 | 連結 |\n| --- | --- |\n'
printf '%s' "$pages" | while IFS= read -r item; do
[ -n "$item" ] || continue
type=${item%%:*}
page=${item#*:}
url=$(page_url "$type" "$page" || true)
printf '| %s | %s |\n' "$page" "${url:-(取不到網址)}"
if [ -n "$page_rows" ]; then
printf '| 頁面 | 連結 | 連結驗證 |\n| --- | --- | --- |\n'
printf '%s\n' "$page_rows" | while IFS="$TAB" read -r page url; do
[ -n "$page" ] || continue
url=${url:-}
printf '| %s | %s | %s |\n' "$page" "${url:-(取不到網址)}" "$(verify_cell "$url")"
done
else
printf '本階段沒有寫入任何 wiki 頁。\n'
fi
if [ "$warn" -eq 1 ]; then
if [ "$warn" -eq 1 ] || [ -n "$link_note" ]; then
printf '\n### 警告\n\n'
[ -n "$worklog" ] || printf -- '- 這個階段還沒有寫入工作日誌,請自行檢查是不是漏了。%s\n' "$pending_note"
[ "$stage" = implement ] && [ -z "$pr_url" ] && printf -- '- 目標分支還沒有 PR,工作尚未交出去。\n'
exit 1
[ -n "$link_note" ] && printf -- '- %s\n' "$link_note"
if [ -n "$dead_urls" ]; then
printf -- '- 下列連結連不到,請修好再貼給別人:\n'
printf '%s\n' "$dead_urls" | while IFS= read -r dead; do
[ -n "$dead" ] || continue
printf -- ' - %s\n' "$dead"
done
fi
[ "$warn" -eq 1 ] && exit 1
fi
exit 0