Commit af0982ad6e

af0982ad6edb02b2633f564cb07c0ceb653cfd97

parent: 25ce05686e

Verified · cmc ci/build: success ci/test: success

cmc <hello@cleberg.net> · 2026-10-14 05:13 UTC

show: mark system comments with kind in --json

A line the server posted reaches --json with author "system", which an
account can also be named, and react refuses it. Comments carry no kind,
so their wire shape is unchanged.

Closes #332

Layout: unified · split

internal/control/commentkind_test.go added +51
@@ -0,0 +1,51 @@
1package control
2
3import (
4 "bytes"
5 "encoding/json"
6 "testing"
7
8 "gitbay.org/gitbay/internal/protocol"
9 "gitbay.org/gitbay/internal/store"
10)
11
12// A line the server posted (a label change, a retarget) says so in
13// --json; a client that offers reactions must not offer them on one, since
14// issue react and mr react refuse it. A comment carries no kind, so the
15// wire shape of every existing comment is unchanged.
16func TestShowMarksSystemComments(t *testing.T) {
17 st, repo, uid := newQueueTestRepo(t)
18 id, err := st.CreateIssue(repo.ID, uid, "t", "", "md")
19 if err != nil {
20 t.Fatal(err)
21 }
22 if err := st.AddIssueComment(id, uid, "me too", "md"); err != nil {
23 t.Fatal(err)
24 }
25 if err := st.AddIssueSystemComment(id, uid, "added label bug"); err != nil {
26 t.Fatal(err)
27 }
28 c, errOut := pruneCtx(st, t.TempDir(), store.User{ID: uid, Username: "alice"})
29 c.JSON = true
30 if code := Dispatch(c, []string{"issue", "show", repo.Path(), "1"}); code != protocol.ExitOK {
31 t.Fatalf("exit %d: %s", code, errOut)
32 }
33 var env struct {
34 Data struct {
35 Comments []map[string]any `json:"comments"`
36 } `json:"data"`
37 }
38 if err := json.Unmarshal(c.Stdout.(*bytes.Buffer).Bytes(), &env); err != nil {
39 t.Fatal(err)
40 }
41 cs := env.Data.Comments
42 if len(cs) != 2 {
43 t.Fatalf("comments: %v", cs)
44 }
45 if _, ok := cs[0]["kind"]; ok {
46 t.Errorf("a comment carries a kind: %v", cs[0])
47 }
48 if cs[1]["kind"] != "system" {
49 t.Errorf("a system line is not marked: %v", cs[1])
50 }
51}
internal/control/issue.go +1 −1
@@ -381,7 +381,7 @@ func runIssueShow(c *Ctx, args []string) int {
381381 }
382382 var cs []commentOut
383383 for _, cm := range comments {
384 cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, cm.Kind, reactionsOut(rx[cm.ID])})
384 cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, wireKind(cm.Kind), reactionsOut(rx[cm.ID])})
385385 }
386386 d := IssueShow{issueOut: issueToOut(issue, true), Reactions: reactionsOut(rx[0]), Comments: cs}
387387 canWrite := false
internal/control/mr.go +1 −1
@@ -788,7 +788,7 @@ func runMRShow(c *Ctx, args []string) int {
788788 }
789789 var cs []commentOut
790790 for _, cm := range comments {
791 cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, cm.Kind, reactionsOut(rx[cm.ID])})
791 cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, wireKind(cm.Kind), reactionsOut(rx[cm.ID])})
792792 }
793793 var rs []ReviewOut
794794 counts := ReviewersWhoCount(c.Store, repo, reviews)
internal/control/thread.go +17 −7
@@ -50,13 +50,23 @@ func authorOrWrite(c *Ctx, repo store.Repo, author, what string) int {
5050
5151// commentOut is one comment as show emits it, for both nouns.
5252type commentOut struct {
53 ID int64 `json:"id"`
54 Author string `json:"author"`
55 Body string `json:"body"`
56 BodyFormat string `json:"body_format,omitempty"`
57 CreatedAt string `json:"created_at"`
58 Kind string `json:"-"` // "comment" or "system"; not part of the wire shape
59 Reactions []ReactionOut `json:"reactions,omitempty"`
53 ID int64 `json:"id"`
54 Author string `json:"author"`
55 Body string `json:"body"`
56 BodyFormat string `json:"body_format,omitempty"`
57 CreatedAt string `json:"created_at"`
58 // Kind is "system" for a line the server posted and "" for a comment,
59 // so the wire shape of a comment is unchanged.
60 Kind string `json:"kind,omitempty"`
61 Reactions []ReactionOut `json:"reactions,omitempty"`
62}
63
64// wireKind is the stored comment kind as commentOut carries it.
65func wireKind(kind string) string {
66 if kind == "system" {
67 return kind
68 }
69 return ""
6070}
6171
6272// thread is what a comment command needs to know about its noun.