deps: pad the issue table so the mail is legible !132

merged merged by cmc on 2026-08-31 15:30 UTC · krz/gitbay:deps-table-align into main

2 files changed, +72 −2

Layout: unified · split

internal/deps/report.go +29 −2
@@ -39,10 +39,37 @@ func Body(branch string, reports []store.DepReport) string {
39 continue 39 continue
40 } 40 }
41 sort.Slice(rows, func(i, j int) bool { return rows[i].Name < rows[j].Name }) 41 sort.Slice(rows, func(i, j int) bool { return rows[i].Name < rows[j].Name })
42 fmt.Fprintf(&b, "\n### %s\n\n| Package | Current | Latest |\n| --- | --- | --- |\n", ecosystemNames[eco]) 42 fmt.Fprintf(&b, "\n### %s\n\n", ecosystemNames[eco])
43 cells := [][3]string{{"Package", "Current", "Latest"}}
43 for _, r := range rows { 44 for _, r := range rows {
44 fmt.Fprintf(&b, "| `%s` | %s | %s |\n", r.Name, r.Current, r.Latest) 45 cells = append(cells, [3]string{"`" + r.Name + "`", r.Current, r.Latest})
45 } 46 }
47 b.WriteString(table(cells))
48 }
49 return b.String()
50}
51
52// table pads every cell to its column, because the body is read twice: as
53// a rendered table on the web, where the padding is ignored, and as the
54// plain text of the notification mail, where it is the only thing keeping
55// the columns readable. Row 0 is the header.
56func table(cells [][3]string) string {
57 var w [3]int
58 for _, c := range cells {
59 for i := range w {
60 if n := len(c[i]); n > w[i] {
61 w[i] = n
62 }
63 }
64 }
65 var b strings.Builder
66 row := func(c [3]string) {
67 fmt.Fprintf(&b, "| %-*s | %-*s | %-*s |\n", w[0], c[0], w[1], c[1], w[2], c[2])
68 }
69 row(cells[0])
70 row([3]string{strings.Repeat("-", w[0]), strings.Repeat("-", w[1]), strings.Repeat("-", w[2])})
71 for _, c := range cells[1:] {
72 row(c)
46 } 73 }
47 return b.String() 74 return b.String()
48} 75}
internal/deps/report_test.go added +43
@@ -0,0 +1,43 @@
1package deps
2
3import (
4 "strings"
5 "testing"
6
7 "gitbay.org/gitbay/internal/store"
8)
9
10// The body is read as a rendered table on the web and as the plain text of
11// the notification mail, so every row has to line up on its own.
12func TestBodyAlignsColumns(t *testing.T) {
13 body := Body("main", []store.DepReport{
14 {Ecosystem: EcoGo, Name: "go.yaml.in/yaml/v3", Current: "v3.0.4", Latest: "v3.0.5"},
15 {Ecosystem: EcoGo, Name: "golang.org/x/net", Current: "v0.57.0", Latest: "v0.58.0"},
16 {Ecosystem: EcoNPM, Name: "react", Current: "18.2.0", Latest: "19.0.0"},
17 })
18 var pipes []int
19 for _, line := range strings.Split(body, "\n") {
20 if !strings.HasPrefix(line, "|") {
21 pipes = append(pipes, -1) // section break
22 continue
23 }
24 pipes = append(pipes, len(line))
25 }
26 // Within a run of table lines every line is the same length.
27 var run []int
28 for _, n := range append(pipes, -1) {
29 if n == -1 {
30 for _, w := range run {
31 if w != run[0] {
32 t.Fatalf("ragged table in:\n%s", body)
33 }
34 }
35 run = nil
36 continue
37 }
38 run = append(run, n)
39 }
40 if !strings.Contains(body, "| `golang.org/x/net` | v0.57.0 | v0.58.0 |") {
41 t.Errorf("row not padded to its column:\n%s", body)
42 }
43}