以 plugin 形式安裝時,腳本會落在 CLI 的 plugin 快取目錄。 六支腳本原本一律取「腳本位置的上兩層」當技能組根目錄,這時一定推錯。 這一輪例行稽核的第一步就實際踩到:不先手動設環境變數,腳本根本跑不動, 而叫用它們的技能說明也沒提要設,等於留了一個必炸的預設值。 把推導規則抽成共用腳本,依序試環境變數、從目前目錄往上找、 腳本位置的上兩層、家目錄底下的 plugins,並以 gitea.sh 在不在當判準。 marketplace 每個 domain 存取庫都帶一份,單看它會把 domain 誤判成根。 推不出來就結束並指名要設環境變數,同時列出試過的每一個候選, 呼叫端一眼看得出要設什麼。只 clone 單一存取庫的環境留了逃生門: 環境變數有設且是目錄就照用,並在錯誤輸出提醒。 順手補上語言檢查與 manifest 同步兩支腳本的結束碼宣告與環境變數說明。 沒有宣告,呼叫端只能猜;猜錯就把失敗當成功。
91 lines
4.0 KiB
Bash
Executable File
91 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env sh
|
||
# sync-domains.sh — 依 Gitea 正本 marketplace 把所有 domain 存取庫同步到本機。
|
||
#
|
||
# 用法: sync-domains.sh(不吃參數)
|
||
#
|
||
# 流程:
|
||
# 1. 透過 jsc-gitea/tools/gitea.sh 讀正本 marketplace
|
||
# (plugins/meta 的 .claude-plugin/marketplace.json),取得權威 domain 清單。
|
||
# 2. 本機缺少的 domain:用 gitea.sh clone-url plugins/{domain} 取網址後 clone。
|
||
# 3. 本機已有的 domain:git pull。工作區有未提交變更就跳過 pull,只在 stderr 提醒——
|
||
# 正在改的 repo 不該被自動 pull 覆蓋。
|
||
#
|
||
# 根目錄: 交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
|
||
# 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
|
||
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
|
||
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain};都沒有才 clone 成 {root}/{domain}。
|
||
#
|
||
# 輸出: 一行一個 domain,格式 {domain}<TAB>{path}(stdout);警告與失敗說明走 stderr。
|
||
# 結束碼: 0=正本讀到、每個 domain 都在本機,而且每個既有存取庫都更新到最新
|
||
# 1=推導不出根目錄、讀不到正本 marketplace,或找不到 gitea.sh(不輸出任何 domain)
|
||
# 2=正本讀到,但有 domain 沒能取得或 clone 失敗(已輸出其餘 domain)
|
||
# 3=每個 domain 都在本機,但有存取庫沒更新到最新:工作區髒而跳過 pull,
|
||
# 或 pull 失敗。stderr 會逐一列出這些路徑。**exit 0 才代表「全部最新」**;
|
||
# 拿 3 當乾淨會讓後續步驟改在落後的檔案上。
|
||
# 同時發生時回較嚴重的那個:2 蓋過 3。
|
||
# 環境變數: JSC_PLUGINS_ROOT(根目錄)
|
||
# JSC_SYNC_DRY_RUN=1(只印出會做什麼,不動檔案;不執行 pull/clone,
|
||
# 所以也不因為「沒更新」回 3)
|
||
set -u
|
||
|
||
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||
. "$HERE/plugins-root.sh"
|
||
DRY="${JSC_SYNC_DRY_RUN:-}"
|
||
|
||
ROOT=$(jsc_plugins_root) || exit 1
|
||
|
||
GITEA=""
|
||
for g in "$ROOT/gitea/tools/gitea.sh" "$ROOT/jsc-gitea/tools/gitea.sh"; do
|
||
[ -f "$g" ] && { GITEA="$g"; break; }
|
||
done
|
||
[ -n "$GITEA" ] || { echo "找不到 gitea.sh:請先取得 jsc-gitea 存取庫" >&2; exit 1; }
|
||
|
||
mkt=$(sh "$GITEA" api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json 2>/dev/null) || mkt=""
|
||
[ -n "$mkt" ] || { echo "讀不到正本 marketplace:檢查 GITEA_HOST、GITEA_TOKEN 或 tea 登入" >&2; exit 1; }
|
||
|
||
DOMAINS=$(printf '%s\n' "$mkt" | sed -n 's/.*"name": *"jsc-\([a-z0-9-]*\)".*/\1/p' | sort -u)
|
||
[ -n "$DOMAINS" ] || { echo "正本 marketplace 裡沒有 jsc-{domain} 條目" >&2; exit 1; }
|
||
|
||
repo_path() { # $1=domain;印出既有路徑(return 0)或預設路徑(return 1)
|
||
for d in "$ROOT/$1" "$ROOT/jsc-$1"; do
|
||
[ -d "$d" ] && { printf '%s' "$d"; return 0; }
|
||
done
|
||
printf '%s' "$ROOT/$1"
|
||
return 1
|
||
}
|
||
|
||
rc=0
|
||
stale=0
|
||
for domain in $DOMAINS; do
|
||
if path=$(repo_path "$domain"); then
|
||
if [ -n "$(git -C "$path" status --porcelain 2>/dev/null)" ]; then
|
||
echo "工作區有未提交變更,跳過 pull,未更新到最新:$path" >&2
|
||
stale=1
|
||
elif [ "$DRY" = 1 ]; then
|
||
echo "[dry-run] git -C $path pull --ff-only" >&2
|
||
elif ! git -C "$path" pull --quiet --ff-only >/dev/null 2>&1; then
|
||
echo "pull 失敗,保留本機版本,未更新到最新:$path" >&2
|
||
stale=1
|
||
fi
|
||
else
|
||
url=$(sh "$GITEA" clone-url "plugins/$domain" 2>/dev/null) || url=""
|
||
if [ -z "$url" ]; then
|
||
echo "取不到 clone 網址,略過:plugins/$domain" >&2
|
||
rc=2
|
||
continue
|
||
fi
|
||
if [ "$DRY" = 1 ]; then
|
||
echo "[dry-run] git clone $url $path" >&2
|
||
elif ! git clone --quiet "$url" "$path" >/dev/null 2>&1; then
|
||
echo "clone 失敗:$url" >&2
|
||
rc=2
|
||
continue
|
||
fi
|
||
fi
|
||
printf '%s\t%s\n' "$domain" "$path"
|
||
done
|
||
|
||
# 2(取不到或 clone 失敗)比 3(沒更新到最新)嚴重,優先回報。
|
||
[ "$rc" -eq 0 ] && [ "$stale" -eq 1 ] && rc=3
|
||
exit $rc
|