feat. rebuild service in Go
Rebuild the Gitea Codex review bot from the product contract with a Go HTTP service, durable SQL queue, typed Gitea client, isolated runner, and deployment updates. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JobStatus string
|
||||
|
||||
const (
|
||||
JobQueued JobStatus = "queued"
|
||||
JobRunning JobStatus = "running"
|
||||
JobSucceeded JobStatus = "succeeded"
|
||||
JobFailed JobStatus = "failed"
|
||||
JobSkipped JobStatus = "skipped"
|
||||
)
|
||||
|
||||
type RunStatus string
|
||||
|
||||
const (
|
||||
RunRunning RunStatus = "running"
|
||||
RunSucceeded RunStatus = "succeeded"
|
||||
RunFailed RunStatus = "failed"
|
||||
RunSkipped RunStatus = "skipped"
|
||||
)
|
||||
|
||||
type ParsedCommand struct {
|
||||
Name string
|
||||
Alias string
|
||||
Raw string
|
||||
Mode string
|
||||
ModeExplicit bool
|
||||
Full bool
|
||||
Arguments []string
|
||||
}
|
||||
|
||||
func (c ParsedCommand) IsReview() bool { return c.Name == "review" || c.Name == "rerun" }
|
||||
|
||||
type RepoReviewConfig struct {
|
||||
Configured bool
|
||||
Enabled bool
|
||||
DefaultMode string
|
||||
MaxDiffBytes int
|
||||
IncludeTests bool
|
||||
Focus []string
|
||||
Ignore []string
|
||||
}
|
||||
|
||||
func DefaultRepoReviewConfig() RepoReviewConfig {
|
||||
return RepoReviewConfig{Configured: true, Enabled: true, DefaultMode: "full", MaxDiffBytes: 200000, Focus: []string{"correctness", "security", "maintainability"}}
|
||||
}
|
||||
|
||||
type PullRequestContext struct {
|
||||
Repo string
|
||||
PRNumber int
|
||||
BaseRef string
|
||||
BaseSHA string
|
||||
HeadRef string
|
||||
HeadSHA string
|
||||
CloneURL string
|
||||
BaseCloneURL string
|
||||
HeadCloneURL string
|
||||
HTMLURL string
|
||||
IsFork bool
|
||||
}
|
||||
|
||||
type Finding struct {
|
||||
Severity string `json:"severity"`
|
||||
File string `json:"file"`
|
||||
LineStart int `json:"line_start"`
|
||||
LineEnd int `json:"line_end"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Suggestion *string `json:"suggestion"`
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
InputTokens int `json:"input_tokens,omitempty"`
|
||||
OutputTokens int `json:"output_tokens,omitempty"`
|
||||
TotalTokens int `json:"total_tokens,omitempty"`
|
||||
}
|
||||
|
||||
type ReviewMeta struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Usage Usage `json:"usage,omitempty"`
|
||||
}
|
||||
|
||||
type ReviewResult struct {
|
||||
Verdict string `json:"verdict"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Summary string `json:"summary"`
|
||||
MarkdownComment string `json:"markdown_comment"`
|
||||
Findings []Finding `json:"findings"`
|
||||
Meta *ReviewMeta `json:"_meta,omitempty"`
|
||||
}
|
||||
|
||||
func (r ReviewResult) Validate() error {
|
||||
if r.Verdict != "correct" && r.Verdict != "has_issues" {
|
||||
return errors.New("invalid verdict")
|
||||
}
|
||||
if r.Confidence < 0 || r.Confidence > 1 {
|
||||
return errors.New("confidence must be between 0 and 1")
|
||||
}
|
||||
if len(r.Summary) > 20000 || len(r.MarkdownComment) > 50000 {
|
||||
return errors.New("review text is too long")
|
||||
}
|
||||
if len(r.Findings) > 200 {
|
||||
return errors.New("too many findings")
|
||||
}
|
||||
for i, f := range r.Findings {
|
||||
if f.Severity != "low" && f.Severity != "medium" && f.Severity != "high" && f.Severity != "critical" {
|
||||
return errors.New("invalid finding severity")
|
||||
}
|
||||
if f.File == "" || len(f.File) > 1000 || f.LineStart < 1 || f.LineEnd < f.LineStart {
|
||||
return errors.New("invalid finding location")
|
||||
}
|
||||
if len(f.Title) > 2000 || len(f.Body) > 20000 {
|
||||
return errors.New("finding text is too long")
|
||||
}
|
||||
if f.Suggestion != nil && len(*f.Suggestion) > 20000 {
|
||||
return errors.New("suggestion is too long")
|
||||
}
|
||||
_ = i
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebhookEvent struct {
|
||||
EventName string
|
||||
DeliveryID string
|
||||
Repo string
|
||||
PRNumber int
|
||||
HeadSHA string
|
||||
CommentID int64
|
||||
CommentBody string
|
||||
Sender string
|
||||
PayloadSHA256 string
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
ID int64
|
||||
Repo string
|
||||
PRNumber int
|
||||
HeadSHA string
|
||||
TriggerCommentID int64
|
||||
TriggerCommentBody string
|
||||
Command string
|
||||
CommandArgs string
|
||||
RequestedBy string
|
||||
Status JobStatus
|
||||
LastError string
|
||||
ResultJSON []byte
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
StartedAt *time.Time
|
||||
FinishedAt *time.Time
|
||||
}
|
||||
|
||||
type ReviewRun struct {
|
||||
ID int64
|
||||
JobID int64
|
||||
Status RunStatus
|
||||
ContainerID string
|
||||
ResultJSON []byte
|
||||
Error string
|
||||
StartedAt time.Time
|
||||
FinishedAt *time.Time
|
||||
}
|
||||
|
||||
func (j Job) AttemptNumber() int { return 1 }
|
||||
|
||||
// ReviewRunner isolates all process/container execution from orchestration.
|
||||
type ReviewRunner interface {
|
||||
Run(context.Context, PullRequestContext, ParsedCommand, RepoReviewConfig) (ReviewResult, error)
|
||||
}
|
||||
|
||||
func NormalizeRepo(repo string) string { return strings.TrimSpace(repo) }
|
||||
Reference in New Issue
Block a user