#!/usr/bin/env bash # build_all.sh —— 一键将 4 部分最新版本编译打包为安装目录 + 部署脚本。 # 流水线:编译 SDK+3 agent → 双审计 → 冒烟 → (定版后:KB 加密打包)→ 组装 → SHA256SUMS → 泄露门禁 # 用法:bash tooling/build_all.sh [版本] (版本默认读 tooling.env VERSION) set -euo pipefail umask 027 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" . "$SCRIPT_DIR/tooling.env" VERSION="${1:-$VERSION}" BUILD_ROOT="$SCRIPT_DIR/build/$VERSION" RELEASE_ROOT="$BUILD_ROOT/install" echo "== build_all.sh $VERSION ==" echo "工程根: $PROJECT_ROOT" mkdir -p "$BUILD_ROOT" # ---- 1. 编译四部分 ---- echo "[1/6] 编译 SDK + 3 agent" "$SDK_PYTHON" -c 'import Cython' || { echo "SDK_PYTHON 缺 Cython" >&2; exit 1; } "$QA_PYTHON" -c 'import Cython' || { echo "QA_PYTHON 缺 Cython" >&2; exit 1; } "$LEGACY_PYTHON" -c 'import Cython' || { echo "LEGACY_PYTHON 缺 Cython" >&2; exit 1; } rm -rf "$BUILD_ROOT/compiled" mkdir -p "$BUILD_ROOT/compiled" bash "$SCRIPT_DIR/compile/compile_sdk.sh" \ "$PROJECT_ROOT/sdk" "$BUILD_ROOT/compiled/sdk" "$SDK_PYTHON" bash "$SCRIPT_DIR/compile/compile_qa.sh" \ "$PROJECT_ROOT/agent/qa" "$BUILD_ROOT/compiled/qa" "$QA_PYTHON" bash "$SCRIPT_DIR/compile/compile_methodology.sh" \ "$PROJECT_ROOT/agent/methodology" "$BUILD_ROOT/compiled/methodology" "$METHODOLOGY_PYTHON" bash "$SCRIPT_DIR/compile/compile_legacy.sh" \ "$PROJECT_ROOT/agent/legacy-rag" "$BUILD_ROOT/compiled/legacy-rag" "$LEGACY_PYTHON" # ---- 2. 双审计(各树用编译时的 Python——ABI 后缀必须匹配) ---- echo "[2/6] 编译树审计" AUDIT="$SCRIPT_DIR/compile/tools/audit_compiled_tree.py" declare -A TREE_PY=( [sdk]="$SDK_PYTHON" [qa]="$QA_PYTHON" [methodology]="$METHODOLOGY_PYTHON" [legacy-rag]="$LEGACY_PYTHON" ) for tree in sdk qa methodology legacy-rag; do py="${TREE_PY[$tree]}" "$py" "$AUDIT" --tree "$BUILD_ROOT/compiled/$tree" --project "$tree" --write-marker \ | python3 -c "import json,sys; r=json.load(sys.stdin); print(f' $tree: ok={r[\"ok\"]} exts={r[\"extension_count\"]}'); sys.exit(0 if r['ok'] else 1)" \ || { echo "$tree 审计失败" >&2; exit 2; } "$py" "$AUDIT" --tree "$BUILD_ROOT/compiled/$tree" --project "$tree" --require-marker \ >/dev/null || { echo "$tree marker 校验失败" >&2; exit 2; } done # ---- 3. 冒烟(起服务探 /health,全 .so import 检查) ---- echo "[3/6] 冒烟(import 检查,服务起停由部署脚本承担)" for tree in sdk qa methodology legacy-rag; do py="$SDK_PYTHON"; [ "$tree" = "qa" ] && py="$QA_PYTHON"; [ "$tree" = "methodology" ] && py="$METHODOLOGY_PYTHON"; [ "$tree" = "legacy-rag" ] && py="$LEGACY_RUN_PYTHON" if [ "$tree" = "sdk" ]; then ( cd "$BUILD_ROOT/compiled/sdk" && PYTHONPATH=. "$py" -c "import app.main" ) elif [ "$tree" = "qa" ]; then ( cd "$BUILD_ROOT/compiled/qa" && PYTHONPATH=.:./src "$py" -c "import scripts.qa_v1_1_73_server" ) elif [ "$tree" = "methodology" ]; then ( cd "$BUILD_ROOT/compiled/methodology" && PYTHONPATH=. "$py" -c "import scripts.methodology_server" ) else ( cd "$BUILD_ROOT/compiled/legacy-rag" && PYTHONPATH=. "$py" -c "import api.app" ) fi echo " $tree import OK" done # ---- 4. KB(定版后 SKIP_KB=0 启用) ---- if [ "${SKIP_KB:-1}" = "0" ]; then echo "[4/6] KB 加密打包(定版后)" KB_KEY_FILE="${KB_KEY_FILE:-$BUILD_ROOT/kb.key}" [ -f "$KB_KEY_FILE" ] || { echo "缺 KB 密钥: $KB_KEY_FILE" >&2; exit 1; } KB_KEY_FILE="$KB_KEY_FILE" bash "$SCRIPT_DIR/kb/encrypt_kb.sh" "$VERSION" else echo "[4/6] KB 跳过(SKIP_KB=1,数据库未定版;操作见 tooling/KB_ENCRYPT_GUIDE.md)" fi # ---- 5. 组装安装目录 ---- echo "[5/6] 组装安装目录" rm -rf "$RELEASE_ROOT" mkdir -p "$RELEASE_ROOT" cp -r "$BUILD_ROOT/compiled/sdk" "$RELEASE_ROOT/sdk" cp -r "$BUILD_ROOT/compiled/qa" "$RELEASE_ROOT/qa" cp -r "$BUILD_ROOT/compiled/methodology" "$RELEASE_ROOT/methodology" cp -r "$BUILD_ROOT/compiled/legacy-rag" "$RELEASE_ROOT/legacy-rag" # UI 产物 ( cd "$PROJECT_ROOT/ui" && npm run build --silent ) || { echo "ui build 失败" >&2; exit 5; } mkdir -p "$RELEASE_ROOT/ui" cp -r "$PROJECT_ROOT/ui/dist" "$RELEASE_ROOT/ui/dist" # 启动脚本模板 cp "$PROJECT_ROOT/agent/qa/scripts/"*.sh "$RELEASE_ROOT/qa/scripts/" 2>/dev/null || true cp "$PROJECT_ROOT/agent/methodology/scripts/"*.sh "$RELEASE_ROOT/methodology/scripts/" 2>/dev/null || true cp "$PROJECT_ROOT/agent/legacy-rag/scripts/"*.sh "$RELEASE_ROOT/legacy-rag/scripts/" 2>/dev/null || true # env 模板 for t in qa methodology legacy-rag; do cp "$PROJECT_ROOT/agent/$t/env/prod.env.example" "$RELEASE_ROOT/$t/env/prod.env.example" 2>/dev/null || true done cp "$PROJECT_ROOT/sdk/.env.example" "$RELEASE_ROOT/sdk/.env.example" 2>/dev/null || true # KB 包(定版后) [ -f "$BUILD_ROOT/knowledge-$VERSION.tar" ] && cp "$BUILD_ROOT/knowledge-$VERSION.tar"* "$RELEASE_ROOT/" # 部署脚本 cat > "$RELEASE_ROOT/start_all.sh" <<'DEPLOY' #!/usr/bin/env bash # start_all.sh —— 安装目录一键启动(端口 8090 段) # 用法:bash start_all.sh [bind] (bind 默认 0.0.0.0) set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BIND="${1:-0.0.0.0}" echo "== 启动 PlantAI-rag-new 四服务 ==" bash "$ROOT/qa/scripts/start.sh" 8091 || true bash "$ROOT/methodology/scripts/start.sh" 8092 || true bash "$ROOT/legacy-rag/scripts/start.sh" 8093 app || true sleep 3 ( cd "$ROOT/sdk" && setsid python -m uvicorn app.main:app --host "$BIND" --port 8090 \ > /tmp/plantai-sdk.log 2>&1 < /dev/null & ) echo "BFF 启动中(8090),5 秒后自检:" sleep 5 curl -s --max-time 15 http://127.0.0.1:8090/api/health | head -c 300 echo DEPLOY chmod +x "$RELEASE_ROOT/start_all.sh" # ---- 6. SHA256SUMS + 泄露门禁 ---- echo "[6/6] SHA256SUMS + 泄露检查" ( cd "$RELEASE_ROOT" && find . -type f -exec sha256sum {} \; | sort -k2 > SHA256SUMS ) LEAKS=$(grep -rnE "BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|sk-[a-zA-Z0-9]{20}|kb\.key" "$RELEASE_ROOT" 2>/dev/null | grep -v SHA256SUMS | head -5 || true) if [ -n "$LEAKS" ]; then echo "泄露门禁失败:" >&2; echo "$LEAKS" >&2; exit 6 fi echo "泄露检查: 净" echo echo "== 构建完成: $RELEASE_ROOT ==" du -sh "$RELEASE_ROOT" echo "下一步:空目录全新安装验收(备用端口 8094/8095/8096/8097)"