Files
meta/tools/sync-marketplace.sh
T
jiantw83 7dc5c32de3 fix(tools): 改用共用推導取得技能組根目錄,並補齊結束碼宣告
以 plugin 形式安裝時,腳本會落在 CLI 的 plugin 快取目錄。
六支腳本原本一律取「腳本位置的上兩層」當技能組根目錄,這時一定推錯。
這一輪例行稽核的第一步就實際踩到:不先手動設環境變數,腳本根本跑不動,
而叫用它們的技能說明也沒提要設,等於留了一個必炸的預設值。

把推導規則抽成共用腳本,依序試環境變數、從目前目錄往上找、
腳本位置的上兩層、家目錄底下的 plugins,並以 gitea.sh 在不在當判準。
marketplace 每個 domain 存取庫都帶一份,單看它會把 domain 誤判成根。
推不出來就結束並指名要設環境變數,同時列出試過的每一個候選,
呼叫端一眼看得出要設什麼。只 clone 單一存取庫的環境留了逃生門:
環境變數有設且是目錄就照用,並在錯誤輸出提醒。

順手補上語言檢查與 manifest 同步兩支腳本的結束碼宣告與環境變數說明。
沒有宣告,呼叫端只能猜;猜錯就把失敗當成功。
2026-08-31 11:11:12 +08:00

128 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env sh
# sync-marketplace.sh — 把一個 plugin 條目寫進正本 marketplace,再同步到所有 domain 存取庫。
#
# 用法: sync-marketplace.sh <domain> <repo-url> <description>
# 例: sync-marketplace.sh doc https://gitea.jsc.idv.tw/plugins/doc.git "文件產出與同步"
#
# 行為:
# 1. 在 plugins/meta 的兩份正本(.claude-plugin/marketplace.json 與
# .agents/plugins/marketplace.json)插入或更新 jsc-{domain} 條目;已存在就覆寫網址與描述。
# 條目依 name 排序,格式維持兩格縮排、不轉義非 ASCII、結尾一個換行。
# 2. 兩份正本與所有副本都由同一份算好的內容複製過去,保證位元組完全一致。
# 3. 逐一複製到每個 domain 存取庫的相同兩個路徑(含新 domain 自己)。
# 4. 全部寫完後逐檔比對,有任何一份不一致就報錯。
#
# 依賴: **python3**(只用標準函式庫的 json 模組)。
# 本 repo 的腳本一律 shell 優先,這裡是唯一例外:條目的插入、排序與重新序列化要動 JSON,
# 純 shell 拼 JSON 會壞掉(縮排、逸出、非 ASCII 描述)。所以保留 python3。
# python3 不在 PATH 時:腳本不寫任何檔案,印出缺少 python3 的訊息,回 1。
# 呼叫端要先裝 python3 再重跑,不可改用 sed 手動改 marketplace。
#
# 根目錄: 交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
# 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain}。
#
# 輸出: 一行一個實際寫入的檔案路徑(stdout);略過與失敗說明走 stderr。
# 結束碼: 0=全部寫入且逐檔一致 2=用法錯誤
# 1=推導不出根目錄、缺 python3、正本讀寫失敗、建不了暫存目錄,
# 或有檔案比對不一致
# 3=寫入成功,但有 domain 存取庫不在本機(先跑 sync-domains.sh 再重跑)
# 環境變數: JSC_PLUGINS_ROOT(根目錄,見 plugins-root.sh)
set -u
usage() {
echo 'usage: sync-marketplace.sh <domain> <repo-url> <description>' >&2
exit 2
}
[ "$#" -eq 3 ] || usage
DOMAIN=$1
URL=$2
DESC=$3
[ -n "$DOMAIN" ] && [ -n "$URL" ] && [ -n "$DESC" ] || usage
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
META=$(CDPATH= cd -- "$HERE/.." && pwd)
. "$HERE/plugins-root.sh"
ROOT=$(jsc_plugins_root) || exit 1
REL_CLAUDE='.claude-plugin/marketplace.json'
REL_AGENTS='.agents/plugins/marketplace.json'
CANON="$META/$REL_CLAUDE"
[ -f "$CANON" ] || { echo "找不到正本 marketplace:$CANON" >&2; exit 1; }
# 先擋掉缺 python3 的環境,才不會寫到一半才失敗。
command -v python3 >/dev/null 2>&1 || {
echo '找不到 python3:本腳本用 python3 的 json 模組改 marketplace 條目。請先安裝 python3 再重跑。' >&2
exit 1
}
WORKDIR=$(mktemp -d) || exit 1
trap 'rm -rf "$WORKDIR"' EXIT
NEW="$WORKDIR/marketplace.json"
# 條目的插入與更新交給 json 解析器;用 sed 拼 JSON 會壞掉。
JSC_MK_DOMAIN="$DOMAIN" JSC_MK_URL="$URL" JSC_MK_DESC="$DESC" \
JSC_MK_IN="$CANON" JSC_MK_OUT="$NEW" python3 - <<'PY' || { echo "更新正本內容失敗:$CANON" >&2; exit 1; }
import json, os
name = "jsc-" + os.environ["JSC_MK_DOMAIN"]
entry = {
"name": name,
"source": {"source": "url", "url": os.environ["JSC_MK_URL"]},
"description": os.environ["JSC_MK_DESC"],
}
with open(os.environ["JSC_MK_IN"], encoding="utf-8") as fh:
data = json.load(fh)
plugins = [p for p in data.get("plugins", []) if p.get("name") != name]
plugins.append(entry)
data["plugins"] = sorted(plugins, key=lambda p: p.get("name", ""))
with open(os.environ["JSC_MK_OUT"], "w", encoding="utf-8") as fh:
fh.write(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
PY
rc=0
write_copy() { # $1=目標檔案
target=$1
dir=$(dirname "$target")
mkdir -p "$dir" 2>/dev/null || { echo "無法建立目錄:$dir" >&2; rc=1; return 1; }
cp "$NEW" "$target" 2>/dev/null || { echo "無法寫入:$target" >&2; rc=1; return 1; }
printf '%s\n' "$target"
}
# ---- 1. 兩份正本 ----
write_copy "$META/$REL_CLAUDE"
write_copy "$META/$REL_AGENTS"
# ---- 2. 每個 domain 存取庫的副本 ----
DOMAINS=$(sed -n 's/.*"name": *"jsc-\([a-z0-9-]*\)".*/\1/p' "$NEW" | sort -u)
for d in $DOMAINS; do
path=""
for cand in "$ROOT/$d" "$ROOT/jsc-$d"; do
[ -d "$cand" ] && { path="$cand"; break; }
done
if [ -z "$path" ]; then
echo "存取庫不在本機,略過:$d(先跑 sync-domains.sh)" >&2
[ "$rc" -eq 0 ] && rc=3
continue
fi
[ "$path" = "$META" ] && continue
write_copy "$path/$REL_CLAUDE"
write_copy "$path/$REL_AGENTS"
done
# ---- 3. 逐檔比對,確認位元組一致 ----
for d in "$META" $(for x in $DOMAINS; do
for cand in "$ROOT/$x" "$ROOT/jsc-$x"; do [ -d "$cand" ] && { echo "$cand"; break; }; done
done); do
for rel in "$REL_CLAUDE" "$REL_AGENTS"; do
[ -f "$d/$rel" ] || continue
cmp -s "$NEW" "$d/$rel" || { echo "內容不一致:$d/$rel" >&2; rc=1; }
done
done
exit $rc