Commit 90af4498e8
90af4498e80f7d80cfa784e16adbc747dbd593af
parent: c89859bef9
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-20 10:09 UTC
control: issue assign files a notice
The dashboard surfaced assigned work and nothing announced it. Direct,
as a mention is, so watchers are not told they were assigned.
Ref #89
internal/control/issue.go
+12
| @@ -445,6 +445,7 @@ func runIssueAssign(c *Ctx, args []string) int { |
| 445 | 445 | } |
| 446 | 446 | return u, -1 |
| 447 | 447 | } |
| 448 | var added []int64 |
| 448 | 449 | for _, name := range adds { |
| 449 | 450 | u, code := resolve(name) |
| 450 | 451 | if code >= 0 { |
| @@ -453,6 +454,7 @@ func runIssueAssign(c *Ctx, args []string) int { |
| 453 | 454 | if err := c.Store.SetIssueAssignee(issue.ID, u.ID, true); err != nil { |
| 454 | 455 | return c.fail(protocol.ExitFailure, "%v", err) |
| 455 | 456 | } |
| 457 | added = append(added, u.ID) |
| 456 | 458 | } |
| 457 | 459 | for _, name := range removes { |
| 458 | 460 | u, code := resolve(name) |
| @@ -472,6 +474,16 @@ func runIssueAssign(c *Ctx, args []string) int { |
| 472 | 474 | } |
| 473 | 475 | c.Store.RecordEvent(repo.ID, c.User.ID, "issue.assigned", |
| 474 | 476 | fmt.Sprintf(`{"number":%d,"assignees":%s}`, issue.Number, jsonStrings(updated.Assignees))) |
| 477 | if len(added) > 0 { |
| 478 | // direct, as a mention is: an assignment is addressed to someone, |
| 479 | // and widening it to watchers would tell them "assigned you". |
| 480 | // Removals file nothing, and notify drops the actor, so assigning |
| 481 | // yourself is silent. |
| 482 | notify(c, added, notice{repo: repo, kind: "issue", direct: true, |
| 483 | subject: fmt.Sprintf("[%s] #%d: %s", repo.Path(), issue.Number, issue.Title), |
| 484 | action: fmt.Sprintf("assigned you to #%d", issue.Number), |
| 485 | path: fmt.Sprintf("%s/issues/%d", repo.Path(), issue.Number)}) |
| 486 | } |
| 475 | 487 | return c.emit(map[string]any{"number": issue.Number, "assignees": updated.Assignees}, func(w io.Writer) { |
| 476 | 488 | fmt.Fprintf(w, "assignees on %s#%d: %s\n", repo.Path(), issue.Number, strings.Join(updated.Assignees, ", ")) |
| 477 | 489 | }) |
internal/control/issue_test.go
added
+34
| @@ -0,0 +1,34 @@ |
| 1 | package control |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestIssueAssignNotifiesTheAssignee(t *testing.T) { |
| 6 | c, repo, bob := testRepoWithWatcher(t) |
| 7 | |
| 8 | if code := runIssueAssign(c, []string{repo.Path(), "1", "--add", "bob"}); code != 0 { |
| 9 | t.Fatalf("exit %d", code) |
| 10 | } |
| 11 | rows, _ := c.Store.Inbox(bob, false, 20, 0) |
| 12 | if len(rows) != 1 || rows[0].Summary != "assigned you to #1" { |
| 13 | t.Fatalf("got %+v", rows) |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | func TestIssueAssignIsSilentForTheActorAndForRemovals(t *testing.T) { |
| 18 | c, repo, bob := testRepoWithWatcher(t) |
| 19 | |
| 20 | // Assigning yourself announces nothing: notify drops the actor. |
| 21 | runIssueAssign(c, []string{repo.Path(), "1", "--add", "alice"}) |
| 22 | if rows, _ := c.Store.Inbox(c.User.ID, false, 20, 0); len(rows) != 0 { |
| 23 | t.Fatalf("self-assignment notified: %+v", rows) |
| 24 | } |
| 25 | |
| 26 | // Unassigning files nothing. |
| 27 | runIssueAssign(c, []string{repo.Path(), "1", "--add", "bob"}) |
| 28 | before, _ := c.Store.Inbox(bob, false, 20, 0) |
| 29 | runIssueAssign(c, []string{repo.Path(), "1", "--remove", "bob"}) |
| 30 | after, _ := c.Store.Inbox(bob, false, 20, 0) |
| 31 | if len(after) != len(before) { |
| 32 | t.Fatalf("removal filed a row: %d then %d", len(before), len(after)) |
| 33 | } |
| 34 | } |
internal/control/notifications_test.go
+5 −2
| @@ -37,8 +37,8 @@ func notifTestCtx(t *testing.T, username string) *Ctx { |
| 37 | 37 | } |
| 38 | 38 | |
| 39 | 39 | // testRepoWithWatcher returns a Ctx acting as alice, a repository she |
| 40 | | // owns, and bob's user id with a watch row on it — the shared setup for |
| 41 | | // notify's recipient-widening tests. |
| 40 | // owns with issue #1 open on it, and bob's user id with a watch row on |
| 41 | // it — the shared setup for notify's recipient-widening tests. |
| 42 | 42 | func testRepoWithWatcher(t *testing.T) (*Ctx, store.Repo, int64) { |
| 43 | 43 | t.Helper() |
| 44 | 44 | c := notifTestCtx(t, "alice") |
| @@ -50,6 +50,9 @@ func testRepoWithWatcher(t *testing.T) (*Ctx, store.Repo, int64) { |
| 50 | 50 | if err != nil { |
| 51 | 51 | t.Fatal(err) |
| 52 | 52 | } |
| 53 | if _, err := c.Store.CreateIssue(repo.ID, c.User.ID, "title", "", "markdown"); err != nil { |
| 54 | t.Fatal(err) |
| 55 | } |
| 53 | 56 | bob, err := c.Store.CreateUser("bob", false) |
| 54 | 57 | if err != nil { |
| 55 | 58 | t.Fatal(err) |