fix(gitea): 認證失敗不再被讀成頁面不存在
金鑰失效以前會偽裝成別的結果。指令把請求直接接進管線,管線的結束狀態 取自後段的解析程式,前段的失敗就被吃掉。wiki 頁清單因此看起來是空的, PR 留言看起來像沒有任何審查意見。wiki 讀取更把每一種失敗都翻成 「頁面不存在」。 技能組寫 wiki 的語意是附加、不覆蓋,判斷依據是先把舊內容讀回來。呼叫端 一旦把認證失敗當成一張新頁,就會整份蓋上去,舊紀錄直接消失。 現在失敗成因分開回報:找不到、金鑰失效或權限不足、其他 API 失敗,各給 一個結束碼。每條管線先接進變數,先看結束碼,再解析內容。議題工具的同一 類缺陷一併修掉。wiki 技能也把「只有找不到才可以建新頁」寫成獨立規則, 涵蓋每一條「不存在就建立」的路徑。
This commit is contained in:
+70
-12
@@ -11,6 +11,14 @@
|
||||
# issue.sh title <owner>/<repo> <index> 印出議題標題
|
||||
# issue.sh body <owner>/<repo> <index> 印出議題正文(markdown)
|
||||
# issue.sh labels-of <owner>/<repo> <index> 印出議題目前的標籤名,一行一個
|
||||
# issue.sh show <owner>/<repo> <index> 一次印出標題、標籤與正文(只打一次 API)
|
||||
# 輸出格式固定四段,正文放最後,因為只有它會多行:
|
||||
# 第 1 行 title<TAB>{標題}
|
||||
# 第 2 行 labels<TAB>{標籤名,逗號分隔;沒有標籤時這一欄留空}
|
||||
# 第 3 行 body (單獨一個字,當正文的起始標記)
|
||||
# 第 4 行起 正文原文,一直到檔尾
|
||||
# 呼叫端取標題用 head -n1 | cut -f2-,取標籤用 sed -n 2p | cut -f2-,取正文用 tail -n +4。
|
||||
# 同一筆議題要標題、正文又要標籤時一律用 show,不要連打 title、body、labels-of 三次。
|
||||
# issue.sh create <owner>/<repo> <title> <body-file> [--labels <id>[,<id>]] [--milestone <id>]
|
||||
# 建立議題。輸出兩行: index=<編號>、url=<議題網址>
|
||||
#
|
||||
@@ -21,6 +29,7 @@
|
||||
# - 正文一律用檔案傳進來,內容維持 UTF-8 與真實換行;不要在參數裡塞 \n。
|
||||
# - Gitea 1.27 沒有公開的專案看板 API,projects 會 exit 3。呼叫端要據實回報「請手動把議題拖到看板」,
|
||||
# 不要假裝已經關聯好——關聯不上跟關聯成功看起來一樣,是最容易被當成完成的一種失敗。
|
||||
# exit 3 的兩條路徑(端點回錯、端點回了但不是清單)都會印出看板網址,呼叫端一定拿得到那條連結。
|
||||
set -eu
|
||||
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
@@ -32,7 +41,7 @@ usage() {
|
||||
issue.sh labels <owner>/<repo> 列出既有標籤:id<TAB>name
|
||||
issue.sh label-ids <owner>/<repo> <name>[,<name>] 標籤名換成 id
|
||||
issue.sh projects <owner>/<repo> 列出專案看板;站台不支援就 exit 3
|
||||
issue.sh title|body|labels-of <owner>/<repo> <index>
|
||||
issue.sh title|body|labels-of|show <owner>/<repo> <index>
|
||||
issue.sh create <owner>/<repo> <title> <body-file> [--labels <id>[,<id>]] [--milestone <id>]
|
||||
結束碼: 0=成功 1=API 失敗 2=用法錯誤 3=站台不支援 4=名稱對不到 id
|
||||
EOF
|
||||
@@ -49,15 +58,30 @@ valid_repo() {
|
||||
esac
|
||||
}
|
||||
|
||||
board_url() { # 看板頁的人工網址。站台沒有看板 API 時,呼叫端要靠這條連結手動處理
|
||||
_host="${GITEA_HOST:-}"
|
||||
case "$_host" in http://*|https://*) : ;; *) _host="https://$_host" ;; esac
|
||||
printf '%s/%s/projects\n' "${_host%/}" "$repo"
|
||||
}
|
||||
|
||||
cmd="${1:-}"; [ -n "$cmd" ] || usage
|
||||
shift || true
|
||||
repo="${1:-}"
|
||||
valid_repo "$repo" || { echo "[jsc][議題][ERR]:存取庫須為 {owner}/{repo},收到「${repo:-空值}」。" >&2; exit 2; }
|
||||
shift
|
||||
|
||||
# gitea.sh 的輸出一律先接進變數再解析,不要寫成 "$GITEA" api ... | python3。
|
||||
# 管線的結束狀態取自 python,會把 gitea.sh 的失敗整個吃掉:金鑰失效時,回應是一份
|
||||
# 錯誤 JSON,python 從裡面挑不到 labels 就印出空清單並回 0,看起來像「這個議題沒有標籤」。
|
||||
api_get() { # $1=API 路徑 -> 回應內容;失敗回 1
|
||||
"$GITEA" api GET "$1"
|
||||
}
|
||||
|
||||
case "$cmd" in
|
||||
labels)
|
||||
"$GITEA" api GET "/repos/$repo/labels?limit=100" | python3 -c '
|
||||
out=$(api_get "/repos/$repo/labels?limit=100") \
|
||||
|| { echo "[jsc][議題][ERR]:讀不到 $repo 的標籤清單。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
for l in json.load(sys.stdin):
|
||||
print("%s\t%s" % (l["id"], l["name"]))
|
||||
@@ -65,7 +89,9 @@ for l in json.load(sys.stdin):
|
||||
|
||||
label-ids)
|
||||
names="${1:-}"; [ -n "$names" ] || usage
|
||||
"$GITEA" api GET "/repos/$repo/labels?limit=100" | python3 -c '
|
||||
out=$(api_get "/repos/$repo/labels?limit=100") \
|
||||
|| { echo "[jsc][議題][ERR]:讀不到 $repo 的標籤清單。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
want=[n.strip() for n in sys.argv[1].split(",") if n.strip()]
|
||||
have={l["name"]: l["id"] for l in json.load(sys.stdin)}
|
||||
@@ -77,7 +103,8 @@ print(",".join(str(have[n]) for n in want))
|
||||
' "$names" ;;
|
||||
|
||||
projects)
|
||||
if out=$("$GITEA" api GET "/repos/$repo/projects" 2>/dev/null); then
|
||||
manual=0
|
||||
if out=$(api_get "/repos/$repo/projects" 2>/dev/null); then
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
try:
|
||||
@@ -88,36 +115,65 @@ if not isinstance(d, list):
|
||||
sys.exit(3)
|
||||
for p in d:
|
||||
print("%s\t%s" % (p.get("id",""), p.get("title") or p.get("name","")))
|
||||
' || exit 3
|
||||
' || manual=1
|
||||
else
|
||||
host="${GITEA_HOST:-}"
|
||||
case "$host" in http://*|https://*) : ;; *) host="https://$host" ;; esac
|
||||
echo "[jsc][議題][WARN]:這個 Gitea 站台沒有專案看板 API,程式關聯不了。請開 ${host%/}/$repo/projects 手動把議題拖進看板。" >&2
|
||||
manual=1
|
||||
fi
|
||||
if [ "$manual" = 1 ]; then
|
||||
# 兩條 exit 3 的路徑都要印出看板網址:呼叫端的技能被要求把這條連結交給使用者,
|
||||
# 少印一次,使用者就只收到「關聯不上」而不知道要去哪裡手動拖。
|
||||
echo "[jsc][議題][WARN]:這個 Gitea 站台沒有專案看板 API,程式關聯不了。請開 $(board_url) 手動把議題拖進看板。" >&2
|
||||
exit 3
|
||||
fi ;;
|
||||
|
||||
title)
|
||||
idx="${1:-}"; [ -n "$idx" ] || usage
|
||||
"$GITEA" api GET "/repos/$repo/issues/$idx" | python3 -c '
|
||||
out=$(api_get "/repos/$repo/issues/$idx") \
|
||||
|| { echo "[jsc][議題][ERR]:讀不到議題 $repo#$idx。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
print(json.load(sys.stdin).get("title",""))
|
||||
' ;;
|
||||
|
||||
body)
|
||||
idx="${1:-}"; [ -n "$idx" ] || usage
|
||||
"$GITEA" api GET "/repos/$repo/issues/$idx" | python3 -c '
|
||||
out=$(api_get "/repos/$repo/issues/$idx") \
|
||||
|| { echo "[jsc][議題][ERR]:讀不到議題 $repo#$idx。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
sys.stdout.write(json.load(sys.stdin).get("body","") or "")
|
||||
' ;;
|
||||
|
||||
labels-of)
|
||||
idx="${1:-}"; [ -n "$idx" ] || usage
|
||||
"$GITEA" api GET "/repos/$repo/issues/$idx" | python3 -c '
|
||||
out=$(api_get "/repos/$repo/issues/$idx") \
|
||||
|| { echo "[jsc][議題][ERR]:讀不到議題 $repo#$idx 的標籤。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
for l in json.load(sys.stdin).get("labels") or []:
|
||||
print(l.get("name",""))
|
||||
' ;;
|
||||
|
||||
show)
|
||||
# 標題、標籤、正文都在同一份 API 回應裡。分成 title、body、labels-of 三次呼叫,
|
||||
# 打的是同一支端點三遍,還可能取到三個不同時間點的版本。
|
||||
idx="${1:-}"; [ -n "$idx" ] || usage
|
||||
out=$(api_get "/repos/$repo/issues/$idx") \
|
||||
|| { echo "[jsc][議題][ERR]:讀不到議題 $repo#$idx。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
try:
|
||||
d=json.load(sys.stdin)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
if not isinstance(d, dict) or not d.get("title"):
|
||||
sys.exit(1)
|
||||
print("title\t%s" % d.get("title"))
|
||||
print("labels\t%s" % ",".join((l.get("name") or "") for l in (d.get("labels") or [])))
|
||||
print("body")
|
||||
sys.stdout.write(d.get("body") or "")
|
||||
' || { echo "[jsc][議題][ERR]:議題 $repo#$idx 的回應解不出標題。" >&2; exit 1; } ;;
|
||||
|
||||
create)
|
||||
title="${1:-}"; body_file="${2:-}"
|
||||
[ -n "$title" ] || usage
|
||||
@@ -150,7 +206,9 @@ if milestone:
|
||||
with open(out, "w", encoding="utf-8") as f:
|
||||
json.dump(d, f, ensure_ascii=False)
|
||||
' "$title" "$body_file" "$labels" "$milestone" "$payload"
|
||||
"$GITEA" api POST "/repos/$repo/issues" "$payload" | python3 -c '
|
||||
out=$("$GITEA" api POST "/repos/$repo/issues" "$payload") \
|
||||
|| { echo "[jsc][議題][ERR]:建不出議題到 $repo。" >&2; exit 1; }
|
||||
printf '%s' "$out" | python3 -c '
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
print("index=%s" % (d.get("number") or d.get("index") or ""))
|
||||
|
||||
Reference in New Issue
Block a user