update-readme #4

Merged
armistace merged 2 commits from update-readme into master 2026-05-21 21:17:18 +10:00
Showing only changes of commit bc7ddd29d4 - Show all commits

View File

@ -3,7 +3,7 @@ import os
import hmac import hmac
import hashlib import hashlib
import base64 import base64
from fastapi import FastAPI, HTTPException, Request from fastapi import FastAPI, HTTPException, Request, BackgroundTasks
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import uvicorn import uvicorn
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
@ -89,8 +89,58 @@ def post_pr_comment(repo_full: str, pr_number: int, comment: str) -> None:
logger.info(f"Posted review comment to PR #{pr_number} in {repo_full}") logger.info(f"Posted review comment to PR #{pr_number} in {repo_full}")
def _run_review_background(repo_full: str, pr_number: int, pr_title: str,
pr_description: str, repo_url: str, branch: str,
base_branch: str, files: List[Dict[str, Any]]) -> None:
converted_files = []
for f in files:
converted_files.append(FileInfo(
path=f["filename"],
content=f.get("content"),
status=f.get("status", "modified"),
additions=f.get("additions", 0),
deletions=f.get("deletions", 0),
patch=f.get("patch"),
))
flow_inputs = {
"pr_id": str(pr_number),
"pr_title": pr_title,
"pr_description": pr_description,
"pr_url": f"{repo_url}/pull/{pr_number}",
"repo_name": repo_full,
"repo_url": repo_url,
"branch": branch,
"base_branch": base_branch,
"files": [f.dict() for f in converted_files],
"context_overrides": None,
}
flow = CodeReviewFlow()
try:
flow_result = flow.kickoff(inputs=flow_inputs)
except Exception as e:
logger.error(f"Background review failed for PR #{pr_number}: {e}")
try:
post_pr_comment(repo_full, pr_number, f"**PR Review failed:** {e}")
except Exception:
pass
return
if flow_result.get("error"):
logger.error(f"PR review failed for PR #{pr_number}: {flow_result['error']}")
try:
summary = flow_result.get("review_summary", "")
if summary:
comment = f"## PR Review Results\n\n{summary}"
post_pr_comment(repo_full, pr_number, comment)
except Exception as e:
logger.warning(f"Failed to post review comment: {e}")
@app.post("/api/v1/gitea-webhook") @app.post("/api/v1/gitea-webhook")
async def gitea_webhook(request: Request) -> Dict[str, Any]: async def gitea_webhook(request: Request, background_tasks: BackgroundTasks) -> Dict[str, Any]:
body = await request.body() body = await request.body()
sig = request.headers.get("X-Gitea-Signature", "") sig = request.headers.get("X-Gitea-Signature", "")
if not verify_signature(body, sig): if not verify_signature(body, sig):
@ -107,7 +157,7 @@ async def gitea_webhook(request: Request) -> Dict[str, Any]:
repo_full = repo["full_name"] repo_full = repo["full_name"]
repo_url = repo.get("html_url", f"{ACCESS_GITEA_URL}/{repo_full}") repo_url = repo.get("html_url", f"{ACCESS_GITEA_URL}/{repo_full}")
if action not in ("opened", "synchronize", "reopened"): if action not in ("opened", "synchronized", "reopened"):
logger.info(f"Ignoring PR action: {action}") logger.info(f"Ignoring PR action: {action}")
return {"status": "ignored", "reason": f"action '{action}' not processed"} return {"status": "ignored", "reason": f"action '{action}' not processed"}
@ -119,62 +169,24 @@ async def gitea_webhook(request: Request) -> Dict[str, Any]:
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"Error fetching PR files: {e}") raise HTTPException(status_code=500, detail=f"Error fetching PR files: {e}")
converted_files = []
for f in files:
converted_files.append(FileInfo(
path=f["filename"],
content=f.get("content"),
status=f.get("status", "modified"),
additions=f.get("additions", 0),
deletions=f.get("deletions", 0),
patch=f.get("patch"),
))
flow_inputs = {
"pr_id": str(pr_number),
"pr_title": pr["title"],
"pr_description": pr.get("body", ""),
"pr_url": f"{repo_url}/pull/{pr_number}",
"repo_name": repo_full,
"repo_url": repo_url,
"branch": pr["head"]["label"],
"base_branch": pr["base"]["label"],
"files": [f.dict() for f in converted_files],
"context_overrides": None,
}
flow = CodeReviewFlow()
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as pool:
try: try:
flow_result = await asyncio.wait_for( post_pr_comment(repo_full, pr_number, "PR received — starting review, sit tight :saluting_face:")
loop.run_in_executor(pool, lambda: flow.kickoff(inputs=flow_inputs)),
timeout=TOTAL_FLOW_TIMEOUT,
)
except asyncio.TimeoutError:
logger.error(f"PR review timed out for PR #{pr_number}")
raise HTTPException(
status_code=504,
detail=f"PR review timed out after {TOTAL_FLOW_TIMEOUT} seconds",
)
if flow_result.get("error"):
logger.error(f"PR review failed for PR #{pr_number}: {flow_result['error']}")
try:
summary = flow_result.get("review_summary", "")
if summary:
comment = f"## PR Review Results\n\n{summary}"
post_pr_comment(repo_full, pr_number, comment)
except Exception as e: except Exception as e:
logger.warning(f"Failed to post review comment: {e}") logger.warning(f"Failed to post initial comment: {e}")
return { background_tasks.add_task(
"status": "completed" if not flow_result.get("error") else "failed", _run_review_background,
"pr_number": pr_number, repo_full=repo_full,
"review_summary": flow_result.get("review_summary"), pr_number=pr_number,
"error": flow_result.get("error"), pr_title=pr["title"],
} pr_description=pr.get("body", ""),
repo_url=repo_url,
branch=pr["head"]["label"],
base_branch=pr["base"]["label"],
files=files,
)
return {"status": "accepted", "pr_number": pr_number}
return {"status": "ignored"} return {"status": "ignored"}