Some checks failed
CI / test (push) Failing after 4s
Watches issue/PR comments and pushes for flagged phrases; warns commenters via reply and opens a redacted correction PR for flagged file content, never rewriting history or deleting content directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
// Package forgejo wraps the parts of the Gitea/Forgejo SDK that
|
|
// yaju-keisatsu needs: replying to comments and opening a correction PR.
|
|
package forgejo
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"code.gitea.io/sdk/gitea"
|
|
)
|
|
|
|
type Client struct {
|
|
sdk *gitea.Client
|
|
}
|
|
|
|
func New(baseURL, token string) (*Client, error) {
|
|
c, err := gitea.NewClient(baseURL, gitea.SetToken(token))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create gitea client: %w", err)
|
|
}
|
|
return &Client{sdk: c}, nil
|
|
}
|
|
|
|
// ReplyToIssue posts a comment on an issue or pull request.
|
|
func (c *Client) ReplyToIssue(owner, repo string, index int64, body string) error {
|
|
_, _, err := c.sdk.CreateIssueComment(owner, repo, index, gitea.CreateIssueCommentOption{Body: body})
|
|
if err != nil {
|
|
return fmt.Errorf("create issue comment: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FileContent fetches a file's current text content and blob SHA at ref.
|
|
func (c *Client) FileContent(owner, repo, ref, path string) (content, sha string, err error) {
|
|
meta, _, err := c.sdk.GetContents(owner, repo, ref, path)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("get file %s: %w", path, err)
|
|
}
|
|
if meta.Content == nil {
|
|
return "", "", fmt.Errorf("get file %s: not a regular file", path)
|
|
}
|
|
// The API may line-wrap the base64 payload; strip whitespace before decoding.
|
|
clean := strings.Map(func(r rune) rune {
|
|
if r == '\n' || r == '\r' {
|
|
return -1
|
|
}
|
|
return r
|
|
}, *meta.Content)
|
|
decoded, err := base64.StdEncoding.DecodeString(clean)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("decode file %s: %w", path, err)
|
|
}
|
|
return string(decoded), meta.SHA, nil
|
|
}
|
|
|
|
// CommitCorrection commits newContent for path on a new branch (branched off
|
|
// baseBranch) without touching baseBranch itself.
|
|
func (c *Client) CommitCorrection(owner, repo, baseBranch, newBranch, path, sha, newContent, message string) error {
|
|
_, _, err := c.sdk.UpdateFile(owner, repo, path, gitea.UpdateFileOptions{
|
|
SHA: sha,
|
|
Content: base64.StdEncoding.EncodeToString([]byte(newContent)),
|
|
FileOptions: gitea.FileOptions{
|
|
Message: message,
|
|
BranchName: baseBranch,
|
|
NewBranchName: newBranch,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("update file %s: %w", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OpenCorrectionPR opens a pull request from head into base.
|
|
func (c *Client) OpenCorrectionPR(owner, repo, head, base, title, body string) error {
|
|
_, _, err := c.sdk.CreatePullRequest(owner, repo, gitea.CreatePullRequestOption{
|
|
Head: head,
|
|
Base: base,
|
|
Title: title,
|
|
Body: body,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("create pull request: %w", err)
|
|
}
|
|
return nil
|
|
}
|