CI: Issue Change Requests 2
All checks were successful
Gemini Writing Review / gemini-review (pull_request) Successful in 56s
Word Count / count-words (pull_request) Successful in 36s

This commit is contained in:
2026-02-28 11:18:31 +01:00
parent 4d6906e5b4
commit c4745d915d

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
@@ -14,6 +15,12 @@ import (
"google.golang.org/api/option"
)
type Suggestion struct {
LineNumber uint64 `json:"line_number"`
Comment string `json:"comment"`
Snippet string `json:"snippet,omitempty"`
}
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
token := os.Getenv("GITEA_TOKEN")
@@ -55,7 +62,7 @@ func main() {
}
defer geminiClient.Close()
model := geminiClient.GenerativeModel("gemini-3-flash-preview")
model := geminiClient.GenerativeModel("gemini-1.5-flash")
// Get PR files
files, _, err := client.ListPullRequestFiles(owner, repo, prNumber, gitea.ListPullRequestFilesOptions{})
@@ -63,7 +70,7 @@ func main() {
log.Fatalf("Failed to get PR files: %v", err)
}
var reviews []string
var giteaComments []gitea.CreatePullReviewComment
for _, file := range files {
if !strings.HasSuffix(file.Filename, ".md") {
continue
@@ -76,25 +83,35 @@ func main() {
continue
}
review, err := getGeminiReview(ctx, model, content)
suggestions, err := getGeminiReview(ctx, model, content)
if err != nil {
fmt.Printf("Error getting review for %s: %v\n", file.Filename, err)
continue
}
reviews = append(reviews, fmt.Sprintf("#### Review for `%s`\n\n%s", file.Filename, review))
for _, s := range suggestions {
body := s.Comment
if s.Snippet != "" {
body += fmt.Sprintf("\n\n```suggestion\n%s\n```", s.Snippet)
}
giteaComments = append(giteaComments, gitea.CreatePullReviewComment{
Path: file.Filename,
Body: body,
NewLineNum: int64(s.LineNumber),
})
}
}
if len(reviews) > 0 {
reviewBody := "### 🤖 Gemini Writing Review\n\n" + strings.Join(reviews, "\n\n---\n\n")
if len(giteaComments) > 0 {
_, _, err = client.CreatePullReview(owner, repo, prNumber, gitea.CreatePullReviewOptions{
State: gitea.ReviewStateRequestChanges,
Body: reviewBody,
State: gitea.ReviewStateRequestChanges,
Body: "### 🤖 Gemini Writing Review\n\nI've found some areas for improvement in the documentation. Please see the inline comments below.",
Comments: giteaComments,
})
if err != nil {
log.Fatalf("Failed to create PR review: %v", err)
}
fmt.Println("Successfully created PR change request review.")
fmt.Printf("Successfully created PR review with %d inline comments.\n", len(giteaComments))
} else {
fmt.Println("No Markdown files to review or no suggestions found.")
}
@@ -114,35 +131,56 @@ func readFile(path string) (string, error) {
return string(content), nil
}
func getGeminiReview(ctx context.Context, model *genai.GenerativeModel, content string) (string, error) {
func getGeminiReview(ctx context.Context, model *genai.GenerativeModel, content string) ([]Suggestion, error) {
prompt := fmt.Sprintf(`
Review the following Markdown content for spelling errors, grammar mistakes, and style improvements.
This review will be posted as a "Request Changes" on a Pull Request, so please be specific and actionable.
Provide your feedback as a list of bullet points. For each point:
1. Identify the issue.
2. Provide the original text snippet.
3. Suggest a clear alternative or fix.
4. Briefly explain why the change is necessary if not obvious.
5. Check the content for completeness and technical correctness.
Analyze the text line by line.
Content:
For each issue found, provide a suggestion in JSON format with:
- "line_number": The 1-indexed line number where the issue occurs.
- "comment": A brief explanation of the problem and the suggested fix.
- "snippet": The corrected text for that line (optional, but highly recommended for spell/grammar fixes).
Return ONLY a JSON array of suggestions. If no issues are found, return "[]".
Content with line numbers for reference:
%s
`, content)
`, addLineNumbers(content))
resp, err := model.GenerateContent(ctx, genai.Text(prompt))
if err != nil {
return "", err
return nil, err
}
if len(resp.Candidates) == 0 || len(resp.Candidates[0].Content.Parts) == 0 {
return "No suggestions found.", nil
return nil, nil
}
var result strings.Builder
var rawJS strings.Builder
for _, part := range resp.Candidates[0].Content.Parts {
if text, ok := part.(genai.Text); ok {
result.WriteString(string(text))
rawJS.WriteString(string(text))
}
}
return result.String(), nil
// Clean up markdown code blocks if present
js := strings.TrimSpace(rawJS.String())
js = strings.TrimPrefix(js, "```json")
js = strings.TrimSuffix(js, "```")
js = strings.TrimSpace(js)
var suggestions []Suggestion
if err := json.Unmarshal([]byte(js), &suggestions); err != nil {
return nil, fmt.Errorf("failed to unmarshal suggestions: %v (raw response: %s)", err, js)
}
return suggestions, nil
}
func addLineNumbers(text string) string {
lines := strings.Split(text, "\n")
for i, line := range lines {
lines[i] = fmt.Sprintf("%d: %s", i+1, line)
}
return strings.Join(lines, "\n")
}