#!/usr/bin/env sh # check-page-name.sh — 比對 wiki 頁名樣式三處是否一致。 # # 用法: check-page-name.sh # 是放 domain 存取庫的目錄,例如 /root/plugins。 # 目錄名同時吃 {domain} 與 jsc-{domain} 兩種寫法,也吃已安裝版面的版本目錄。 # # 查哪三處: # 1. {gitea}/tools/page-name.sh — 頁名樣式正本 # 2. {hooks}/hooks/comment-scope.sh — hook 內建的一份 # 3. {log}/tools/worklog-pending.sh — 工作日誌暫存內建的一份 # # 為什麼三處各留一份,不共用函式: hook 必須自足。hook 在執行期去載別的 plugin 路徑, # 那個 plugin 沒裝、版本不同、或路徑換了,hook 就當場失效,而且失效是安靜的。 # 共用函式換來的一致,付出的是 hook 的可用性。所以改成三處各自實作,一致性由稽核時比對。 # # 每處斷言四項(判準見 jsc-meta references/guidelines.md 的「Wiki 頁命名總表」): # 前兩處比對整個頁名,四項全查;第三處只驗純雜湊,沒有型別前綴,只查後兩項長度。 # 1. 型別清單 — 十五種型別逐一出現(只查比對整個頁名的那兩處): QUESTION、PLAN、ANALYZE、DELIVER、MAINTAIN、REPO、 # LOG、LEARN、ERROR、CHECK、REPORT、SKILLSET、TOOLING、MONITOR、CONTENTS。 # CONTENTS 是第十五種型別,「接受 CONTENTS」由這一項涵蓋。 # 2. 未知型別 — 出現 {某型別}_CONTENTS 或 {某型別}_{HASH} 的字樣,那個型別要在十五種之內。 # 3. 40 碼 — 接受完整 SHA-1 的 40 碼長度。 # 4. 8 碼 — 仍接受舊的 8 碼長度,遷移期間的舊頁名才讀得到。 # 5. H 開頭 — 仍接受 H 加 7 碼的舊頁名。這一項單獨列,因為三處很容易「一致地只收 # 純十六進位」——一致但都是錯的,前四項照樣全過。 # # 判定方式與其極限: 本腳本做的是**文字層**比對,不執行那三支腳本。 # 三支的寫法各不相同(正規表示式、case 樣式、長度比較),沒有共同的可執行介面可以問, # 要問就得各寫一套呼叫方式,那等於把三處的差異又抄第四份。 # 所以長度證據同時收樣式量詞({40}、{8,40})與檔頭文字(「40 碼」),兩者都算數。 # 代價講白: 這支抓得到「某一處漏了一種型別或一種長度」,抓不到「樣式寫法本身有錯」。 # 後者仍要人看,或由那三支自己的測試涵蓋。 # # 輸出: 一行一個不合格項目,格式 {檔案}:{說明}(stderr);每處的判定摘要走 stdout。 # 結束碼: 0=三處一致,五項斷言全過 # 1=有不一致或有缺項,清單在 stderr(找不到其中一處或兩處也算這一碼) # 2=用法錯誤(本腳本只吃一個參數) # 3=三處一支都找不到——**什麼都沒查**,不等於通過。先確認 指對地方再重跑 set -u usage() { echo 'usage: check-page-name.sh ' >&2 exit 2 } [ "$#" -eq 1 ] || usage ROOT=${1%/} [ -n "$ROOT" ] || usage [ -d "$ROOT" ] || { echo "找不到根目錄:$ROOT" >&2; exit 3; } # 十五種型別。這一行是本腳本的唯一真實來源,改動時同步 guidelines.md 的「Wiki 頁命名總表」。 TYPES='QUESTION PLAN ANALYZE DELIVER MAINTAIN REPO LOG LEARN ERROR CHECK REPORT SKILLSET TOOLING MONITOR CONTENTS' # 長度證據的樣式。同時收樣式量詞與檔頭文字,理由見檔頭「判定方式與其極限」。 LEN40='\{40\}|\{8,40\}|\{40,40\}|40 碼|(-eq|=|\||\()[[:space:]]*40([^0-9]|$)' LEN8='\{8\}|\{8,40\}|\{8,8\}|8 碼|(-eq|=|\||\()[[:space:]]*8([^0-9]|$)' # H 開頭的舊頁名。舊演算法把首碼落在 0-9ABC 的改寫成 H 加原前 7 碼,十六個首碼有十三個 # 會命中,所以既有頁名多半長這樣。少了這一項,三處可以「都只收純十六進位」而一致地錯。 LENH='H\[0-9A-F\]\{7\}|H\[0-9A-Fa-f\]\{7\}|H 加(上)?(原)?前 7 碼|H\*' hit=0 found=0 note() { # $1=檔案 $2=說明 printf '%s:%s\n' "$1" "$2" >&2 hit=1 } find_file() { # $1=domain 目錄名(不含 jsc- 前綴) $2=存取庫內相對路徑 for _d in "$ROOT/$1" "$ROOT/jsc-$1"; do [ -f "$_d/$2" ] && { printf '%s\n' "$_d/$2"; return 0; } done # 已安裝版面:一個 plugin 一個版本目錄,取排序最後的一份(通常即最新版) _c=$(ls "$ROOT/jsc-$1"/*/"$2" "$ROOT/$1"/*/"$2" 2>/dev/null | sort | tail -n1) [ -n "$_c" ] && [ -f "$_c" ] && { printf '%s\n' "$_c"; return 0; } return 1 } check_one() { # $1=檔案 $2=這一處的稱呼 $3=比對範圍:page=整個頁名 hash=只有雜湊 file=$1 label=$2 scope=$3 # 只有比對整個頁名的地方才該列型別。驗純雜湊的地方沒有型別前綴可比, # 硬要它列十五種型別,等於逼它抄一份自己用不到的清單,下次改型別就多一處會漏。 [ "$scope" = 'page' ] || { check_len "$file"; printf '%s\t%s\n' "$label" "$file"; return 0; } miss='' for t in $TYPES; do if ! grep -qE "(^|[^A-Za-z0-9_])$t([^A-Za-z0-9_]|\$)" "$file" 2>/dev/null; then if [ -z "$miss" ]; then miss=$t; else miss="$miss、$t"; fi fi done [ -z "$miss" ] || note "$file" "型別清單缺 $miss" # 反向查:出現了十五種以外的型別,代表某一處還留著已經改名或已經刪掉的型別。 unknown='' for u in $(grep -oE '(^|[^A-Za-z0-9_])[A-Z][A-Z0-9]{2,}_(CONTENTS|\{HASH\})' "$file" 2>/dev/null \ | sed -e 's/^[^A-Z]*//' -e 's/_.*$//' | sort -u); do case " $TYPES " in *" $u "*) ;; *) if [ -z "$unknown" ]; then unknown=$u; else unknown="$unknown、$u"; fi ;; esac done [ -z "$unknown" ] || note "$file" "出現十五種以外的型別 $unknown" check_len "$file" printf '%s\t%s\n' "$label" "$file" } check_len() { # $1=檔案。長度斷言三處都要過,驗純雜湊的那一處也不例外。 grep -qE "$LEN40" "$1" 2>/dev/null || note "$1" '沒有接受 40 碼長度的跡象,完整 SHA-1 的頁名會被判為不合法' grep -qE "$LEN8" "$1" 2>/dev/null || note "$1" '沒有接受 8 碼長度的跡象,遷移期間的舊頁名會讀不到' grep -qE "$LENH" "$1" 2>/dev/null || note "$1" '沒有接受 H 開頭舊頁名的跡象,八成的既有頁名會讀不到' } # 三處逐一查。找不到就記一行,不中止:一次把三處的狀況都報回去,比修一處重跑一次快。 scan() { # $1=domain 目錄名 $2=相對路徑 $3=稱呼 $4=比對範圍 page|hash if f=$(find_file "$1" "$2"); then found=$((found + 1)) check_one "$f" "$3" "$4" else printf '%s/%s:%s\n' "$1" "$2" "在 $ROOT 底下找不到這一處(也試過 jsc-$1 與版本目錄)" >&2 hit=1 fi } scan gitea tools/page-name.sh '正本' page scan hooks hooks/comment-scope.sh 'hook' page scan log tools/worklog-pending.sh '工作日誌暫存' hash if [ "$found" -eq 0 ]; then echo "三處一支都找不到,什麼都沒查:$ROOT" >&2 exit 3 fi if [ "$hit" -eq 0 ]; then echo "頁名樣式檢查通過:三處一致(型別十五種、40 碼、8 碼、H 加 7 碼)" >&2 else echo "頁名樣式檢查有不一致:已查 $found 處,清單見 stderr" >&2 fi exit $hit