120 lines
3.9 KiB
Bash
Executable File
120 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env sh
|
||
# check-requires.sh — Check jsc.requires before updating one plugin.
|
||
set -u
|
||
|
||
usage() {
|
||
echo "用法:check-requires.sh {claude|codex|copilot|antigravity|kiro} {manifest}" >&2
|
||
exit 2
|
||
}
|
||
|
||
[ "$#" -eq 2 ] || usage
|
||
CLI=$1
|
||
MANIFEST=$2
|
||
|
||
case "$CLI" in
|
||
claude|codex|copilot|antigravity|kiro) ;;
|
||
*) usage ;;
|
||
esac
|
||
|
||
[ -f "$MANIFEST" ] || {
|
||
printf 'status=blocked reason=找不到 manifest:%s\n' "$MANIFEST"
|
||
exit 1
|
||
}
|
||
|
||
JSC_HOME_DIR="${JSC_HOME:-$HOME/.jsc}"
|
||
LOCAL_DIR="${JSC_LOCAL_PLUGINS:-$JSC_HOME_DIR/plugins}"
|
||
KIRO_SKILLS="${JSC_KIRO_SKILLS:-$HOME/.kiro/skills}"
|
||
|
||
python3 - "$CLI" "$MANIFEST" "$LOCAL_DIR" "$KIRO_SKILLS" <<'PY'
|
||
import glob
|
||
import json
|
||
import os
|
||
import re
|
||
import sys
|
||
|
||
cli, manifest, local_dir, kiro_skills = sys.argv[1:5]
|
||
|
||
try:
|
||
with open(manifest, encoding="utf-8") as fh:
|
||
data = json.load(fh)
|
||
except Exception as exc:
|
||
print(f"status=blocked reason=manifest 不是有效 JSON:{manifest}:{exc}")
|
||
sys.exit(1)
|
||
|
||
requires = ((data.get("jsc") or {}).get("requires") or {})
|
||
if not requires:
|
||
print("status=ok reason=沒有宣告相依版本")
|
||
sys.exit(0)
|
||
|
||
def version_tuple(value):
|
||
value = str(value or "").strip()
|
||
parts = value.split(".")
|
||
out = []
|
||
for part in parts[:3]:
|
||
match = re.match(r"^(\d+)", part)
|
||
out.append(int(match.group(1)) if match else 0)
|
||
while len(out) < 3:
|
||
out.append(0)
|
||
return tuple(out)
|
||
|
||
def current_version(plugin):
|
||
domain = plugin[4:] if plugin.startswith("jsc-") else plugin
|
||
candidates = []
|
||
patterns = []
|
||
if cli == "claude":
|
||
registry = os.path.expanduser("~/.claude/plugins/installed_plugins.json")
|
||
try:
|
||
with open(registry, encoding="utf-8") as fh:
|
||
entries = (json.load(fh).get("plugins") or {}).get(f"jsc-{domain}@jsc") or []
|
||
for entry in entries:
|
||
path = os.path.join(entry.get("installPath") or "", "plugin.json")
|
||
with open(path, encoding="utf-8") as fh:
|
||
version = json.load(fh).get("version", "")
|
||
if version:
|
||
return version, path
|
||
except Exception:
|
||
pass
|
||
patterns.append(os.path.expanduser(f"~/.claude/plugins/cache/jsc/jsc-{domain}/*/plugin.json"))
|
||
elif cli == "codex":
|
||
patterns.append(os.path.expanduser(f"~/.codex/plugins/cache/jsc/jsc-{domain}/*/plugin.json"))
|
||
elif cli == "copilot":
|
||
patterns.append(os.path.expanduser(f"~/.copilot/installed-plugins/jsc/jsc-{domain}/plugin.json"))
|
||
elif cli == "antigravity":
|
||
patterns.append(os.path.join(local_dir, domain, "plugin.json"))
|
||
patterns.append(os.path.expanduser(f"~/.antigravity/plugins/jsc-{domain}/plugin.json"))
|
||
elif cli == "kiro":
|
||
patterns.append(os.path.join(kiro_skills, f"jsc-{domain}", "plugin.json"))
|
||
|
||
for pattern in patterns:
|
||
for path in glob.glob(pattern):
|
||
try:
|
||
with open(path, encoding="utf-8") as fh:
|
||
version = json.load(fh).get("version", "")
|
||
except Exception:
|
||
continue
|
||
if version:
|
||
candidates.append((version_tuple(version), version, path))
|
||
if not candidates:
|
||
return "", ""
|
||
candidates.sort()
|
||
return candidates[-1][1], candidates[-1][2]
|
||
|
||
failures = []
|
||
for plugin, constraint in sorted(requires.items()):
|
||
required = str(constraint).strip()
|
||
minimum = required[2:].strip() if required.startswith(">=") else required
|
||
current, path = current_version(plugin)
|
||
if not current:
|
||
failures.append(f"{plugin} 需要 {required},目前未安裝")
|
||
continue
|
||
if version_tuple(current) < version_tuple(minimum):
|
||
failures.append(f"{plugin} 需要 {required},目前 {current}({path})")
|
||
|
||
if failures:
|
||
print("status=blocked reason=" + ";".join(failures))
|
||
sys.exit(1)
|
||
|
||
print("status=ok reason=相依版本符合:" + ", ".join(f"{k} {v}" for k, v in sorted(requires.items())))
|
||
sys.exit(0)
|
||
PY
|