What:依 jsc-meta:skill-check 的稽核結果修正技能與工具——補上每個步驟的可檢核完成條件、 把留在內文的標準輸入輸出流程下放 tools/、修正查表與退碼路由造成的誤判。 Why:稽核發現這些缺失會讓技能在實際執行時走錯分支或靜默通過。 完成條件缺漏是最常被違反的一項;退碼誤判與查表錯誤則會讓良性狀況被當成失敗。 How:逐項對照 references/guidelines.md 的審核檢查清單修正,新增的工具都有 documented exit codes,並以真實執行驗證每條路徑。 Who:jsc-meta:skill-check 例行稽核(2026-08-25)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
150 lines
4.9 KiB
Bash
Executable File
150 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env sh
|
||
# apply-version.sh — 改寫版本來源檔案,把套件釘選到指定版本。
|
||
# 用法:apply-version.sh <ecosystem> <project-dir> <name> <version>
|
||
# ecosystem:nodejs | python | dotnet
|
||
# 對應檔案(與 list-packages.sh 相同的解析邏輯):
|
||
# nodejs → package.json(dependencies / devDependencies)
|
||
# python → requirements.txt(優先)或 pyproject.toml 的 [project] dependencies
|
||
# dotnet → *.csproj 的 PackageReference
|
||
# 結束碼(慣例見 README「工具」章的結束碼總表):
|
||
# 0 改寫成功
|
||
# 1 參數個數不對
|
||
# 2 ecosystem 不認識
|
||
# 3 找不到對應的版本來源檔案——呼叫方跳過這個套件
|
||
# 4 檔案中找不到該套件——呼叫方跳過這個套件
|
||
# 5 需要的指令不存在(python3)
|
||
# 6 找不到專案目錄——呼叫方停手,不要當成跳過
|
||
set -u
|
||
|
||
if [ "$#" -ne 4 ]; then
|
||
echo "用法:apply-version.sh <ecosystem> <project-dir> <name> <version>" >&2
|
||
exit 1
|
||
fi
|
||
|
||
eco="$1"
|
||
DIR="$2"
|
||
name="$3"
|
||
version="$4"
|
||
|
||
# 先擋找不到專案目錄。少了這道,壞路徑會被報成「沒有版本來源檔案」,然後被當成跳過藏起來。
|
||
[ -d "$DIR" ] || { echo "project dir not found: $DIR" >&2; exit 6; }
|
||
|
||
# 三種來源檔案都靠 python3 改寫。少了 python3 會被誤判成檔案中找不到該套件,所以先擋。
|
||
command -v python3 >/dev/null 2>&1 || { echo "python3 not found" >&2; exit 5; }
|
||
|
||
case "$eco" in
|
||
nodejs)
|
||
[ -f "$DIR/package.json" ] || { echo "package.json not found in $DIR" >&2; exit 3; }
|
||
python3 - "$DIR/package.json" "$name" "$version" <<'EOF'
|
||
import json,sys
|
||
path,name,version=sys.argv[1],sys.argv[2],sys.argv[3]
|
||
d=json.load(open(path,encoding="utf-8"))
|
||
found=False
|
||
for k in ("dependencies","devDependencies"):
|
||
if name in (d.get(k) or {}):
|
||
d[k][name]=version
|
||
found=True
|
||
if not found:
|
||
sys.exit(4)
|
||
with open(path,"w",encoding="utf-8") as f:
|
||
json.dump(d,f,indent=2,ensure_ascii=False)
|
||
f.write("\n")
|
||
EOF
|
||
rc=$?
|
||
[ "$rc" -eq 0 ] || { echo "$name not found in $DIR/package.json" >&2; exit "$rc"; }
|
||
;;
|
||
|
||
python)
|
||
if [ -f "$DIR/requirements.txt" ]; then
|
||
python3 - "$DIR/requirements.txt" "$name" "$version" <<'EOF'
|
||
import re,sys
|
||
path,name,version=sys.argv[1],sys.argv[2],sys.argv[3]
|
||
pat=re.compile(r"([A-Za-z0-9_.\-\[\]]+)\s*(?:(?:==|>=|<=|~=|!=|>|<)\s*([^,;\s]+))?")
|
||
lname=name.split("[")[0].lower()
|
||
lines=open(path,encoding="utf-8").readlines()
|
||
out=[]
|
||
found=False
|
||
for line in lines:
|
||
body=line.split("#")[0].strip()
|
||
m=pat.match(body)
|
||
if m and m.group(1).split("[")[0].lower()==lname:
|
||
out.append(f"{m.group(1)}=={version}\n")
|
||
found=True
|
||
else:
|
||
out.append(line)
|
||
if not found:
|
||
sys.exit(4)
|
||
open(path,"w",encoding="utf-8").writelines(out)
|
||
EOF
|
||
rc=$?
|
||
[ "$rc" -eq 0 ] || { echo "$name not found in $DIR/requirements.txt" >&2; exit "$rc"; }
|
||
elif [ -f "$DIR/pyproject.toml" ]; then
|
||
python3 - "$DIR/pyproject.toml" "$name" "$version" <<'EOF'
|
||
import re,sys
|
||
path,name,version=sys.argv[1],sys.argv[2],sys.argv[3]
|
||
pat=re.compile(r"([A-Za-z0-9_.\-\[\]]+)\s*(?:(?:==|>=|<=|~=|!=|>|<)\s*([^,;\s\"']+))?")
|
||
lname=name.split("[")[0].lower()
|
||
lines=open(path,encoding="utf-8").readlines()
|
||
out=[]
|
||
found=False
|
||
for line in lines:
|
||
m=re.search(r'"([^"]+)"', line)
|
||
if m:
|
||
dep=m.group(1)
|
||
dm=pat.match(dep)
|
||
if dm and dm.group(1).split("[")[0].lower()==lname:
|
||
newdep=f"{dm.group(1)}=={version}"
|
||
line=line.replace(dep,newdep,1)
|
||
found=True
|
||
out.append(line)
|
||
if not found:
|
||
sys.exit(4)
|
||
open(path,"w",encoding="utf-8").writelines(out)
|
||
EOF
|
||
rc=$?
|
||
[ "$rc" -eq 0 ] || { echo "$name not found in $DIR/pyproject.toml" >&2; exit "$rc"; }
|
||
else
|
||
echo "requirements.txt or pyproject.toml not found in $DIR" >&2
|
||
exit 3
|
||
fi
|
||
;;
|
||
|
||
dotnet)
|
||
projs=$(find "$DIR" -maxdepth 3 -name '*.csproj' 2>/dev/null)
|
||
[ -n "$projs" ] || { echo "*.csproj not found in $DIR" >&2; exit 3; }
|
||
found=0
|
||
for proj in $projs; do
|
||
python3 - "$proj" "$name" "$version" <<'EOF'
|
||
import re,sys
|
||
path,name,version=sys.argv[1],sys.argv[2],sys.argv[3]
|
||
text=open(path,encoding="utf-8").read()
|
||
hit=[False]
|
||
def repl(m):
|
||
tag=m.group(0)
|
||
inc=re.search(r'Include="([^"]+)"',tag)
|
||
if inc and inc.group(1)==name:
|
||
hit[0]=True
|
||
if re.search(r'Version="[^"]*"',tag):
|
||
tag=re.sub(r'Version="[^"]*"', 'Version="%s"'%version, tag)
|
||
elif tag.endswith("/>"):
|
||
tag=tag[:-2]+' Version="%s" />'%version
|
||
else:
|
||
tag=tag[:-1]+' Version="%s">'%version
|
||
return tag
|
||
new_text=re.sub(r"<PackageReference\b[^>]*>", repl, text)
|
||
if not hit[0]:
|
||
sys.exit(4)
|
||
open(path,"w",encoding="utf-8").write(new_text)
|
||
EOF
|
||
if [ $? -eq 0 ]; then
|
||
found=1
|
||
break
|
||
fi
|
||
done
|
||
[ "$found" -eq 1 ] || { echo "PackageReference $name not found under $DIR" >&2; exit 4; }
|
||
;;
|
||
|
||
*) echo "unknown ecosystem: $eco" >&2; exit 2 ;;
|
||
esac
|
||
exit 0
|