feat(forge): add org label iterator
Some checks failed
Security Scan / security (push) Failing after 12s
Test / test (push) Successful in 2m17s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 09:42:41 +00:00
parent f27a01d3c5
commit 697bfde215
3 changed files with 60 additions and 1 deletions

View file

@ -89,7 +89,7 @@ The `gitea/` package mirrors this using `GITEA_URL`/`GITEA_TOKEN` and `gitea.*`
| `client.go` | `New`, `NewFromConfig`, `GetCurrentUser`, `ForkRepo`, `CreatePullRequest` |
| `repos.go` | `ListOrgRepos`, `ListOrgReposIter`, `ListUserRepos`, `ListUserReposIter`, `GetRepo`, `CreateOrgRepo`, `DeleteRepo`, `MigrateRepo` |
| `issues.go` | `ListIssues`, `ListIssuesIter`, `GetIssue`, `CreateIssue`, `EditIssue`, `AssignIssue`, `ListPullRequests`, `ListPullRequestsIter`, `GetPullRequest`, `CreateIssueComment`, `ListIssueComments`, `ListIssueCommentsIter`, `CloseIssue` |
| `labels.go` | `ListOrgLabels`, `ListRepoLabels`, `ListRepoLabelsIter`, `CreateRepoLabel`, `GetLabelByName`, `EnsureLabel`, `AddIssueLabels`, `RemoveIssueLabel` |
| `labels.go` | `ListOrgLabels`, `ListOrgLabelsIter`, `ListRepoLabels`, `ListRepoLabelsIter`, `CreateRepoLabel`, `GetLabelByName`, `EnsureLabel`, `AddIssueLabels`, `RemoveIssueLabel` |
| `prs.go` | `MergePullRequest`, `SetPRDraft`, `ListPRReviews`, `GetCombinedStatus`, `DismissReview` |
| `webhooks.go` | `CreateRepoWebhook`, `ListRepoWebhooks` |
| `orgs.go` | `ListMyOrgs`, `GetOrg`, `CreateOrg` |

View file

@ -48,6 +48,40 @@ func (c *Client) ListOrgLabels(org string) ([]*forgejo.Label, error) {
return all, nil
}
// ListOrgLabelsIter returns an iterator over unique labels across repos in the given organisation.
// Note: The Forgejo SDK does not have a dedicated org-level labels endpoint.
// Labels are yielded in first-seen order across repositories and deduplicated by name.
// Usage: ListOrgLabelsIter(...)
func (c *Client) ListOrgLabelsIter(org string) iter.Seq2[*forgejo.Label, error] {
return func(yield func(*forgejo.Label, error) bool) {
seen := make(map[string]struct{})
for repo, err := range c.ListOrgReposIter(org) {
if err != nil {
yield(nil, log.E("forge.ListOrgLabels", "failed to list org repos", err))
return
}
for label, err := range c.ListRepoLabelsIter(repo.Owner.UserName, repo.Name) {
if err != nil {
yield(nil, log.E("forge.ListOrgLabels", "failed to list repo labels", err))
return
}
key := strings.ToLower(label.Name)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
if !yield(label, nil) {
return
}
}
}
}
}
// ListRepoLabels returns all labels for a repository.
// Usage: ListRepoLabels(...)
func (c *Client) ListRepoLabels(owner, repo string) ([]*forgejo.Label, error) {

View file

@ -161,6 +161,31 @@ func TestClient_ListOrgLabels_Good(t *testing.T) {
assert.Equal(t, "documentation", labels[2].Name)
}
func TestClient_ListOrgLabelsIter_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
var names []string
for label, err := range client.ListOrgLabelsIter("test-org") {
require.NoError(t, err)
names = append(names, label.Name)
}
require.Len(t, names, 3)
assert.Equal(t, []string{"bug", "feature", "documentation"}, names)
}
func TestClient_ListOrgLabelsIter_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
for _, err := range client.ListOrgLabelsIter("test-org") {
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to list org repos")
break
}
}
func TestClient_ListOrgLabels_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()