#!/bin/bash # check-bundle-drift.sh — 驗「Arcrun 原始碼 vs bundles 鏡像」有沒有漂移(2026-07-28 立) # # 解的病(install-flow-map.md §3.8 的誠實缺口):Arcrun main 改了 cypher/portal # 但忘記重打包 bundle → 新裝用戶拿到舊引擎(07-27 同事 demo 前夕連線全斷的根因)。 # 之前只靠流程紀律,本腳本把它變機械可查。 # # 用法:scripts/check-bundle-drift.sh # 例: scripts/check-bundle-drift.sh /private/tmp/wt-arcrun /bundles-push2 # 出口碼:0=無漂移;1=有漂移(列出哪個件);2=用法/環境錯 set -euo pipefail cd "$(dirname "$0")/.." ARCRUN="${1:?用法:$0 }" BUNDLES="${2:?缺 bundles clone 路徑}" [ -d "$ARCRUN/console-ui" ] || { echo "❌ $ARCRUN 不像 Arcrun repo"; exit 2; } [ -f "$BUNDLES/manifest.json" ] || { echo "❌ $BUNDLES 沒有 manifest.json"; exit 2; } DRIFT=0 echo "① UI(console-ui/public → tier2/ui)" node products/arcrun-rag/installer/scripts/build-ui-bundle.mjs \ --arcrun "$ARCRUN" --out /tmp/drift-ui.js >/dev/null A=$(shasum -a 256 /tmp/drift-ui.js | cut -d' ' -f1) B=$(shasum -a 256 "$BUNDLES/tier2/ui/index.js" | cut -d' ' -f1) if [ "$A" = "$B" ]; then echo " ✅ 一致($(echo $A | cut -c1-12))" else echo " 🔴 漂移:原始碼 $(echo $A | cut -c1-12) ≠ 鏡像 $(echo $B | cut -c1-12) → 重跑 build-ui-bundle.mjs 推鏡像"; DRIFT=1; fi echo "② cypher(cypher-executor → tier2/cypher)" ( cd "$ARCRUN/cypher-executor" && npx wrangler deploy --dry-run --outdir /tmp/drift-cypher >/dev/null 2>&1 ) \ || { echo " ⚠️ cypher dry-run 失敗(依賴沒裝?)"; DRIFT=1; } if [ -f /tmp/drift-cypher/index.js ]; then A=$(shasum -a 256 /tmp/drift-cypher/index.js | cut -d' ' -f1) B=$(shasum -a 256 "$BUNDLES/tier2/cypher/index.js" | cut -d' ' -f1) if [ "$A" = "$B" ]; then echo " ✅ 一致($(echo $A | cut -c1-12))" else echo " 🔴 漂移:原始碼 $(echo $A | cut -c1-12) ≠ 鏡像 $(echo $B | cut -c1-12) → 重建 cypher 推鏡像"; DRIFT=1; fi fi echo "②b 其餘 tier2(kbdb/registry/mcp——07-28 真實案例:kbdb bundle 缺 /entries/libraries=庫目錄靜默失效)" for t2 in kbdb registry mcp; do ( cd "$ARCRUN/$t2" && npx wrangler deploy --dry-run --outdir /tmp/drift-$t2 >/dev/null 2>&1 ) || { echo " ⚠️ $t2 dry-run 失敗"; DRIFT=1; continue; } A=$(shasum -a 256 /tmp/drift-$t2/index.js | cut -d' ' -f1) B=$(shasum -a 256 "$BUNDLES/tier2/$t2/index.js" | cut -d' ' -f1) if [ "$A" = "$B" ]; then echo " ✅ $t2 一致($(echo $A | cut -c1-12))" else echo " 🔴 $t2 漂移:原始碼 $(echo $A | cut -c1-12) ≠ 鏡像 $(echo $B | cut -c1-12)"; DRIFT=1; fi done echo "③ daemon(collector 產物 vs 鏡像 zip)" for z in ArcrunRAG-mac-unsigned.zip; do L="products/arcrun-rag/collector/cmd/arcrun-tray/$z" [ -f "$L" ] && [ -f "$BUNDLES/daemon/$z" ] || continue A=$(shasum -a 256 "$L" | cut -d' ' -f1); B=$(shasum -a 256 "$BUNDLES/daemon/$z" | cut -d' ' -f1) if [ "$A" = "$B" ]; then echo " ✅ $z 一致" else echo " 🔴 $z 漂移(本地重建過沒推?)"; DRIFT=1; fi done echo "④ manifest 內 sha vs 檔案實體" python3 - "$BUNDLES" <<'PY' import json,sys,hashlib,os b=sys.argv[1]; m=json.load(open(os.path.join(b,'manifest.json'))); bad=0 for c in m['core']: p=os.path.join(b,c['main_file']) real=hashlib.sha256(open(p,'rb').read()).hexdigest() if real!=c['sha256']: print(f" 🔴 {c['name']}: manifest sha ≠ 檔案實體"); bad=1 print(" ✅ 27 件 manifest sha 全對" if not bad else "", end="\n" if not bad else "") sys.exit(bad) PY [ $? -ne 0 ] && DRIFT=1 [ $DRIFT -eq 0 ] && echo "✅ 無漂移" || echo "🔴 有漂移——照上面指示重建後推鏡像(arm)+釘 commit+部署安裝器" exit $DRIFT