69e0a459c3
CI / test (pull_request) Successful in 14s
CI / review (gpt-4.1, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 23s
CI / review (gpt-5, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 58s
CI / review (gpt-5, security, SECURITY_REVIEW.md, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m35s
Sentinel-based cleanup: - Reviews embed <!-- review-bot:NAME --> in body (hidden HTML comment) - Cleanup matches by sentinel, not token identity - Each reviewer-name is a logical identity (sonnet, gpt, security) - Same token can run multiple review types without conflict - No extra API scopes needed System prompt file (--system-prompt-file / SYSTEM_PROMPT_FILE): - Loads a local file with additional review instructions - Appended to system base as "Additional Review Instructions" - Enables specialized reviews (security, performance, etc.) - Partially addresses #5 Security review: - SECURITY_REVIEW.md prompt focused on vulnerabilities - 3rd CI matrix entry using same token, different prompt - Focus: injection, auth, secrets, input validation, crypto, races CI changes: - REVIEWER_NAME passed from matrix.name - SYSTEM_PROMPT_FILE passed from matrix (empty for standard reviews) - 3 reviewers: sonnet (general), gpt (general), security (focused)
51 lines
1.3 KiB
Go
51 lines
1.3 KiB
Go
package review
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// FormatMarkdown formats a ReviewResult into the markdown body for a Gitea review.
|
|
func FormatMarkdown(result *ReviewResult, reviewerName string) string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("## Summary\n\n")
|
|
sb.WriteString(result.Summary)
|
|
sb.WriteString("\n\n")
|
|
|
|
if len(result.Findings) > 0 {
|
|
sb.WriteString("## Findings\n\n")
|
|
sb.WriteString("| # | Severity | File | Line | Finding |\n")
|
|
sb.WriteString("|---|----------|------|------|--------|\n")
|
|
|
|
for i, f := range result.Findings {
|
|
sb.WriteString(fmt.Sprintf("| %d | [%s] | `%s` | %d | %s |\n",
|
|
i+1, f.Severity, f.File, f.Line, f.Finding))
|
|
}
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
sb.WriteString("## Recommendation\n\n")
|
|
sb.WriteString(fmt.Sprintf("**%s** — %s\n", result.Verdict, result.Recommendation))
|
|
|
|
if reviewerName != "" {
|
|
sb.WriteString(fmt.Sprintf("\n---\n*Review by %s*\n", reviewerName))
|
|
// Hidden sentinel for identifying this bot's reviews during cleanup
|
|
sb.WriteString(fmt.Sprintf("\n<!-- review-bot:%s -->\n", reviewerName))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// GiteaEvent converts the verdict to the Gitea API event string.
|
|
func GiteaEvent(verdict string) string {
|
|
switch verdict {
|
|
case "APPROVE":
|
|
return "APPROVED"
|
|
case "REQUEST_CHANGES":
|
|
return "REQUEST_CHANGES"
|
|
default:
|
|
return "COMMENT"
|
|
}
|
|
}
|