// Package gitea provides a minimal Gitea API client for fetching PR diffs // and posting review comments. package gitea import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" ) const maxDiffBytes = 100 * 1024 // 100 KB — well within deepseek-coder's context // Client is a minimal Gitea API client. type Client struct { BaseURL string Token string http *http.Client } // New creates a new Gitea client. func New(baseURL, token string) *Client { return &Client{ BaseURL: baseURL, Token: token, http: &http.Client{Timeout: 30 * time.Second}, } } func (c *Client) newRequest(method, url string, body []byte) (*http.Request, error) { var req *http.Request var err error if body != nil { req, err = http.NewRequest(method, url, bytes.NewReader(body)) } else { req, err = http.NewRequest(method, url, nil) } if err != nil { return nil, err } req.Header.Set("Authorization", "token "+c.Token) if body != nil { req.Header.Set("Content-Type", "application/json") } return req, nil } // FetchDiff returns the unified diff for a pull request. // It fetches the raw .diff endpoint and caps the result at maxDiffBytes. func (c *Client) FetchDiff(owner, repo string, number int) (string, error) { url := fmt.Sprintf("%s/%s/%s/pulls/%d.diff", c.BaseURL, owner, repo, number) req, err := c.newRequest("GET", url, nil) if err != nil { return "", err } resp, err := c.http.Do(req) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("gitea: fetch diff %s/%s#%d: %s", owner, repo, number, resp.Status) } b, err := io.ReadAll(io.LimitReader(resp.Body, maxDiffBytes)) if err != nil { return "", err } return string(b), nil } // PostComment posts a comment on a pull request. // In Gitea, pull request comments are posted via the issues API using the PR number. func (c *Client) PostComment(owner, repo string, number int, body string) error { url := fmt.Sprintf("%s/api/v1/repos/%s/%s/issues/%d/comments", c.BaseURL, owner, repo, number) payload, _ := json.Marshal(map[string]string{"body": body}) req, err := c.newRequest("POST", url, payload) if err != nil { return err } resp, err := c.http.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 300 { return fmt.Errorf("gitea: post comment %s/%s#%d: %s", owner, repo, number, resp.Status) } return nil }