#!/usr/bin/env sh # check-wiki-rules — 驗證 wiki repo 解析、hash-id 與頁名樣式規則。 # 涵蓋 TYPES 列的每一種頁面類型,每種三項:專用變數優先、退回共用變數、不得跨類型代用。 # 全部通過印 OK 並 exit 0;任一項不符印出差異並 exit 1。 # 結束碼: 0=全部通過,stdout 印 OK # 1=有一項不符,stderr 印出 {項目}: want=… got=… 之後立刻停住,不續跑其餘項目。 # 只有 0 與 1 兩種;這支不吃參數,也沒有用法錯誤那條路。 set -eu dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) gitea="$dir/gitea.sh" hash_id="$dir/hash-id" page_name="$dir/page-name.sh" TYPES='QUESTION PLAN ANALYZE DELIVER MAINTAIN REPO LOG LEARN ERROR CHECK REPORT SKILLSET TOOLING MONITOR CONTENTS' fail() { printf '%s\n' "$1" >&2 exit 1 } expect_eq() { got=$1 want=$2 label=$3 [ "$got" = "$want" ] || fail "$label: want=$want got=$got" } check_repo() { # type label want [VAR=值...] type=$1 label=$2 want=$3 shift 3 got=$(env -i PATH="${PATH:-/usr/bin:/bin}" "$@" "$gitea" wiki-repo "$type") expect_eq "$got" "$want" "$label" } check_no_repo() { # type label [VAR=值...]:期望 exit 3 且不印出任何 {owner}/{repo} type=$1 label=$2 shift 2 if got=$(env -i PATH="${PATH:-/usr/bin:/bin}" "$@" "$gitea" wiki-repo "$type" 2>/dev/null); then code=0 else code=$? fi [ "$code" -eq 3 ] || fail "$label: want exit=3 got exit=$code output=$got" [ -z "$got" ] || fail "$label: want no output got=$got" } check_hash() { input=$1 want=$2 got=$("$hash_id" "$input") expect_eq "$got" "$want" "hash-id $input" } # 其餘每一個類型的誘餌值。跨類型代用一旦發生,回傳的就會是 decoy/{別的類型}。 decoys() { # $1=要排除的類型 for t in $TYPES; do [ "$t" = "$1" ] || printf 'JSC_WIKI_REPO_%s=decoy/%s ' "$t" "$t" done } for ty in $TYPES; do # 1. 自己的變數贏過共用變數,也贏過其他類型的誘餌。 check_repo "$ty" "$ty specific wins" "own/$ty" \ $(decoys "$ty") "JSC_WIKI_REPO_$ty=own/$ty" JSC_WIKI_REPO='shared/wiki' # 2. 自己的變數未設定時,只退回共用變數。 check_repo "$ty" "$ty falls back to shared only" 'shared/wiki' \ $(decoys "$ty") JSC_WIKI_REPO='shared/wiki' # 3. 自己的變數與共用變數都沒有時,exit 3,不借用別的類型。 check_no_repo "$ty" "$ty never borrows another type" $(decoys "$ty") done # 未知類型 exit 2,與「設定不足」的 exit 3 分開。 if got=$(env -i PATH="${PATH:-/usr/bin:/bin}" JSC_WIKI_REPO='shared/wiki' \ "$gitea" wiki-repo NOSUCH 2>/dev/null); then code=0 else code=$? fi [ "$code" -eq 2 ] || fail "unknown type: want exit=2 got exit=$code" [ -z "$got" ] || fail "unknown type: want no output got=$got" # hash-id:完整 40 碼大寫十六進位,不截短、不加前綴。 check_hash 'case-2' '5172CB7A273D9C64ADBAAC70F321F2EDCD49C495' check_hash 'case-11' 'A9A6662D94D9CC4BE593F3846DABE9AA147333E8' check_hash 'case-1' 'B6EC7FD12D959BFFB08AC7F8B279B5255D55AA4C' check_hash 'case-12' 'CCA8D4284394EBBA49F6493004ECAAE54FD74E0F' check_hash 'case-3' 'D3E5AA27999009B42908E5BB7C1636BC14087D9C' check_hash 'case-7' 'D794C0028687E7F4F3AEFF703276964E618F2EE3' # hash-id 空輸入回 2。靜靜回傳空字串的 SHA-1 會生出一個看起來合法的頁名, # 內容就寫到沒有人讀的那一頁。 if got=$(printf '' | "$hash_id" 2>/dev/null); then code=0 else code=$? fi [ "$code" -eq 2 ] || fail "hash-id empty input: want exit=2 got exit=$code output=$got" [ -z "$got" ] || fail "hash-id empty input: want no output got=$got" # page-name.sh:四種合法尾段各一筆,不合法的一批。 check_page_ok() { sh "$page_name" check "$1" >/dev/null 2>&1 || fail "page-name $1: want exit=0" } check_page_bad() { if sh "$page_name" check "$1" >/dev/null 2>&1; then code=0 else code=$? fi [ "$code" -eq 1 ] || fail "page-name $1: want exit=1 got exit=$code" } check_page_ok 'CHECK_D3E5AA27999009B42908E5BB7C1636BC14087D9C' check_page_ok 'LOG_FB8DF0B5' check_page_ok 'PLAN_CONTENTS' # H 開頭的舊頁名。舊演算法十六個首碼裡有十三個會改寫成 H 加前 7 碼, # 既有舊頁名多數長這樣。判成不合法,先驗頁名的技能就讀不到它們。 check_page_ok 'LOG_H1A2B3C4' check_page_ok 'CHECK_H87EC88F' check_page_ok 'LOG_H87095D0' # CONTENTS 只解存取庫,自己沒有頁。放行就會建出規格上不存在的那一頁。 check_page_bad 'CONTENTS_CONTENTS' check_page_bad 'CONTENTS_D3E5AA27999009B42908E5BB7C1636BC14087D9C' check_page_bad 'NOSUCH_CONTENTS' check_page_bad 'PLAN_d3e5aa27' check_page_bad 'PLAN_H87EC88' check_page_bad 'PLAN' # regex 子命令要印得出東西,頁名樣式的正本才只有這一份。 [ -n "$(sh "$page_name" regex)" ] || fail 'page-name regex: want a pattern' printf '%s\n' 'OK'