cli/pkg/forge/issues.go
Snider 2373a7d439 feat(bugseti): migrate from GitHub gh CLI to Forgejo SDK
Replace all exec.Command("gh", ...) calls with the existing pkg/forge
wrapper around the Forgejo Go SDK. BugSETI no longer requires the gh
CLI to be installed.

Changes:
- fetcher: use forge.ListIssues/GetIssue instead of gh issue list/view
- submit: use forge.ForkRepo/CreatePullRequest instead of gh pr create
- seeder: use git clone with forge URL + token auth instead of gh clone
- ghcheck: CheckForge() returns *forge.Client via forge.NewFromConfig()
- config: add ForgeURL/ForgeToken fields (GitHubToken kept for migration)
- pkg/forge: add Token(), GetCurrentUser(), ForkRepo(), CreatePullRequest(),
  ListIssueComments(), and label filtering to ListIssuesOpts

Co-Authored-By: Virgil <virgil@lethean.io>
2026-02-16 05:53:52 +00:00

181 lines
4.7 KiB
Go

package forge
import (
forgejo "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
"github.com/host-uk/core/pkg/log"
)
// ListIssuesOpts configures issue listing.
type ListIssuesOpts struct {
State string // "open", "closed", "all"
Labels []string // filter by label names
Page int
Limit int
}
// ListIssues returns issues for the given repository.
func (c *Client) ListIssues(owner, repo string, opts ListIssuesOpts) ([]*forgejo.Issue, error) {
state := forgejo.StateOpen
switch opts.State {
case "closed":
state = forgejo.StateClosed
case "all":
state = forgejo.StateAll
}
limit := opts.Limit
if limit == 0 {
limit = 50
}
page := opts.Page
if page == 0 {
page = 1
}
listOpt := forgejo.ListIssueOption{
ListOptions: forgejo.ListOptions{Page: page, PageSize: limit},
State: state,
Type: forgejo.IssueTypeIssue,
Labels: opts.Labels,
}
issues, _, err := c.api.ListRepoIssues(owner, repo, listOpt)
if err != nil {
return nil, log.E("forge.ListIssues", "failed to list issues", err)
}
return issues, nil
}
// GetIssue returns a single issue by number.
func (c *Client) GetIssue(owner, repo string, number int64) (*forgejo.Issue, error) {
issue, _, err := c.api.GetIssue(owner, repo, number)
if err != nil {
return nil, log.E("forge.GetIssue", "failed to get issue", err)
}
return issue, nil
}
// CreateIssue creates a new issue in the given repository.
func (c *Client) CreateIssue(owner, repo string, opts forgejo.CreateIssueOption) (*forgejo.Issue, error) {
issue, _, err := c.api.CreateIssue(owner, repo, opts)
if err != nil {
return nil, log.E("forge.CreateIssue", "failed to create issue", err)
}
return issue, nil
}
// EditIssue edits an existing issue.
func (c *Client) EditIssue(owner, repo string, number int64, opts forgejo.EditIssueOption) (*forgejo.Issue, error) {
issue, _, err := c.api.EditIssue(owner, repo, number, opts)
if err != nil {
return nil, log.E("forge.EditIssue", "failed to edit issue", err)
}
return issue, nil
}
// AssignIssue assigns an issue to the specified users.
func (c *Client) AssignIssue(owner, repo string, number int64, assignees []string) error {
_, _, err := c.api.EditIssue(owner, repo, number, forgejo.EditIssueOption{
Assignees: assignees,
})
if err != nil {
return log.E("forge.AssignIssue", "failed to assign issue", err)
}
return nil
}
// ListPullRequests returns pull requests for the given repository.
func (c *Client) ListPullRequests(owner, repo string, state string) ([]*forgejo.PullRequest, error) {
st := forgejo.StateOpen
switch state {
case "closed":
st = forgejo.StateClosed
case "all":
st = forgejo.StateAll
}
var all []*forgejo.PullRequest
page := 1
for {
prs, resp, err := c.api.ListRepoPullRequests(owner, repo, forgejo.ListPullRequestsOptions{
ListOptions: forgejo.ListOptions{Page: page, PageSize: 50},
State: st,
})
if err != nil {
return nil, log.E("forge.ListPullRequests", "failed to list pull requests", err)
}
all = append(all, prs...)
if resp == nil || page >= resp.LastPage {
break
}
page++
}
return all, nil
}
// GetPullRequest returns a single pull request by number.
func (c *Client) GetPullRequest(owner, repo string, number int64) (*forgejo.PullRequest, error) {
pr, _, err := c.api.GetPullRequest(owner, repo, number)
if err != nil {
return nil, log.E("forge.GetPullRequest", "failed to get pull request", err)
}
return pr, nil
}
// CreateIssueComment posts a comment on an issue or pull request.
func (c *Client) CreateIssueComment(owner, repo string, issue int64, body string) error {
_, _, err := c.api.CreateIssueComment(owner, repo, issue, forgejo.CreateIssueCommentOption{
Body: body,
})
if err != nil {
return log.E("forge.CreateIssueComment", "failed to create comment", err)
}
return nil
}
// ListIssueComments returns comments for an issue.
func (c *Client) ListIssueComments(owner, repo string, number int64) ([]*forgejo.Comment, error) {
var all []*forgejo.Comment
page := 1
for {
comments, resp, err := c.api.ListIssueComments(owner, repo, number, forgejo.ListIssueCommentOptions{
ListOptions: forgejo.ListOptions{Page: page, PageSize: 50},
})
if err != nil {
return nil, log.E("forge.ListIssueComments", "failed to list comments", err)
}
all = append(all, comments...)
if resp == nil || page >= resp.LastPage {
break
}
page++
}
return all, nil
}
// CloseIssue closes an issue by setting its state to closed.
func (c *Client) CloseIssue(owner, repo string, number int64) error {
closed := forgejo.StateClosed
_, _, err := c.api.EditIssue(owner, repo, number, forgejo.EditIssueOption{
State: &closed,
})
if err != nil {
return log.E("forge.CloseIssue", "failed to close issue", err)
}
return nil
}