All checks were successful
CD Pipeline / deploy (push) Successful in 1m21s
架構重建: - 移除 pre-commit hook(本機 commit 不再阻塞) - 改為 CD 健康檢查通過後自動觸發 webhook 新建 services/code_review_pipeline_service.py: 5-Step Pipeline(後台 daemon thread) Step1 system 讀取部署後變更檔案內容 Step2 Hermes 程式碼掃描(bugs/security/perf,hermes3:latest) Step3 OpenClaw 架構品質評估(Gemini 2.5 Flash) Step4 ElephantAlpha 決策協調(severity + auto_fix 裁量) Step5 NemoTron action_plans 寫入 + AiderHeal 觸發 全程 Telegram 告警(啟動/完成/錯誤)+ ai_insights DB 持久化 重建 routes/code_review_routes.py: POST /code-review/api/internal/trigger CD webhook(X-Internal-Token) GET /code-review/api/status 前端即時 polling GET /code-review/api/history 歷史清單 GET /code-review/ 前端儀表板 重建 templates/code_review.html: 深色儀表板,Pipeline 即時進度 + Severity 分佈 + 問題清單 + EA 決策 3s polling(running)/ 30s(idle) .gitea/workflows/cd.yaml: 健康檢查通過後注入「觸發 AI Code Review」step continue-on-error: true(不影響部署結果) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
4.6 KiB
Python
108 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
routes/code_review_routes.py
|
||
Code Review 路由層
|
||
|
||
端點:
|
||
POST /code-review/api/internal/trigger — CD Webhook(部署後觸發)
|
||
GET /code-review/api/status — 前端 polling 即時狀態
|
||
GET /code-review/api/history — 歷史 review 清單
|
||
GET /code-review/ — 前端儀表板
|
||
"""
|
||
|
||
import logging
|
||
from flask import Blueprint, jsonify, render_template, request
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
code_review_bp = Blueprint("code_review", __name__, url_prefix="/code-review")
|
||
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# CD Webhook — 部署成功後由 Gitea Action 呼叫
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
|
||
@code_review_bp.route("/api/internal/trigger", methods=["POST"])
|
||
def trigger_review():
|
||
"""
|
||
接受 Gitea CD pipeline 觸發 Code Review 的 webhook。
|
||
|
||
Request body (JSON):
|
||
commit_sha str 完整 commit SHA
|
||
changed_files list 變更檔案路徑清單
|
||
branch str 分支名稱(預設 main)
|
||
deploy_type str sync | rebuild
|
||
|
||
Header:
|
||
X-Internal-Token 與 INTERNAL_WEBHOOK_TOKEN env 比對
|
||
"""
|
||
try:
|
||
from services.code_review_pipeline_service import (
|
||
trigger_post_deploy_review,
|
||
verify_internal_token,
|
||
)
|
||
|
||
# 驗證 token
|
||
token = request.headers.get("X-Internal-Token", "")
|
||
if not verify_internal_token(token):
|
||
logger.warning("[CodeReview] Webhook token 驗證失敗")
|
||
return jsonify({"ok": False, "error": "Unauthorized"}), 401
|
||
|
||
body = request.get_json(silent=True) or {}
|
||
commit_sha = body.get("commit_sha", "")
|
||
changed_files = body.get("changed_files", [])
|
||
branch = body.get("branch", "main")
|
||
deploy_type = body.get("deploy_type", "sync")
|
||
|
||
if not commit_sha:
|
||
return jsonify({"ok": False, "error": "commit_sha 必填"}), 400
|
||
|
||
pipeline_id = trigger_post_deploy_review(
|
||
commit_sha=commit_sha,
|
||
changed_files=changed_files,
|
||
branch=branch,
|
||
deploy_type=deploy_type,
|
||
)
|
||
|
||
logger.info("[CodeReview] Webhook 觸發成功 pipeline_id=%s", pipeline_id)
|
||
return jsonify({"ok": True, "pipeline_id": pipeline_id})
|
||
|
||
except Exception as e:
|
||
logger.error("[CodeReview] Webhook 處理失敗: %s", e, exc_info=True)
|
||
return jsonify({"ok": False, "error": str(e)}), 500
|
||
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# 前端 API — 即時狀態 / 歷史
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
|
||
@code_review_bp.route("/api/status", methods=["GET"])
|
||
def api_status():
|
||
"""即時 Pipeline 狀態(前端每 3 秒 polling)"""
|
||
try:
|
||
from services.code_review_pipeline_service import get_current_state
|
||
return jsonify(get_current_state())
|
||
except Exception as e:
|
||
return jsonify({"error": str(e)}), 500
|
||
|
||
|
||
@code_review_bp.route("/api/history", methods=["GET"])
|
||
def api_history():
|
||
"""歷史 Code Review 結果清單"""
|
||
try:
|
||
limit = min(int(request.args.get("limit", 20)), 50)
|
||
from services.code_review_pipeline_service import get_history
|
||
return jsonify(get_history(limit=limit))
|
||
except Exception as e:
|
||
return jsonify({"error": str(e)}), 500
|
||
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
# 前端頁面
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
|
||
@code_review_bp.route("/", methods=["GET"])
|
||
def dashboard():
|
||
return render_template("code_review.html")
|