85 lines
2.8 KiB
Go
85 lines
2.8 KiB
Go
package pierre
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"log/slog"
|
||
|
||
"git.schreifuchs.ch/schreifuchs/pierre-bot/internal/chatter"
|
||
)
|
||
|
||
func (s *Service) MakeReview(ctx context.Context, organisation string, repo string, prID int) error {
|
||
// Fetch Diff using positional args from shared RepoArgs
|
||
diff, err := s.git.GetDiff(ctx, organisation, repo, prID)
|
||
defer diff.Close()
|
||
if err != nil {
|
||
return fmt.Errorf("error fetching diff: %w", err)
|
||
}
|
||
|
||
// Run Logic
|
||
comments, err := s.judgePR(ctx, diff)
|
||
if err != nil {
|
||
return fmt.Errorf("error judging PR: %w", err)
|
||
}
|
||
|
||
// ---------- Sanity‑check step ----------
|
||
headSHA, err := s.git.GetPRHeadSHA(ctx, organisation, repo, prID)
|
||
if err != nil {
|
||
slog.Warn("could not fetch PR head SHA", "error", err)
|
||
} else if !s.skipSanityCheck {
|
||
filtered := []Comment{}
|
||
for _, c := range comments {
|
||
// Retrieve full file content at the PR head
|
||
fileContent, fErr := s.git.GetFileContent(ctx, organisation, repo, c.File, headSHA)
|
||
if fErr != nil {
|
||
slog.Warn("failed to fetch file", "path", c.File, "error", fErr)
|
||
filtered = append(filtered, c)
|
||
continue
|
||
}
|
||
|
||
// Build a simple sanity‑check prompt
|
||
systemPrompt := `You are a senior software architect. Given the full source code of a file and a review comment that refers to it, decide whether the comment is useful. Return JSON with fields "useful" (bool) and "reason" (short explanation, ≤2 sentences).`
|
||
userPrompt := fmt.Sprintf("File content:\n%s\n\nComment:\n%s", fileContent, c.Message)
|
||
|
||
type sanityResult struct {
|
||
Useful bool `json:"useful"`
|
||
Reason string `json:"reason"`
|
||
}
|
||
var res sanityResult
|
||
if err := s.chat.GenerateStructured(ctx, []chatter.Message{{Role: chatter.RoleSystem, Content: systemPrompt}, {Role: chatter.RoleUser, Content: userPrompt}}, &res); err != nil {
|
||
slog.Error("sanity check error", "file", c.File, "line", c.Line, "error", err)
|
||
filtered = append(filtered, c)
|
||
continue
|
||
}
|
||
if res.Useful {
|
||
// Optionally annotate the comment with the reason for debugging
|
||
c.Message = fmt.Sprintf("%s (Reason: %s)", c.Message, res.Reason)
|
||
filtered = append(filtered, c)
|
||
} else {
|
||
slog.Info("comment discarded", "file", c.File, "line", c.Line, "reason", res.Reason)
|
||
}
|
||
}
|
||
comments = filtered
|
||
}
|
||
|
||
fmt.Printf("Analysis complete. Found %d issues.\n---\n", len(comments))
|
||
|
||
model := s.chat.GetProviderName()
|
||
|
||
for _, c := range comments {
|
||
c.Message = fmt.Sprintf("%s (Generated by: %s)", c.Message, model)
|
||
|
||
// Normal mode: print to stdout and post the comment to the VCS.
|
||
fmt.Printf("File: %s\nLine: %d\nMessage: %s\n%s\n",
|
||
c.File, c.Line, c.Message, "---")
|
||
|
||
if !s.disableComments {
|
||
if err := s.git.AddComment(ctx, organisation, repo, prID, c); err != nil {
|
||
slog.Error("failed to add comment", "error", err)
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|