Commit 480aeee73f
480aeee73f37d273e63517d0329b9f3a00899722
parent: 314c9ca55c
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-11 16:17 UTC
store, web: the issue list shows the org's labels
ListIssueLabels filtered on repo_id, which no org label has, so a chip
set from an org label never reached the web issue list. It takes the
repository and the scope clause now, and joins issues so the answer stays
the repository's own.
Ref #203
internal/httpd/web.go
+1 −1
| @@ -1668,7 +1668,7 @@ func (s *Server) issues(w http.ResponseWriter, r *http.Request) { |
| 1668 | 1668 | issues = issues[:listPage] |
| 1669 | 1669 | older = olderLink(r, issues[len(issues)-1].Number) |
| 1670 | 1670 | } |
| 1671 | | if labels, err := s.st.ListIssueLabels(p.Repo.ID); err == nil { |
| 1671 | if labels, err := s.st.ListIssueLabels(p.Repo); err == nil { |
| 1672 | 1672 | for i := range issues { |
| 1673 | 1673 | issues[i].Labels = labels[issues[i].ID] |
| 1674 | 1674 | } |
internal/store/issues.go
+7 −4
| @@ -271,13 +271,16 @@ func (s *Store) AddIssueSystemComment(issueID, actorID int64, body string) error |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | // ListIssueLabels returns the label names attached to each issue of a |
| 274 | | // repo, keyed by issue id. Used by the web issue listing; ListIssues |
| 275 | | // itself stays label-free for the CLI's lean list output. |
| 276 | | func (s *Store) ListIssueLabels(repoID int64) (map[int64][]string, error) { |
| 274 | // repo, keyed by issue id, its org's labels included. Used by the web |
| 275 | // issue listing; ListIssues itself stays label-free for the CLI's lean |
| 276 | // list output. |
| 277 | func (s *Store) ListIssueLabels(repo Repo) (map[int64][]string, error) { |
| 278 | where, args := scopeClause("l", repo) |
| 277 | 279 | rows, err := s.DB.Query(` |
| 278 | 280 | SELECT il.issue_id, l.name FROM issue_labels il |
| 279 | 281 | JOIN labels l ON l.id = il.label_id |
| 280 | | WHERE l.repo_id = ? ORDER BY l.name`, repoID) |
| 282 | JOIN issues i ON i.id = il.issue_id |
| 283 | WHERE i.repo_id = ? AND `+where+` ORDER BY l.name`, append([]any{repo.ID}, args...)...) |
| 281 | 284 | if err != nil { |
| 282 | 285 | return nil, err |
| 283 | 286 | } |
internal/store/labels_test.go
+25
| @@ -137,6 +137,31 @@ func TestIssueLabelResolvesOrgRowFirst(t *testing.T) { |
| 137 | 137 | } |
| 138 | 138 | } |
| 139 | 139 | |
| 140 | // The web issue list reads labels per repository; an org label attached |
| 141 | // to an issue has to come back from there like the repository's own. |
| 142 | func TestListIssueLabelsIncludesOrgRows(t *testing.T) { |
| 143 | f := newAcme(t) |
| 144 | if _, err := f.s.SetOrgLabel(f.org, "bug", ""); err != nil { |
| 145 | t.Fatal(err) |
| 146 | } |
| 147 | if err := f.s.SetLabel(f.core, "docs", ""); err != nil { |
| 148 | t.Fatal(err) |
| 149 | } |
| 150 | for _, name := range []string{"bug", "docs"} { |
| 151 | if err := f.s.SetIssueLabel(f.core, f.coreIssue, name, true); err != nil { |
| 152 | t.Fatal(err) |
| 153 | } |
| 154 | } |
| 155 | got, err := f.s.ListIssueLabels(f.core) |
| 156 | if err != nil || len(got[f.coreIssue]) != 2 || got[f.coreIssue][0] != "bug" || got[f.coreIssue][1] != "docs" { |
| 157 | t.Fatalf("core issue labels = %v, %v", got, err) |
| 158 | } |
| 159 | // Another repository under the org does not pick up core's attachment. |
| 160 | if got, _ := f.s.ListIssueLabels(f.site); len(got) != 0 { |
| 161 | t.Fatalf("site issue labels = %v", got) |
| 162 | } |
| 163 | } |
| 164 | |
| 140 | 165 | func TestRepoLabelRefusedWhenOrgHoldsName(t *testing.T) { |
| 141 | 166 | f := newAcme(t) |
| 142 | 167 | if _, err := f.s.SetOrgLabel(f.org, "bug", ""); err != nil { |