package main import ( "context" "fmt" "io" "log" "os" "strconv" "strings" "code.gitea.io/sdk/gitea" "github.com/google/generative-ai-go/genai" "google.golang.org/api/option" ) func main() { apiKey := os.Getenv("GEMINI_API_KEY") token := os.Getenv("GITEA_TOKEN") baseURL := os.Getenv("GITEA_URL") repoFullName := os.Getenv("GITHUB_REPOSITORY") prNumberStr := os.Getenv("PR_NUMBER") if apiKey == "" || token == "" || repoFullName == "" || prNumberStr == "" { log.Fatal("Missing required environment variables: GEMINI_API_KEY, GITEA_TOKEN, GITHUB_REPOSITORY, PR_NUMBER") } if baseURL == "" { baseURL = "https://gitea.com" } prNumber, err := strconv.ParseInt(prNumberStr, 10, 64) if err != nil { log.Fatalf("Invalid PR_NUMBER: %v", err) } repoParts := strings.Split(repoFullName, "/") if len(repoParts) != 2 { log.Fatalf("Invalid GITHUB_REPOSITORY format: %s", repoFullName) } owner, repo := repoParts[0], repoParts[1] ctx := context.Background() // Initialize Gitea Client client, err := gitea.NewClient(baseURL, gitea.SetToken(token)) if err != nil { log.Fatalf("Failed to create Gitea client: %v", err) } // Initialize Gemini Client geminiClient, err := genai.NewClient(ctx, option.WithAPIKey(apiKey)) if err != nil { log.Fatalf("Failed to create Gemini client: %v", err) } defer geminiClient.Close() model := geminiClient.GenerativeModel("gemini-3-flash") // Get PR files files, _, err := client.ListPullRequestFiles(owner, repo, prNumber, gitea.ListPullRequestFilesOptions{}) if err != nil { log.Fatalf("Failed to get PR files: %v", err) } var reviews []string for _, file := range files { if !strings.HasSuffix(file.Filename, ".md") { continue } fmt.Printf("Reviewing file: %s\n", file.Filename) content, err := readFile(file.Filename) if err != nil { fmt.Printf("Error reading file %s: %v\n", file.Filename, err) continue } review, 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)) } if len(reviews) > 0 { commentBody := "### 🤖 Gemini Writing Review\n\n" + strings.Join(reviews, "\n\n---\n\n") _, _, err = client.CreateIssueComment(owner, repo, prNumber, gitea.CreateIssueCommentOption{ Body: commentBody, }) if err != nil { log.Fatalf("Failed to post PR comment: %v", err) } fmt.Println("Successfully posted review comments.") } else { fmt.Println("No Markdown files to review or no suggestions found.") } } func readFile(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } defer f.Close() content, err := io.ReadAll(f) if err != nil { return "", err } return string(content), nil } func getGeminiReview(ctx context.Context, model *genai.GenerativeModel, content string) (string, error) { prompt := fmt.Sprintf(` Review the following Markdown content for spelling errors, grammar mistakes, and style improvements. Provide your feedback as a list of bullet points. For each point: 1. Identify the issue. 2. Provide the original text. 3. Suggest an alternative or fix. Content: %s `, content) resp, err := model.GenerateContent(ctx, genai.Text(prompt)) if err != nil { return "", err } if len(resp.Candidates) == 0 || len(resp.Candidates[0].Content.Parts) == 0 { return "No suggestions found.", nil } var result strings.Builder for _, part := range resp.Candidates[0].Content.Parts { if text, ok := part.(genai.Text); ok { result.WriteString(string(text)) } } return result.String(), nil }