157 lines
5.2 KiB
Bash
157 lines
5.2 KiB
Bash
|
|
#!/usr/bin/env bash
|
|||
|
|
set -euo pipefail
|
|||
|
|
|
|||
|
|
PROJECT_DIR="/home/long/project/蚊子"
|
|||
|
|
REPORT_DIR="$PROJECT_DIR/logs/prd-review"
|
|||
|
|
RUN_LOG="$PROJECT_DIR/logs/prd_review_cycle.log"
|
|||
|
|
LOCK_FILE="/tmp/mosquito_prd_review_cycle.lock"
|
|||
|
|
PID_FILE="$PROJECT_DIR/logs/prd-review/cycle.pid"
|
|||
|
|
FORCE_RERUN="${FORCE_RERUN:-0}"
|
|||
|
|
STALE_SECONDS="${STALE_SECONDS:-10800}"
|
|||
|
|
CLAUDE_BIN="$HOME/.cursor/extensions/anthropic.claude-code-2.1.15-linux-x64/resources/native-binary/claude"
|
|||
|
|
EVIDENCE_CHECK_SCRIPT="$PROJECT_DIR/scripts/verify_review_evidence.sh"
|
|||
|
|
|
|||
|
|
# cron环境下PATH通常不完整,显式补齐nvm/codex
|
|||
|
|
export HOME="${HOME:-/home/long}"
|
|||
|
|
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
|||
|
|
export PATH="$HOME/.local/bin:$HOME/bin:/usr/local/bin:/usr/bin:/bin:$PATH"
|
|||
|
|
|
|||
|
|
if ! command -v codex >/dev/null 2>&1; then
|
|||
|
|
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
|||
|
|
# shellcheck disable=SC1090
|
|||
|
|
. "$NVM_DIR/nvm.sh" >/dev/null 2>&1 || true
|
|||
|
|
nvm use --silent 20 >/dev/null 2>&1 || true
|
|||
|
|
nvm use --silent default >/dev/null 2>&1 || true
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
CODEX_BIN="$(command -v codex || true)"
|
|||
|
|
if [ -z "$CODEX_BIN" ] && [ -x "$HOME/.nvm/versions/node/v20.19.0/bin/codex" ]; then
|
|||
|
|
CODEX_BIN="$HOME/.nvm/versions/node/v20.19.0/bin/codex"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
mkdir -p "$REPORT_DIR" "$PROJECT_DIR/logs"
|
|||
|
|
|
|||
|
|
if [ -f "$PID_FILE" ]; then
|
|||
|
|
prev_pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|||
|
|
if [ -n "${prev_pid:-}" ] && kill -0 "$prev_pid" 2>/dev/null; then
|
|||
|
|
now_ts=$(date +%s)
|
|||
|
|
pid_age=$((now_ts - $(stat -c %Y "$PID_FILE" 2>/dev/null || echo "$now_ts")))
|
|||
|
|
if [ "$FORCE_RERUN" = "1" ] || [ "$pid_age" -gt "$STALE_SECONDS" ]; then
|
|||
|
|
echo "[$(date '+%F %T')] force/stale rerun: killing previous pid=$prev_pid age=${pid_age}s" >> "$RUN_LOG"
|
|||
|
|
kill "$prev_pid" 2>/dev/null || true
|
|||
|
|
sleep 2
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
exec 9>"$LOCK_FILE"
|
|||
|
|
if ! flock -n 9; then
|
|||
|
|
echo "[$(date '+%F %T')] skip: previous cycle still running" >> "$RUN_LOG"
|
|||
|
|
exit 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo $$ > "$PID_FILE"
|
|||
|
|
trap 'rm -f "$PID_FILE"' EXIT
|
|||
|
|
|
|||
|
|
cd "$PROJECT_DIR"
|
|||
|
|
|
|||
|
|
TIMESTAMP="$(date '+%Y%m%d_%H%M%S')"
|
|||
|
|
REPORT_FILE="$REPORT_DIR/review_${TIMESTAMP}.md"
|
|||
|
|
LATEST_REPORT="$REPORT_DIR/latest_review.md"
|
|||
|
|
CLAUDE_SUMMARY="$REPORT_DIR/claude_apply_${TIMESTAMP}.md"
|
|||
|
|
|
|||
|
|
PRD_FILES=()
|
|||
|
|
for f in "$PROJECT_DIR/docs/PRD.md" "$PROJECT_DIR/docs/prd"/*.md; do
|
|||
|
|
if [ -f "$f" ]; then
|
|||
|
|
PRD_FILES+=("$f")
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
if [ ${#PRD_FILES[@]} -eq 0 ]; then
|
|||
|
|
echo "[$(date '+%F %T')] error: no PRD files found" >> "$RUN_LOG"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
PRD_LIST=$(printf '%s\n' "${PRD_FILES[@]}")
|
|||
|
|
|
|||
|
|
CODEX_PROMPT=$(cat <<EOF
|
|||
|
|
你是严格的项目评审官。请在仓库 $PROJECT_DIR 执行全面review,并以 PRD 为基准进行差距分析。
|
|||
|
|
|
|||
|
|
PRD文件如下:
|
|||
|
|
$PRD_LIST
|
|||
|
|
|
|||
|
|
输出要求(中文,Markdown):
|
|||
|
|
1. 总体结论(是否满足最新PRD)
|
|||
|
|
2. PRD事项对照矩阵(事项/当前状态/证据文件路径/优先级)
|
|||
|
|
3. 未完成清单(P0/P1/P2)
|
|||
|
|
4. 给Claude可直接执行的优化任务清单(按顺序,包含具体文件与验收标准)
|
|||
|
|
5. 建议执行命令(构建、测试、e2e)
|
|||
|
|
|
|||
|
|
请务必面向“可执行落地”,不要空泛建议。
|
|||
|
|
EOF
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if [ -z "$CODEX_BIN" ]; then
|
|||
|
|
echo "[$(date '+%F %T')] error: codex binary not found (PATH=$PATH)" >> "$RUN_LOG"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "[$(date '+%F %T')] cycle start: generating codex report with $CODEX_BIN" >> "$RUN_LOG"
|
|||
|
|
"$CODEX_BIN" exec \
|
|||
|
|
--cd "$PROJECT_DIR" \
|
|||
|
|
--dangerously-bypass-approvals-and-sandbox \
|
|||
|
|
--output-last-message "$REPORT_FILE" \
|
|||
|
|
"$CODEX_PROMPT" >> "$RUN_LOG" 2>&1
|
|||
|
|
|
|||
|
|
if [ ! -s "$REPORT_FILE" ]; then
|
|||
|
|
echo "[$(date '+%F %T')] error: codex report is empty: $REPORT_FILE" >> "$RUN_LOG"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
cp -f "$REPORT_FILE" "$LATEST_REPORT"
|
|||
|
|
|
|||
|
|
echo "[$(date '+%F %T')] codex report ready: $REPORT_FILE" >> "$RUN_LOG"
|
|||
|
|
|
|||
|
|
CLAUDE_PROMPT=$(cat <<EOF
|
|||
|
|
请读取并执行该评审报告中的优化任务:
|
|||
|
|
$REPORT_FILE
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
1) 按优先级执行修复,优先完成P0/P1
|
|||
|
|
2) 可以直接修改代码并运行必要命令(含后端测试、前端测试、端到端测试)
|
|||
|
|
3) 若修复后仍有失败,继续迭代直到通过或给出明确阻塞原因
|
|||
|
|
4) 输出最终摘要:执行命令、修改文件、测试结果、剩余未完成PRD项
|
|||
|
|
5) 必须输出“证据矩阵”,每条包含:命令 | 退出码 | 原始日志路径 | 结果结论
|
|||
|
|
6) 所有结论都要附可追溯日志路径(如 logs/*.log、target/surefire-reports/*.xml)
|
|||
|
|
|
|||
|
|
若无法提供可追溯证据,必须明确写“证据不足-本轮无效”。
|
|||
|
|
请直接开始执行,不要只给计划。
|
|||
|
|
EOF
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
echo "[$(date '+%F %T')] cycle continue: running claude apply" >> "$RUN_LOG"
|
|||
|
|
"$CLAUDE_BIN" -p \
|
|||
|
|
--permission-mode dontAsk \
|
|||
|
|
--dangerously-skip-permissions \
|
|||
|
|
"$CLAUDE_PROMPT" > "$CLAUDE_SUMMARY" 2>> "$RUN_LOG"
|
|||
|
|
|
|||
|
|
if [ ! -s "$CLAUDE_SUMMARY" ]; then
|
|||
|
|
echo "[$(date '+%F %T')] error: claude summary is empty: $CLAUDE_SUMMARY" >> "$RUN_LOG"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [ -x "$EVIDENCE_CHECK_SCRIPT" ]; then
|
|||
|
|
if "$EVIDENCE_CHECK_SCRIPT" "$CLAUDE_SUMMARY" "$PROJECT_DIR" >> "$RUN_LOG" 2>&1; then
|
|||
|
|
echo "[$(date '+%F %T')] evidence gate pass" >> "$RUN_LOG"
|
|||
|
|
else
|
|||
|
|
echo "[$(date '+%F %T')] error: evidence gate failed, mark cycle invalid" >> "$RUN_LOG"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
else
|
|||
|
|
echo "[$(date '+%F %T')] warn: evidence checker missing: $EVIDENCE_CHECK_SCRIPT" >> "$RUN_LOG"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "[$(date '+%F %T')] cycle done: $CLAUDE_SUMMARY" >> "$RUN_LOG"
|