Files

211 lines
6.9 KiB
Bash
Raw Permalink Normal View History

2026-05-29 17:33:12 +08:00
#!/usr/bin/env bash
# ============================================================
# seed-v2.sh — 起 standalone gateway → 跑 seed-v2 → 验落盘 → 停
#
# 这是给"想完整自动化跑一次"的用户准备的便利 wrapper。
# 不想自动起停 gateway 的话,直接 `npm run seed-v2 -- --input <file>` 即可。
#
# 用法:
# ./seed-v2.sh # 默认用 fixtures/minimal.json
# ./seed-v2.sh path/to/fixture.json # 用指定 fixture
# ./seed-v2.sh --keep # 跑完不停 gateway(调试)
# ./seed-v2.sh --no-start # 假设 gateway 已在跑(不 reset、不 start、不 stop
# ./seed-v2.sh --no-reset # 不清空数据目录(增量灌)
#
# 透传给 seed-v2.ts 的环境变量:
# SEED_ENDPOINT / SEED_API_KEY / SEED_SERVICE_ID
# SEED_EVERY_N / SEED_POLL_MS / SEED_STABLE_ROUNDS / SEED_MAX_WAIT_MS
# SEED_VERBOSE 1=详细 / 0=安静
#
# 退出码:
# 0 成功 + 落盘断言全部通过
# 1 seed 或断言失败
# 2 前置条件不满足
# ============================================================
set -euo pipefail
# Compute our own dir BEFORE sourcing env.sh (which overwrites SCRIPT_DIR).
SEED_V2_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLUGIN_ROOT="$(cd "$SEED_V2_DIR/../.." && pwd)"
STANDALONE_HELPERS="$PLUGIN_ROOT/__tests__/standalone"
# 复用 standalone 测试套件里的 env / start / stop 工具(保持一份真理之源,
# 不在这里重复实现 gateway 起停)
if [ ! -f "$STANDALONE_HELPERS/env.sh" ]; then
echo "✗ standalone helpers not found at: $STANDALONE_HELPERS" >&2
echo " this wrapper depends on env.sh / start.sh / stop.sh from there." >&2
exit 2
fi
# shellcheck disable=SC1091
source "$STANDALONE_HELPERS/env.sh"
# ============================================================
# 参数
# ============================================================
KEEP_RUNNING=0
NO_START=0
DO_RESET=1
FIXTURE="$SEED_V2_DIR/fixtures/minimal.json"
while [ $# -gt 0 ]; do
case "$1" in
--keep) KEEP_RUNNING=1 ;;
--no-start) NO_START=1; KEEP_RUNNING=1; DO_RESET=0 ;;
--no-reset) DO_RESET=0 ;;
-h|--help)
sed -n '2,25p' "$0" | sed 's/^# \?//'
exit 0
;;
-*)
log_err "unknown flag: $1"
exit 2
;;
*)
FIXTURE="$1"
;;
esac
shift
done
[ -f "$FIXTURE" ] || { log_err "fixture not found: $FIXTURE"; exit 2; }
# ============================================================
# Reset 数据目录(必须在 start.sh 之前)
# ============================================================
if [ "$DO_RESET" -eq 1 ]; then
if echo "$STANDALONE_DATA_DIR" | grep -q "/\.codebuddy/"; then
log_info "resetting data dir: $STANDALONE_DATA_DIR"
rm -rf "$STANDALONE_DATA_DIR"
else
log_err "refuse to reset $STANDALONE_DATA_DIR (must be under .codebuddy/)"
exit 2
fi
fi
# ============================================================
# 起 gateway
# ============================================================
if [ "$NO_START" -eq 0 ]; then
log_info "starting gateway..."
if ! "$STANDALONE_HELPERS/start.sh"; then
log_err "start.sh failed"
exit 2
fi
fi
if ! gateway_alive; then
log_err "gateway not reachable at $STANDALONE_BASE"
exit 2
fi
# ============================================================
# trap:保证不论结果如何,都按 KEEP_RUNNING 决定要不要停
# ============================================================
cleanup() {
local rc=$?
if [ "$KEEP_RUNNING" -eq 0 ]; then
log_info "stopping gateway"
"$STANDALONE_HELPERS/stop.sh" >/dev/null 2>&1 || true
else
log_info "gateway kept at $STANDALONE_BASE (use $STANDALONE_HELPERS/stop.sh to stop)"
fi
exit $rc
}
trap cleanup EXIT INT TERM
# ============================================================
# 跑 seed-v2(通过 npm bin
# ============================================================
log_info "running seed-v2 with fixture: $FIXTURE"
echo
cd "$PLUGIN_ROOT"
SEED_ENDPOINT="${SEED_ENDPOINT:-$STANDALONE_BASE}" \
SEED_API_KEY="${SEED_API_KEY:-standalone-e2e}" \
SEED_SERVICE_ID="${SEED_SERVICE_ID:-default}" \
SEED_EVERY_N="${SEED_EVERY_N:-5}" \
SEED_POLL_MS="${SEED_POLL_MS:-500}" \
SEED_STABLE_ROUNDS="${SEED_STABLE_ROUNDS:-2}" \
SEED_MAX_WAIT_MS="${SEED_MAX_WAIT_MS:-600000}" \
SEED_VERBOSE="${SEED_VERBOSE:-1}" \
node "$PLUGIN_ROOT/bin/seed-v2.mjs" --input "$FIXTURE"
SEED_RC=$?
# ============================================================
# 落盘断言
# ============================================================
echo
log_info "=== Post-seed assertions ==="
ASSERT_PASS=0
ASSERT_FAIL=0
assert() {
local name="$1"
local cond="$2"
if eval "$cond" >/dev/null 2>&1; then
log_ok "$name"
ASSERT_PASS=$((ASSERT_PASS+1))
else
log_err "$name"
ASSERT_FAIL=$((ASSERT_FAIL+1))
fi
}
# 1. L0: JSONL 镜像(standalone 特有)
TODAY="$(date +%Y-%m-%d)"
JSONL_PATH="$STANDALONE_DATA_DIR/conversations/$TODAY.jsonl"
assert "L0: JSONL mirror exists" "[ -f \"$JSONL_PATH\" ]"
if [ -f "$JSONL_PATH" ]; then
JSONL_LINES=$(wc -l < "$JSONL_PATH")
log_info " JSONL line count: $JSONL_LINES"
fi
# 2. L0: SQLite (vectors.db)
DB_PATH="$STANDALONE_DATA_DIR/vectors.db"
assert "L0: SQLite vectors.db exists" "[ -f \"$DB_PATH\" ]"
SQLITE_BIN="$(command -v sqlite3 || true)"
if [ -z "$SQLITE_BIN" ] && [ -x "$HOME/miniconda3/bin/sqlite3" ]; then
SQLITE_BIN="$HOME/miniconda3/bin/sqlite3"
fi
if [ -f "$DB_PATH" ] && [ -n "$SQLITE_BIN" ]; then
L0_COUNT="$("$SQLITE_BIN" "$DB_PATH" "SELECT COUNT(*) FROM l0_conversations" 2>/dev/null || echo 0)"
log_info " SQLite l0_conversations count: $L0_COUNT"
assert "L0: SQLite has records" "[ \"$L0_COUNT\" -gt 0 ]"
else
log_info "L0: sqlite3 binary not found, skipping SQLite assertion"
fi
# 3. L1: records JSONL — 仅当 LLM key 配置时会有产出
RECORDS_DIR="$STANDALONE_DATA_DIR/records"
if [ -d "$RECORDS_DIR" ]; then
RECORD_FILES="$(find "$RECORDS_DIR" -name "*.jsonl" -size +0 2>/dev/null | wc -l)"
if [ "$RECORD_FILES" -gt 0 ]; then
log_ok "L1: records/ has $RECORD_FILES non-empty jsonl file(s) [LLM key configured]"
ASSERT_PASS=$((ASSERT_PASS+1))
else
log_info "L1: records/ exists but empty (LLM key likely not configured — skipped, not a failure)"
fi
else
log_info "L1: records/ not created (LLM key likely not configured — skipped, not a failure)"
fi
# 4. Pipeline status: busy=false at the end
STATUS_RESP="$(curl -sS -X POST "$STANDALONE_BASE/v2/pipeline/status" \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer standalone-e2e' \
-H 'x-tdai-service-id: default' \
-d '{}')"
assert "Pipeline status: busy=false at completion" \
"echo '$STATUS_RESP' | python3 -c 'import sys,json; assert json.loads(sys.stdin.read())[\"data\"][\"busy\"] is False'"
# 汇总
echo
if [ "$ASSERT_FAIL" -gt 0 ]; then
log_err "Post-seed assertions: $ASSERT_PASS passed, $ASSERT_FAIL failed"
exit 1
fi
log_ok "Post-seed assertions: $ASSERT_PASS/$ASSERT_PASS passed"
exit $SEED_RC