feat(wiki): 目錄頁專用存取庫、HASH 去除截短與 H 前綴
What:CONTENTS 成為第 15 種頁面類型,解析鏈為 JSC_WIKI_REPO_CONTENTS 到 JSC_WIKI_REPO,刻意不退回型別變數。hash-id 拿掉 8 碼截短與 H 前綴改寫,只留大寫 轉換,輸出完整 40 碼;空輸入改成用法錯誤。新增 page-name.sh、wiki-contents.sh、 migrate-wiki.sh 與 wiki-delete 子命令。 Why:H 前綴會命中 16 個首碼裡的 13 個,還丟掉第 8 碼,把有效熵壓到 28 位元,而且 全庫查不到任何理由紀錄。目錄頁的整列 upsert 原本 14 處只有一處寫成程式,同一段判斷 做 14 次,錯一次就少一筆紀錄。 How:頁名樣式仍收 H 加 7 碼的舊頁,遷移期間讀得到舊頁。wiki-contents.sh 建新頁時 剝掉範本的示範列,否則每個目錄頁第一次建立都會留一列佔位死連結。migrate-wiki.sh 預設只印對照表,--apply 先寫新頁、確認寫成、才刪舊頁;孤兒頁只列不猜,因為 SHA-1 不可逆,新頁名只能靠候選鍵正推。 Who:jsc-gitea
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
# check-wiki-rules — 驗證 wiki repo 解析與 hash-id 規則。
|
||||
# check-wiki-rules — 驗證 wiki repo 解析、hash-id 與頁名樣式規則。
|
||||
# 涵蓋 TYPES 列的每一種頁面類型,每種三項:專用變數優先、退回共用變數、不得跨類型代用。
|
||||
# 全部通過印 OK 並 exit 0;任一項不符印出差異並 exit 1。
|
||||
# 結束碼: 0=全部通過,stdout 印 OK
|
||||
@@ -10,8 +10,9 @@ 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'
|
||||
TYPES='QUESTION PLAN ANALYZE DELIVER MAINTAIN REPO LOG LEARN ERROR CHECK REPORT SKILLSET TOOLING MONITOR CONTENTS'
|
||||
|
||||
fail() {
|
||||
printf '%s\n' "$1" >&2
|
||||
@@ -84,12 +85,57 @@ 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:首碼 0-9/A/B/C 改成 H 加前 7 碼;其餘首碼原樣輸出 8 碼大寫。
|
||||
check_hash 'case-2' 'H5172CB7'
|
||||
check_hash 'case-11' 'HA9A6662'
|
||||
check_hash 'case-1' 'HB6EC7FD'
|
||||
check_hash 'case-12' 'HCCA8D42'
|
||||
check_hash 'case-3' 'D3E5AA27'
|
||||
check_hash 'case-7' 'D794C002'
|
||||
# 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'
|
||||
|
||||
+18
-4
@@ -5,13 +5,14 @@
|
||||
# gitea.sh repos <owner> # 列出 owner 底下可讀取的 repo(全名)
|
||||
# gitea.sh default-branch <owner>/<repo> # 印出預設分支
|
||||
# gitea.sh clone-url <owner>/<repo> # 印出 clone URL
|
||||
# gitea.sh hash-id <text> # 產生 8 碼大寫 SHA-1 hash;首碼為 0-9/A/B/C 時改成 Hxxxxxxx
|
||||
# gitea.sh hash-id <text> # 產生完整 40 碼大寫 SHA-1 hash
|
||||
# gitea.sh wiki-repo <TYPE> # 解析頁面類型的 wiki 位置:
|
||||
# TYPE = QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT|SKILLSET|TOOLING|MONITOR(即頁名前綴)
|
||||
# TYPE = QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT|SKILLSET|TOOLING|MONITOR|CONTENTS(即頁名前綴)
|
||||
# 依序取 JSC_WIKI_REPO_{TYPE} > JSC_WIKI_REPO;不得跨類型代用;都未設定 exit 3
|
||||
# gitea.sh wiki-list <owner>/<repo> # 列出 wiki 頁名
|
||||
# gitea.sh wiki-get <owner>/<repo> <page> # 印出 wiki 頁 markdown;不存在時 exit 4
|
||||
# gitea.sh wiki-put <owner>/<repo> <page> <file> # 建立或更新 wiki 頁(內容取自檔案)
|
||||
# gitea.sh wiki-delete <owner>/<repo> <page> # 刪除 wiki 頁;與 wiki-put 走同一道人工確認
|
||||
# gitea.sh wiki-url <owner>/<repo> <page> # 印出 wiki 頁絕對網址(取自 API 的 html_url);跨存取庫連結用
|
||||
# gitea.sh pr-create <owner>/<repo> <head> <base> <title> <body-file> # 建立 PR,印出 PR URL
|
||||
# gitea.sh pr-status <owner>/<repo> <pr-index> # 印出「{state} {merged} {mergeable}」
|
||||
@@ -42,7 +43,7 @@
|
||||
# (~/.config/tea/config.yml,優先取 url 與 GITEA_HOST 同主機的登入,其次 default: true);兩者皆無才失敗。
|
||||
# 結束碼: 0=成功 1=api 子命令的請求失敗 2=用法錯誤或不認得的指令
|
||||
# 3=wiki-repo 的該類型沒有設定存取庫、pr-of-branch 的該分支沒有開啟中的 PR
|
||||
# 4=找不到(HTTP 404,含 wiki 頁不存在)、PR 系列子命令的 API 失敗,
|
||||
# 4=找不到(HTTP 404,含 wiki 頁不存在、wiki-delete 要刪的頁不存在)、PR 系列子命令的 API 失敗,
|
||||
# 以及 pr-of-branch 翻過 50 頁上限仍沒結束(分頁沒有前進)
|
||||
# 5=wiki 頁沒有 html_url
|
||||
# 7=Gitea 金鑰失效或權限不足(HTTP 401/403)
|
||||
@@ -59,7 +60,7 @@ confirm_write() {
|
||||
resolve_wiki_repo() { # TYPE -> JSC_WIKI_REPO_{TYPE} -> JSC_WIKI_REPO
|
||||
type=$(printf '%s' "${1:?TYPE required}" | tr a-z A-Z)
|
||||
case "$type" in
|
||||
QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT|SKILLSET|TOOLING|MONITOR) ;;
|
||||
QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT|SKILLSET|TOOLING|MONITOR|CONTENTS) ;;
|
||||
*) echo "unknown wiki type: $type" >&2; exit 2 ;;
|
||||
esac
|
||||
eval "v=\${JSC_WIKI_REPO_${type}:-}"
|
||||
@@ -280,6 +281,19 @@ print(json.dumps({"title":title,"content_base64":base64.b64encode(content).decod
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
echo "OK $page" ;;
|
||||
wiki-delete)
|
||||
# 刪除 wiki 頁。刪除是不可回復的寫入,所以走 wiki-put 同一道人工確認。
|
||||
# 404 要回 4 而不是併進通用失敗碼:搬移流程靠「舊頁還在不在」決定要不要重跑,
|
||||
# 把找不到讀成一般失敗,會讓呼叫端一直重試一個已經刪掉的頁。
|
||||
or="${1:?owner/repo required}"; page="${2:?page required}"
|
||||
confirm_write "刪除 wiki 頁" "$page"
|
||||
if ! req DELETE "/repos/$or/wiki/page/$page" >/dev/null 2>&1; then
|
||||
case "$(req_code)" in
|
||||
404) echo "wiki page not found: $page" >&2; exit 4 ;;
|
||||
*) api_fail "刪除 wiki 頁 $or/$page 失敗" ;;
|
||||
esac
|
||||
fi
|
||||
echo "OK $page" ;;
|
||||
pr-create)
|
||||
or="${1:?owner/repo required}"; head="${2:?head required}"; base="${3:?base required}"
|
||||
title="${4:?title required}"; body_file="${5:?body file required}"
|
||||
|
||||
+11
-9
@@ -1,12 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
# hash-id — 產生 8 碼大寫 SHA-1 hash。
|
||||
# hash-id — 產生完整 40 碼大寫 SHA-1 hash。
|
||||
# 用法:
|
||||
# hash-id <text>
|
||||
# printf '%s' <text> | hash-id
|
||||
# 規則:
|
||||
# 先取 SHA-1 前 8 碼大寫。
|
||||
# 若首碼為 0-9、A、B、C,改成 H 加上原 SHA-1 前 7 碼。
|
||||
# 取完整 SHA-1 四十碼,a-f 轉大寫,不截短、不加前綴。
|
||||
# 輸入為空即 exit 2。空字串本身也有合法的 SHA-1,靜靜回傳它就會生出
|
||||
# 一個看起來正常的頁名,把內容寫到沒有人讀的那一頁。
|
||||
# 結束碼: 0=成功 1=這台機器沒有 sha1sum 也沒有 shasum,算不出雜湊
|
||||
# 2=用法錯誤:沒有給文字,或給的是空字串
|
||||
set -eu
|
||||
|
||||
input=''
|
||||
@@ -18,6 +20,11 @@ else
|
||||
input=$(cat)
|
||||
fi
|
||||
|
||||
if [ -z "$input" ]; then
|
||||
echo 'usage: hash-id <text>' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if command -v sha1sum >/dev/null 2>&1; then
|
||||
raw=$(printf '%s' "$input" | sha1sum | awk '{print $1}')
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
@@ -27,9 +34,4 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
hash=$(printf '%s' "$raw" | cut -c1-8 | tr a-f A-F)
|
||||
case "$hash" in
|
||||
[0-9ABC]*) hash="H$(printf '%s' "$hash" | cut -c1-7)" ;;
|
||||
esac
|
||||
|
||||
printf '%s\n' "$hash"
|
||||
printf '%s\n' "$raw" | tr a-f A-F
|
||||
|
||||
Executable
+435
@@ -0,0 +1,435 @@
|
||||
#!/usr/bin/env sh
|
||||
# migrate-wiki.sh — 把既有 wiki 頁搬到新規則。
|
||||
#
|
||||
# 一次改兩件事:
|
||||
# 目錄頁 {TYPE}_CONTENTS 換存取庫,頁名不變。
|
||||
# 內容頁 {TYPE}_{舊 HASH} 換頁名,存取庫不變。
|
||||
#
|
||||
# 為什麼只做正推:SHA-1 不可逆,新頁名算不回舊頁名。所以拿候選鍵跑舊演算法,
|
||||
# 對得上現有頁名才算配對成功。配不上就列成孤兒,交給人處理,絕不猜。
|
||||
#
|
||||
# 用法:
|
||||
# migrate-wiki.sh [--apply] [--key <候選鍵>]...
|
||||
# 不帶 --apply 時只印對照表與孤兒清單,不寫任何東西。
|
||||
# --key 可重複,補充自動蒐集不到的候選鍵。
|
||||
#
|
||||
# 候選鍵來源:
|
||||
# 目錄頁表格的存取庫、主機、帳號、工具、期間欄,以及這幾欄依序串成的組合鍵。
|
||||
# 內容頁的 H1 標題(CHECK 頁的 H1 直接就是 {主機}/{帳號})。
|
||||
# --key 補充的候選。
|
||||
#
|
||||
# --apply 的順序:先把每一頁寫到新位置,再改連結,最後才刪舊頁。
|
||||
# 連結改寫不能省:[[...]] 只在同一個 wiki 內解析,頁名或存取庫一變,
|
||||
# 連結就指向一個不存在的頁,而畫面上看不出異常。
|
||||
# 先全部寫到新位置,wiki-url 才查得到每一個目的地的絕對網址。
|
||||
# 刪除排在確認新頁讀得回來之後:先刪再寫,中間出錯就兩邊都沒有。
|
||||
# 每一頁寫到新位置之前一定先讀目的地,只有結束碼 4 才准寫:目的地那一頁可能是
|
||||
# 前一次搬到一半留下的,也可能是 wiki-contents.sh 剛建好的活頁,直接寫就是覆蓋。
|
||||
#
|
||||
# 連結改寫的範圍限制:只改寫被搬的那些頁自己的內容。沒被搬的頁——已經合規的頁、
|
||||
# 目的地存取庫裡原有的頁——裡面指向被搬頁的 [[...]] 這支不動。要改寫全部,
|
||||
# 得把每個存取庫的每一頁都讀回來重寫,代價是整批頁的讀寫都變成寫入風險。
|
||||
# 所以改採列清單:報告的「引用被搬頁、但自己沒被搬」一節列出每一個引用方與它指到的頁名,
|
||||
# 交給人改。清單不空時結束碼是 3。
|
||||
#
|
||||
# 逐列處理的迴圈一律從 fd 3 讀清單,不佔用 stdin。wiki-put 與 wiki-delete 的
|
||||
# 人工確認要從終端讀一行,stdin 被清單檔佔走的話,每一次寫入都會被判成
|
||||
# 「沒有互動終端」而拒絕。
|
||||
# 結束碼: 0=全部搬完 1=有頁搬移失敗 2=用法錯誤,含 CONTENTS 存取庫未設定
|
||||
# 3=有需人工處理的項目:孤兒頁、沒被搬的引用方、目的地已有內容的頁
|
||||
set -eu
|
||||
|
||||
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
gitea="$dir/gitea.sh"
|
||||
hash_id="$dir/hash-id"
|
||||
TAB=$(printf '\t')
|
||||
|
||||
TYPES='QUESTION PLAN ANALYZE DELIVER MAINTAIN REPO LOG LEARN ERROR CHECK REPORT SKILLSET TOOLING MONITOR'
|
||||
|
||||
usage() {
|
||||
echo 'usage: migrate-wiki.sh [--apply] [--key <候選鍵>]...' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
apply=0
|
||||
work=$(mktemp -d)
|
||||
trap 'rm -rf "$work"' EXIT
|
||||
: > "$work/keys"
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--apply) apply=1; shift ;;
|
||||
--key)
|
||||
[ "$#" -ge 2 ] && [ -n "$2" ] || usage
|
||||
printf '%s\n' "$2" >> "$work/keys"; shift 2 ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
old_hash() { # 舊演算法:SHA-1 前 8 碼大寫;首碼落在 0-9ABC 就改成 H 加前 7 碼
|
||||
if command -v sha1sum >/dev/null 2>&1; then
|
||||
raw=$(printf '%s' "$1" | sha1sum | awk '{print $1}')
|
||||
else
|
||||
raw=$(printf '%s' "$1" | shasum -a 1 | awk '{print $1}')
|
||||
fi
|
||||
h=$(printf '%s' "$raw" | cut -c1-8 | tr a-f A-F)
|
||||
case "$h" in [0-9ABC]*) h="H$(printf '%s' "$h" | cut -c1-7)" ;; esac
|
||||
printf '%s\n' "$h"
|
||||
}
|
||||
|
||||
rc=0
|
||||
contents_repo=$(sh "$gitea" wiki-repo CONTENTS) || rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
echo '[jsc][gitea][ERR]:目錄頁的專用存取庫沒有設定。請先設 JSC_WIKI_REPO_CONTENTS 或 JSC_WIKI_REPO。' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
: > "$work/map" # 舊存取庫 \t 舊頁名 \t 新存取庫 \t 新頁名
|
||||
: > "$work/orphans" # 存取庫 \t 頁名 \t 原因
|
||||
: > "$work/pending" # 存取庫 \t 頁名 \t 型別 \t 舊 HASH
|
||||
: > "$work/allpages" # 存取庫 \t 頁名(列得出來的每一頁,含不搬的)
|
||||
: > "$work/refs" # 存取庫 \t 頁名 \t 被指到的舊頁名
|
||||
: > "$work/manual" # 需人工確認的項目,一行一句
|
||||
: > "$work/notes"
|
||||
|
||||
# ---- 第一輪:列出各型別的頁,分成目錄頁、待配對的內容頁、已經合規的頁 ----
|
||||
for ty in $TYPES; do
|
||||
rc=0
|
||||
repo=$(sh "$gitea" wiki-repo "$ty" 2>/dev/null) || rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '%s:沒有設定存取庫,略過。\n' "$ty" >> "$work/notes"
|
||||
continue
|
||||
fi
|
||||
rc=0
|
||||
pages=$(sh "$gitea" wiki-list "$repo" 2>/dev/null) || rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '%s(%s):列不出 wiki 頁(結束碼 %s)。\n' "$ty" "$repo" "$rc" >> "$work/notes"
|
||||
continue
|
||||
fi
|
||||
printf '%s\n' "$pages" > "$work/pagelist"
|
||||
while IFS= read -r pg <&3; do
|
||||
[ -n "$pg" ] || continue
|
||||
# 先收進全頁清單,再過型別前綴。引用方掃描要看的是沒被搬的那些頁。
|
||||
printf '%s\t%s\n' "$repo" "$pg" >> "$work/allpages"
|
||||
case "$pg" in "${ty}_"*) ;; *) continue ;; esac
|
||||
suffix=${pg#"${ty}_"}
|
||||
if [ "$suffix" = CONTENTS ]; then
|
||||
if [ "$repo" = "$contents_repo" ]; then
|
||||
printf '%s:已經在目錄頁存取庫。\n' "$pg" >> "$work/notes"
|
||||
else
|
||||
printf '%s\t%s\t%s\t%s\n' "$repo" "$pg" "$contents_repo" "$pg" >> "$work/map"
|
||||
fi
|
||||
elif printf '%s' "$suffix" | grep -Eq '^[0-9A-F]{40}$'; then
|
||||
printf '%s:已經是 40 碼頁名。\n' "$pg" >> "$work/notes"
|
||||
elif printf '%s' "$suffix" | grep -Eq '^([0-9A-F]{8}|H[0-9A-F]{7})$'; then
|
||||
printf '%s\t%s\t%s\t%s\n' "$repo" "$pg" "$ty" "$suffix" >> "$work/pending"
|
||||
else
|
||||
printf '%s\t%s\t%s\n' "$repo" "$pg" '頁名不符任何已知樣式' >> "$work/orphans"
|
||||
fi
|
||||
done 3< "$work/pagelist"
|
||||
done
|
||||
|
||||
# ---- 第二輪:蒐集候選鍵 ----
|
||||
# 目錄頁表格的欄位值,以及依序串成的組合鍵
|
||||
while IFS="$TAB" read -r orepo opage nrepo npage <&3; do
|
||||
[ -n "$opage" ] || continue
|
||||
case "$opage" in *_CONTENTS) ;; *) continue ;; esac
|
||||
sh "$gitea" wiki-get "$orepo" "$opage" 2>/dev/null > "$work/one" || continue
|
||||
python3 - "$work/one" <<'PY' >> "$work/keys" || true
|
||||
import re
|
||||
import sys
|
||||
|
||||
WANT = ['存取庫', '主機', '工具', '帳號', '期間']
|
||||
|
||||
lines = open(sys.argv[1], encoding='utf-8').read().split('\n')
|
||||
|
||||
|
||||
def cells(line):
|
||||
s = line.strip()
|
||||
if not s.startswith('|'):
|
||||
return None
|
||||
s = s[1:]
|
||||
if s.endswith('|'):
|
||||
s = s[:-1]
|
||||
return [c.strip() for c in s.split('|')]
|
||||
|
||||
|
||||
def is_sep(cs):
|
||||
return bool(cs) and all(c and set(c) <= set('-: ') for c in cs)
|
||||
|
||||
|
||||
def plain(v):
|
||||
v = re.sub(r'\[\[([^\]|]*)\|([^\]]*)\]\]', r'\1', v)
|
||||
v = re.sub(r'\[\[([^\]]*)\]\]', r'\1', v)
|
||||
v = re.sub(r'\[([^\]]*)\]\([^)]*\)', r'\1', v)
|
||||
return v.replace('`', '').strip()
|
||||
|
||||
|
||||
idx = {}
|
||||
seen_sep = False
|
||||
out = []
|
||||
for line in lines:
|
||||
cs = cells(line)
|
||||
if cs is None:
|
||||
idx, seen_sep = {}, False
|
||||
continue
|
||||
if is_sep(cs):
|
||||
seen_sep = True
|
||||
continue
|
||||
if not seen_sep:
|
||||
# 分隔列之前的那一列是表頭,欄位位置從它認出來。
|
||||
idx = {}
|
||||
for w in WANT:
|
||||
for i, h in enumerate(cs):
|
||||
if w in h:
|
||||
idx[w] = i
|
||||
break
|
||||
continue
|
||||
if not idx:
|
||||
continue
|
||||
parts = []
|
||||
for w in WANT:
|
||||
i = idx.get(w)
|
||||
if i is None or i >= len(cs):
|
||||
continue
|
||||
v = plain(cs[i])
|
||||
if not v:
|
||||
continue
|
||||
out.append(v)
|
||||
parts.append(v)
|
||||
if len(parts) > 1:
|
||||
out.append('/'.join(parts))
|
||||
|
||||
for v in out:
|
||||
print(v)
|
||||
PY
|
||||
done 3< "$work/map"
|
||||
|
||||
# 內容頁的 H1 標題
|
||||
while IFS="$TAB" read -r repo pg ty suffix <&3; do
|
||||
[ -n "$pg" ] || continue
|
||||
sh "$gitea" wiki-get "$repo" "$pg" 2>/dev/null > "$work/one" || continue
|
||||
sed -n 's/^# \{1,\}//p' "$work/one" | head -n 3 >> "$work/keys"
|
||||
done 3< "$work/pending"
|
||||
|
||||
sort -u "$work/keys" > "$work/keys.uniq"
|
||||
|
||||
# 候選鍵的舊 HASH 對照表
|
||||
: > "$work/hashes"
|
||||
while IFS= read -r k <&3; do
|
||||
[ -n "$k" ] || continue
|
||||
printf '%s\t%s\n' "$(old_hash "$k")" "$k" >> "$work/hashes"
|
||||
done 3< "$work/keys.uniq"
|
||||
|
||||
# ---- 第三輪:正推配對 ----
|
||||
while IFS="$TAB" read -r repo pg ty suffix <&3; do
|
||||
[ -n "$pg" ] || continue
|
||||
key=$(awk -F'\t' -v h="$suffix" '$1==h {print $2; exit}' "$work/hashes")
|
||||
if [ -z "$key" ]; then
|
||||
printf '%s\t%s\t%s\n' "$repo" "$pg" '找不到能正推出這個 HASH 的候選鍵' >> "$work/orphans"
|
||||
continue
|
||||
fi
|
||||
printf '%s\t%s\t%s\t%s_%s\n' "$repo" "$pg" "$repo" "$ty" "$(sh "$hash_id" "$key")" >> "$work/map"
|
||||
done 3< "$work/pending"
|
||||
|
||||
# ---- 第四輪:找出指向被搬頁、但自己沒被搬的引用方 ----
|
||||
# 連結改寫只動被搬的那些頁自己的內容。沒被搬的頁裡那些 [[...]] 這支碰不到,
|
||||
# 所以至少要把它們列出來交給人改,不能讓它們搬完就靜靜 404。
|
||||
if [ -s "$work/map" ]; then
|
||||
cut -f2 "$work/map" | sort -u > "$work/movednames"
|
||||
cut -f1,2 "$work/map" | sort -u > "$work/movedkeys"
|
||||
sort -u "$work/allpages" > "$work/allpages.uniq"
|
||||
while IFS="$TAB" read -r repo pg <&3; do
|
||||
[ -n "$pg" ] || continue
|
||||
if grep -qxF "$repo$TAB$pg" "$work/movedkeys"; then continue; fi
|
||||
sh "$gitea" wiki-get "$repo" "$pg" 2>/dev/null > "$work/one" || continue
|
||||
# [[顯示文字|頁名]] 的頁名在豎線右邊,[[頁名]] 就是整段。兩種都收成頁名再比對。
|
||||
targets=$(grep -oE '\[\[[^]]+\]\]' "$work/one" 2>/dev/null \
|
||||
| sed -e 's/^\[\[//' -e 's/\]\]$//' -e 's/^.*|//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \
|
||||
| sort -u | grep -xF -f "$work/movednames" 2>/dev/null || true)
|
||||
for t in $targets; do
|
||||
printf '%s\t%s\t%s\n' "$repo" "$pg" "$t" >> "$work/refs"
|
||||
done
|
||||
done 3< "$work/allpages.uniq"
|
||||
fi
|
||||
|
||||
# ---- 報告 ----
|
||||
echo '對照表(舊存取庫/舊頁名 → 新存取庫/新頁名)'
|
||||
if [ -s "$work/map" ]; then
|
||||
while IFS="$TAB" read -r orepo opage nrepo npage <&3; do
|
||||
[ -n "$opage" ] || continue
|
||||
printf ' %s/%s → %s/%s\n' "$orepo" "$opage" "$nrepo" "$npage"
|
||||
done 3< "$work/map"
|
||||
else
|
||||
echo ' (沒有需要搬移的頁)'
|
||||
fi
|
||||
|
||||
echo '孤兒頁(需人工處理)'
|
||||
if [ -s "$work/orphans" ]; then
|
||||
while IFS="$TAB" read -r repo pg why <&3; do
|
||||
[ -n "$pg" ] || continue
|
||||
printf ' %s/%s:%s\n' "$repo" "$pg" "$why"
|
||||
done 3< "$work/orphans"
|
||||
else
|
||||
echo ' (無)'
|
||||
fi
|
||||
|
||||
echo '引用被搬頁、但自己沒被搬(連結改寫碰不到,需人工改)'
|
||||
if [ -s "$work/refs" ]; then
|
||||
while IFS="$TAB" read -r repo pg target <&3; do
|
||||
[ -n "$pg" ] || continue
|
||||
printf ' %s/%s 指向 %s\n' "$repo" "$pg" "$target"
|
||||
done 3< "$work/refs"
|
||||
else
|
||||
echo ' (無)'
|
||||
fi
|
||||
|
||||
if [ -s "$work/notes" ]; then
|
||||
echo '備註'
|
||||
sed 's/^/ /' "$work/notes"
|
||||
fi
|
||||
|
||||
if [ "$apply" -eq 0 ]; then
|
||||
echo '(預覽模式,沒有寫入任何內容。加 --apply 才真的搬。)'
|
||||
if [ -s "$work/orphans" ] || [ -s "$work/refs" ]; then exit 3; fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---- 搬移第一步:把每一頁寫到新位置 ----
|
||||
mkdir -p "$work/body"
|
||||
: > "$work/done" # 舊存取庫 \t 舊頁名 \t 新存取庫 \t 新頁名 \t 序號
|
||||
: > "$work/failed"
|
||||
|
||||
abort() { # $1=訊息。金鑰或 API 出問題就整個停下,把已經做完的部分照樣報出來。
|
||||
printf '%s\n' "$1" >> "$work/failed"
|
||||
echo '搬移中止'
|
||||
sed 's/^/ /' "$work/failed"
|
||||
if [ -s "$work/manual" ]; then
|
||||
echo '需人工確認'
|
||||
sed 's/^/ /' "$work/manual"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
n=0
|
||||
while IFS="$TAB" read -r orepo opage nrepo npage <&3; do
|
||||
[ -n "$opage" ] || continue
|
||||
n=$((n+1))
|
||||
rc=0
|
||||
sh "$gitea" wiki-get "$orepo" "$opage" > "$work/body/$n" 2>/dev/null || rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '%s/%s:讀不回舊頁(結束碼 %s),這一頁不搬。\n' "$orepo" "$opage" "$rc" >> "$work/failed"
|
||||
continue
|
||||
fi
|
||||
|
||||
# 寫之前先讀目的地。只有 4 才准建新頁——部分搬完後重跑,或目的地那一頁已經由
|
||||
# wiki-contents.sh 建好,直接寫就是拿舊庫那份蓋掉一個活著的頁。
|
||||
rc=0
|
||||
sh "$gitea" wiki-get "$nrepo" "$npage" >/dev/null 2>&1 || rc=$?
|
||||
case "$rc" in
|
||||
4) ;;
|
||||
0)
|
||||
printf '%s/%s 已經有內容,不覆蓋。請比對它與 %s/%s 之後自行決定。\n' \
|
||||
"$nrepo" "$npage" "$orepo" "$opage" >> "$work/manual"
|
||||
continue ;;
|
||||
7)
|
||||
abort "$(printf '讀 %s/%s 遇金鑰失效或權限不足(結束碼 7)。' "$nrepo" "$npage")" ;;
|
||||
*)
|
||||
abort "$(printf '讀 %s/%s 失敗(結束碼 %s)。' "$nrepo" "$npage" "$rc")" ;;
|
||||
esac
|
||||
|
||||
rc=0
|
||||
sh "$gitea" wiki-put "$nrepo" "$npage" "$work/body/$n" >/dev/null || rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '%s/%s:寫不進 %s/%s(結束碼 %s)。\n' "$orepo" "$opage" "$nrepo" "$npage" "$rc" >> "$work/failed"
|
||||
continue
|
||||
fi
|
||||
printf '%s\t%s\t%s\t%s\t%s\n' "$orepo" "$opage" "$nrepo" "$npage" "$n" >> "$work/done"
|
||||
done 3< "$work/map"
|
||||
|
||||
# ---- 搬移第二步:查每一個目的地的絕對網址,改寫 [[...]] 連結 ----
|
||||
: > "$work/urls" # 舊頁名 \t 新頁名 \t 絕對網址
|
||||
while IFS="$TAB" read -r orepo opage nrepo npage idx <&3; do
|
||||
[ -n "$opage" ] || continue
|
||||
url=$(sh "$gitea" wiki-url "$nrepo" "$npage" 2>/dev/null) || continue
|
||||
printf '%s\t%s\t%s\n' "$opage" "$npage" "$url" >> "$work/urls"
|
||||
done 3< "$work/done"
|
||||
|
||||
while IFS="$TAB" read -r orepo opage nrepo npage idx <&3; do
|
||||
[ -n "$opage" ] || continue
|
||||
rc=0
|
||||
python3 - "$work/body/$idx" "$work/urls" "$work/body/$idx.new" <<'PY' || rc=$?
|
||||
import re
|
||||
import sys
|
||||
|
||||
src, urlmap_path, out = sys.argv[1:4]
|
||||
urlmap = {}
|
||||
for line in open(urlmap_path, encoding='utf-8'):
|
||||
parts = line.rstrip('\n').split('\t')
|
||||
if len(parts) == 3:
|
||||
urlmap[parts[0]] = (parts[1], parts[2])
|
||||
|
||||
text = open(src, encoding='utf-8').read()
|
||||
|
||||
|
||||
def repl(m):
|
||||
inner = m.group(1)
|
||||
if '|' in inner:
|
||||
label, target = inner.split('|', 1)
|
||||
label, target = label.strip(), target.strip()
|
||||
else:
|
||||
target = inner.strip()
|
||||
label = None
|
||||
hit = urlmap.get(target)
|
||||
if not hit:
|
||||
return m.group(0)
|
||||
newname, url = hit
|
||||
# 原本沒有顯示文字的寫法,顯示的就是頁名本身;頁名換了就跟著顯示新頁名。
|
||||
return '[%s](%s)' % (label if label is not None else newname, url)
|
||||
|
||||
|
||||
new = re.sub(r'\[\[([^\]]+)\]\]', repl, text)
|
||||
open(out, 'w', encoding='utf-8').write(new)
|
||||
# 9 代表這一頁沒有需要改寫的連結,不必再寫一次。
|
||||
raise SystemExit(0 if new != text else 9)
|
||||
PY
|
||||
if [ "$rc" -eq 9 ]; then continue; fi
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '%s/%s:連結改寫失敗。\n' "$nrepo" "$npage" >> "$work/failed"
|
||||
continue
|
||||
fi
|
||||
if ! sh "$gitea" wiki-put "$nrepo" "$npage" "$work/body/$idx.new" >/dev/null; then
|
||||
printf '%s/%s:連結改寫後寫不回去。\n' "$nrepo" "$npage" >> "$work/failed"
|
||||
fi
|
||||
done 3< "$work/done"
|
||||
|
||||
# ---- 搬移第三步:確認新頁讀得回來,才刪舊頁 ----
|
||||
: > "$work/moved"
|
||||
while IFS="$TAB" read -r orepo opage nrepo npage idx <&3; do
|
||||
[ -n "$opage" ] || continue
|
||||
if ! sh "$gitea" wiki-get "$nrepo" "$npage" >/dev/null 2>&1; then
|
||||
printf '%s/%s:新頁讀不回來,舊頁保留不刪。\n' "$nrepo" "$npage" >> "$work/failed"
|
||||
continue
|
||||
fi
|
||||
if ! sh "$gitea" wiki-delete "$orepo" "$opage" >/dev/null; then
|
||||
printf '%s/%s:新頁已就位,舊頁刪不掉,請人工刪除。\n' "$orepo" "$opage" >> "$work/failed"
|
||||
continue
|
||||
fi
|
||||
printf '%s/%s → %s/%s\n' "$orepo" "$opage" "$nrepo" "$npage" >> "$work/moved"
|
||||
done 3< "$work/done"
|
||||
|
||||
echo '已搬移'
|
||||
if [ -s "$work/moved" ]; then sed 's/^/ /' "$work/moved"; else echo ' (無)'; fi
|
||||
|
||||
if [ -s "$work/manual" ]; then
|
||||
echo '需人工確認'
|
||||
sed 's/^/ /' "$work/manual"
|
||||
fi
|
||||
|
||||
if [ -s "$work/failed" ]; then
|
||||
echo '搬移失敗'
|
||||
sed 's/^/ /' "$work/failed"
|
||||
exit 1
|
||||
fi
|
||||
if [ -s "$work/orphans" ] || [ -s "$work/refs" ] || [ -s "$work/manual" ]; then exit 3; fi
|
||||
exit 0
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env sh
|
||||
# page-name.sh — wiki 頁名樣式的唯一正本。
|
||||
#
|
||||
# 為什麼要有這支腳本:頁名樣式散在多支腳本與技能裡各寫一份,改一次規則就得
|
||||
# 到處找。集中成一支,比對的人與比對的程式讀的是同一條式子。
|
||||
#
|
||||
# 用法:
|
||||
# page-name.sh regex # 印出合法頁名的 ERE
|
||||
# page-name.sh check <page> # 驗證單一頁名
|
||||
#
|
||||
# 規則:
|
||||
# 合法頁名是 {前綴}_{尾段}。前綴取十四種內容型別之一。
|
||||
# 尾段四選一:CONTENTS 是目錄頁;40 碼大寫十六進位是內容頁;
|
||||
# 8 碼大寫十六進位,以及 H 加 7 碼大寫十六進位,留給還沒搬過來的舊頁。
|
||||
#
|
||||
# 為什麼舊頁收兩種樣式:舊演算法算出前 8 碼後,首碼落在 0-9ABC 就改寫成 H 加前 7 碼。
|
||||
# 十六個首碼裡有十三個會命中,既有舊頁名絕大多數長成 H 開頭那一種。只收純 8 碼,
|
||||
# 等於把八成的舊頁判成不合法,先驗頁名的技能就再也讀不到它們。
|
||||
#
|
||||
# 為什麼 CONTENTS 不能當前綴:CONTENTS 只用來解目錄頁的存取庫,它自己沒有頁。
|
||||
# 放行 CONTENTS_CONTENTS 或 CONTENTS_{HASH},會建出一個規格上不存在的頁。
|
||||
# 結束碼: 0=合法 1=不合法 2=用法錯誤
|
||||
set -eu
|
||||
|
||||
PREFIXES='QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT|SKILLSET|TOOLING|MONITOR'
|
||||
RE="^($PREFIXES)_(CONTENTS|[0-9A-F]{8}|H[0-9A-F]{7}|[0-9A-F]{40})\$"
|
||||
|
||||
cmd="${1-}"
|
||||
case "$cmd" in
|
||||
regex)
|
||||
[ "$#" -eq 1 ] || { echo 'usage: page-name.sh regex' >&2; exit 2; }
|
||||
printf '%s\n' "$RE" ;;
|
||||
check)
|
||||
page="${2-}"
|
||||
[ "$#" -eq 2 ] && [ -n "$page" ] || { echo 'usage: page-name.sh check <page>' >&2; exit 2; }
|
||||
if printf '%s\n' "$page" | grep -Eq "$RE"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "[jsc][gitea][ERR]:頁名不合規則:$page" >&2
|
||||
exit 1 ;;
|
||||
*)
|
||||
echo 'usage: page-name.sh regex | page-name.sh check <page>' >&2
|
||||
exit 2 ;;
|
||||
esac
|
||||
Executable
+178
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env sh
|
||||
# wiki-contents.sh — 目錄頁(*_CONTENTS)的表格列 upsert。
|
||||
#
|
||||
# 為什麼要有這支腳本:目錄頁的「找同一列就取代、找不到就附加」原本靠模型照
|
||||
# SKILL.md 手工做,十四個目錄頁只有一處寫成程式。同一段判斷做十四次,錯一次
|
||||
# 就少一筆紀錄。抽成一支,讀舊頁、比對鍵、整頁寫回只有一種做法。
|
||||
#
|
||||
# 用法:
|
||||
# wiki-contents.sh upsert <TYPE> <key-col> <key> <row-file> [template-file]
|
||||
# TYPE 頁型,決定頁名 {TYPE}_CONTENTS
|
||||
# key-col 鍵在表格第幾欄,1 起算
|
||||
# key 鍵值,用來找既有列
|
||||
# row-file 整列 markdown 表格列的檔案
|
||||
# template-file 選用。頁不存在時用它建新頁
|
||||
#
|
||||
# 規則:
|
||||
# 目錄頁一律住在 CONTENTS 專用存取庫,所以存取庫走 wiki-repo CONTENTS,
|
||||
# 不走各自的頁型。
|
||||
# 只有 wiki-get 回 4 才准建新頁。回 7 或 8 一律中止:把金鑰失效讀成
|
||||
# 「頁面不存在」,就會拿新範本蓋掉活著的頁,舊紀錄整份沒了。
|
||||
# 這條規則的正本在 skills/wiki/SKILL.md 的 Rules 第 4 條。
|
||||
# 結束碼: 0=已更新或已新增 1=寫入失敗 2=用法錯誤 3=CONTENTS 存取庫未設定
|
||||
# 4=頁不存在且沒給範本 7=金鑰失效或權限不足 8=其他 API 失敗
|
||||
set -eu
|
||||
|
||||
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
gitea="$dir/gitea.sh"
|
||||
page_name="$dir/page-name.sh"
|
||||
|
||||
usage() {
|
||||
echo 'usage: wiki-contents.sh upsert <TYPE> <key-col> <key> <row-file> [template-file]' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ "${1-}" = upsert ] || usage
|
||||
shift
|
||||
[ "$#" -ge 4 ] && [ "$#" -le 5 ] || usage
|
||||
|
||||
type=$(printf '%s' "${1-}" | tr a-z A-Z)
|
||||
keycol="${2-}"
|
||||
key="${3-}"
|
||||
rowfile="${4-}"
|
||||
template="${5-}"
|
||||
|
||||
[ -n "$type" ] || usage
|
||||
page="${type}_CONTENTS"
|
||||
# 頁型合不合法交給 page-name.sh 判:型別清單只留一份正本。
|
||||
# 它連 CONTENTS 一起擋掉——CONTENTS 只用來解存取庫,沒有 CONTENTS_CONTENTS 這一頁。
|
||||
sh "$page_name" check "$page" >/dev/null 2>&1 || { echo "不能用來組目錄頁頁名的頁型: $type" >&2; usage; }
|
||||
|
||||
case "$keycol" in
|
||||
''|*[!0-9]*) echo "key-col must be a positive integer: $keycol" >&2; exit 2 ;;
|
||||
esac
|
||||
[ "$keycol" -ge 1 ] || { echo "key-col must be a positive integer: $keycol" >&2; exit 2; }
|
||||
[ -n "$key" ] || { echo 'key required' >&2; exit 2; }
|
||||
[ -f "$rowfile" ] || { echo "找不到列檔案: $rowfile" >&2; exit 2; }
|
||||
[ -z "$template" ] || [ -f "$template" ] || { echo "找不到範本檔: $template" >&2; exit 2; }
|
||||
|
||||
# 存取庫解析失敗照原碼傳出去:3 是「沒設定」,2 是型別不認得,兩者處置不同。
|
||||
rc=0
|
||||
repo=$(sh "$gitea" wiki-repo CONTENTS) || rc=$?
|
||||
[ "$rc" -eq 0 ] || exit "$rc"
|
||||
|
||||
old=$(mktemp)
|
||||
new=$(mktemp)
|
||||
trap 'rm -f "$old" "$new"' EXIT
|
||||
|
||||
fresh=0
|
||||
rc=0
|
||||
sh "$gitea" wiki-get "$repo" "$page" > "$old" 2>/dev/null || rc=$?
|
||||
|
||||
case "$rc" in
|
||||
0) ;;
|
||||
4)
|
||||
# 頁不存在。只有這一碼准許建新頁。
|
||||
[ -n "$template" ] || {
|
||||
echo "[jsc][gitea][ERR]:$repo/$page 不存在,也沒有給範本,不建新頁。" >&2
|
||||
exit 4
|
||||
}
|
||||
cat "$template" > "$old"
|
||||
fresh=1 ;;
|
||||
7)
|
||||
echo "[jsc][gitea][ERR]:讀 $repo/$page 遇金鑰失效或權限不足,整個動作中止。" >&2
|
||||
exit 7 ;;
|
||||
*)
|
||||
echo "[jsc][gitea][ERR]:讀 $repo/$page 失敗(結束碼 $rc),整個動作中止。" >&2
|
||||
exit 8 ;;
|
||||
esac
|
||||
|
||||
rc=0
|
||||
action=$(python3 - "$old" "$rowfile" "$keycol" "$key" "$new" "$fresh" <<'PY'
|
||||
import sys
|
||||
|
||||
old_path, row_path, keycol, key, new_path, fresh = sys.argv[1:7]
|
||||
keycol = int(keycol)
|
||||
fresh = fresh == '1'
|
||||
|
||||
lines = open(old_path, encoding='utf-8').read().split('\n')
|
||||
row = open(row_path, encoding='utf-8').read().strip('\n')
|
||||
|
||||
|
||||
def cells(line):
|
||||
s = line.strip()
|
||||
if not s.startswith('|'):
|
||||
return None
|
||||
s = s[1:]
|
||||
if s.endswith('|'):
|
||||
s = s[:-1]
|
||||
return [c.strip() for c in s.split('|')]
|
||||
|
||||
|
||||
def is_sep(cs):
|
||||
return bool(cs) and all(c and set(c) <= set('-: ') for c in cs)
|
||||
|
||||
|
||||
# 範本表格的示範列落在分隔列之後,會被當成真的資料列。照抄進新頁,那一列就永遠留著,
|
||||
# 之後每次 upsert 都比不到它的鍵而跳過,正式頁上多出一條指向不存在的頁的死連結。
|
||||
# 所以建新頁時剝掉分隔列之後的所有資料列,只留標題、說明、表頭與分隔列。
|
||||
if fresh:
|
||||
kept = []
|
||||
passed_sep = False
|
||||
for line in lines:
|
||||
cs = cells(line)
|
||||
if cs is None:
|
||||
kept.append(line)
|
||||
continue
|
||||
if is_sep(cs):
|
||||
passed_sep = True
|
||||
kept.append(line)
|
||||
continue
|
||||
if passed_sep:
|
||||
continue
|
||||
kept.append(line)
|
||||
lines = kept
|
||||
|
||||
# 分隔列之前的都是表頭。從分隔列之後才開始比對鍵,表頭第一欄剛好等於鍵時才不會被改掉。
|
||||
seen_sep = False
|
||||
hit = -1
|
||||
last_row = -1
|
||||
for i, line in enumerate(lines):
|
||||
cs = cells(line)
|
||||
if cs is None:
|
||||
continue
|
||||
if is_sep(cs):
|
||||
seen_sep = True
|
||||
last_row = i
|
||||
continue
|
||||
if not seen_sep:
|
||||
continue
|
||||
last_row = i
|
||||
if hit < 0 and len(cs) >= keycol and cs[keycol - 1] == key:
|
||||
hit = i
|
||||
|
||||
if hit >= 0:
|
||||
lines[hit] = row
|
||||
action = 'updated'
|
||||
elif last_row >= 0:
|
||||
lines.insert(last_row + 1, row)
|
||||
action = 'added'
|
||||
else:
|
||||
sys.stderr.write('[jsc][gitea][ERR]:頁面裡找不到 markdown 表格,無處可放這一列。\n')
|
||||
raise SystemExit(1)
|
||||
|
||||
open(new_path, 'w', encoding='utf-8').write('\n'.join(lines))
|
||||
print(action)
|
||||
PY
|
||||
) || rc=$?
|
||||
# 整不出正確的頁就不要送出去。組不出內容跟送出失敗一樣寫不進去,共用結束碼 1。
|
||||
[ "$rc" -eq 0 ] || exit 1
|
||||
|
||||
rc=0
|
||||
sh "$gitea" wiki-put "$repo" "$page" "$new" >/dev/null || rc=$?
|
||||
case "$rc" in
|
||||
0) printf '%s %s/%s\n' "$action" "$repo" "$page" ;;
|
||||
7) echo "[jsc][gitea][ERR]:寫入 $repo/$page 遇金鑰失效或權限不足。" >&2; exit 7 ;;
|
||||
8) echo "[jsc][gitea][ERR]:寫入 $repo/$page 遇 API 失敗。" >&2; exit 8 ;;
|
||||
*) echo "[jsc][gitea][ERR]:寫入 $repo/$page 失敗(結束碼 $rc)。" >&2; exit 1 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user