Skip to content

Commit a630fd3

Browse files
authored
feat: add run summaries (#206)
1 parent f2ab2fb commit a630fd3

File tree

4 files changed

+156
-22
lines changed

4 files changed

+156
-22
lines changed

internal/command/updater.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,34 @@ import (
1616
"github.com/CustomResourceDefinition/catalog/internal/timing"
1717
)
1818

19+
type teeWriter struct {
20+
Writers []io.Writer
21+
}
22+
23+
func (t teeWriter) Write(p []byte) (n int, err error) {
24+
for _, w := range t.Writers {
25+
if _, err := w.Write(p); err != nil {
26+
return n, err
27+
}
28+
n = len(p)
29+
}
30+
return n, nil
31+
}
32+
33+
func (cmd Updater) createSummaryWriter() (io.Writer, func(), error) {
34+
summaryPath := os.Getenv("GITHUB_STEP_SUMMARY")
35+
if summaryPath == "" {
36+
return cmd.Logger, func() {}, nil
37+
}
38+
39+
f, err := os.OpenFile(summaryPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
40+
if err != nil {
41+
return nil, nil, err
42+
}
43+
44+
return teeWriter{Writers: []io.Writer{cmd.Logger, f}}, func() { f.Close() }, nil
45+
}
46+
1947
type Updater struct {
2048
Configuration, Schema, Definitions string
2149
Logger io.Writer
@@ -105,7 +133,12 @@ func (cmd Updater) Run() error {
105133
}
106134
}
107135

108-
totalStats.PrintSummary(cmd.Logger)
136+
writer, closer, err := cmd.createSummaryWriter()
137+
if err != nil {
138+
return err
139+
}
140+
defer closer()
141+
totalStats.PrintSummary(writer)
109142

110143
return merge(tmpDir, cmd.Schema)
111144
}

internal/command/updater_test.go

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package command
22

33
import (
44
"bytes"
5+
"fmt"
6+
"io"
57
"net/http"
68
"net/http/httptest"
79
"os"
@@ -328,9 +330,9 @@ func TestRunAggregatesStats(t *testing.T) {
328330

329331
outStr := output.String()
330332
assert.Contains(t, outStr, "Update Statistics")
331-
assert.Contains(t, outStr, "Overall:")
333+
assert.Contains(t, outStr, "**Overall:**")
332334
assert.Contains(t, outStr, "operations")
333-
assert.Contains(t, outStr, "Http:")
335+
assert.Contains(t, outStr, "#### Http")
334336
assert.Contains(t, outStr, "api_fetch")
335337
}
336338

@@ -417,6 +419,107 @@ func TestRunWithPerformanceLog(t *testing.T) {
417419
perfContent, err := os.ReadFile(logPath)
418420
assert.Nil(t, err)
419421
assert.NotEmpty(t, string(perfContent))
420-
assert.Contains(t, string(perfContent), "http")
421-
assert.Contains(t, string(perfContent), "api_fetch")
422+
}
423+
424+
func TestTeeWriter(t *testing.T) {
425+
var buf1, buf2 bytes.Buffer
426+
tw := teeWriter{Writers: []io.Writer{&buf1, &buf2}}
427+
428+
n, err := tw.Write([]byte("hello world"))
429+
assert.Nil(t, err)
430+
assert.Equal(t, 11, n)
431+
432+
assert.Equal(t, "hello world", buf1.String())
433+
assert.Equal(t, "hello world", buf2.String())
434+
}
435+
436+
func TestTeeWriterPartialError(t *testing.T) {
437+
var buf1 bytes.Buffer
438+
errorWriter := errorWriter{err: io.EOF}
439+
tw := teeWriter{Writers: []io.Writer{&buf1, errorWriter}}
440+
441+
_, err := tw.Write([]byte("hello"))
442+
assert.Error(t, err)
443+
assert.Equal(t, io.EOF, err)
444+
}
445+
446+
type errorWriter struct {
447+
err error
448+
}
449+
450+
func (e errorWriter) Write(p []byte) (n int, err error) {
451+
return 0, e.err
452+
}
453+
454+
func TestCreateSummaryWriterNoEnv(t *testing.T) {
455+
original := os.Getenv("GITHUB_STEP_SUMMARY")
456+
os.Unsetenv("GITHUB_STEP_SUMMARY")
457+
defer func() {
458+
if original != "" {
459+
os.Setenv("GITHUB_STEP_SUMMARY", original)
460+
}
461+
}()
462+
463+
buf := bytes.NewBuffer([]byte{})
464+
updater := Updater{Logger: buf}
465+
466+
writer, closer, err := updater.createSummaryWriter()
467+
assert.Nil(t, err)
468+
assert.Equal(t, buf, writer)
469+
closer()
470+
}
471+
472+
func TestCreateSummaryWriterWithEnv(t *testing.T) {
473+
original := os.Getenv("GITHUB_STEP_SUMMARY")
474+
tmpDir := t.TempDir()
475+
summaryPath := path.Join(tmpDir, "summary.md")
476+
os.Setenv("GITHUB_STEP_SUMMARY", summaryPath)
477+
defer func() {
478+
os.Unsetenv("GITHUB_STEP_SUMMARY")
479+
if original != "" {
480+
os.Setenv("GITHUB_STEP_SUMMARY", original)
481+
}
482+
}()
483+
484+
buf := bytes.NewBuffer([]byte{})
485+
updater := Updater{Logger: buf}
486+
487+
writer, closer, err := updater.createSummaryWriter()
488+
assert.Nil(t, err)
489+
closer()
490+
491+
_, ok := writer.(teeWriter)
492+
assert.True(t, ok)
493+
494+
content, err := os.ReadFile(summaryPath)
495+
assert.Nil(t, err)
496+
assert.Empty(t, string(content))
497+
}
498+
499+
func TestCreateSummaryWriterAppendsToFile(t *testing.T) {
500+
original := os.Getenv("GITHUB_STEP_SUMMARY")
501+
tmpDir := t.TempDir()
502+
summaryPath := path.Join(tmpDir, "summary.md")
503+
os.WriteFile(summaryPath, []byte("Existing content\n"), 0644)
504+
os.Setenv("GITHUB_STEP_SUMMARY", summaryPath)
505+
defer func() {
506+
os.Unsetenv("GITHUB_STEP_SUMMARY")
507+
if original != "" {
508+
os.Setenv("GITHUB_STEP_SUMMARY", original)
509+
}
510+
}()
511+
512+
buf := bytes.NewBuffer([]byte{})
513+
updater := Updater{Logger: buf}
514+
515+
writer, closer, err := updater.createSummaryWriter()
516+
assert.Nil(t, err)
517+
518+
fmt.Fprintf(writer, "New content")
519+
closer()
520+
521+
content, err := os.ReadFile(summaryPath)
522+
assert.Nil(t, err)
523+
assert.Contains(t, string(content), "Existing content")
524+
assert.Contains(t, string(content), "New content")
422525
}

internal/timing/stats.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ func (s *Stats) PrintSummary(writer io.Writer) {
235235
s.mu.RLock()
236236
defer s.mu.RUnlock()
237237

238-
fmt.Fprintf(writer, "\n=== Update Statistics ===\n\n")
239-
fmt.Fprintf(writer, "Overall: %s (%d operations)\n\n", formatDuration(s.totalTime), s.totalOps)
238+
fmt.Fprintf(writer, "\n### Update Statistics\n\n")
239+
fmt.Fprintf(writer, "**Overall:** %s (%d operations)\n\n", formatDuration(s.totalTime), s.totalOps)
240240

241241
categoryOrder := []Category{CategoryHTTP, CategoryGit, CategoryHelm, CategoryOCI, CategoryGeneration, CategoryMisc}
242242

@@ -252,7 +252,9 @@ func (s *Stats) PrintSummary(writer io.Writer) {
252252
typeStats[op.Type] = append(typeStats[op.Type], op.Duration)
253253
}
254254

255-
fmt.Fprintf(writer, "%s:\n", strings.ToUpper(string(cat)[:1])+string(cat)[1:])
255+
fmt.Fprintf(writer, "#### %s\n\n", strings.ToUpper(string(cat)[:1])+string(cat)[1:])
256+
fmt.Fprintf(writer, "| Operation | Count | Total | p75 | p90 | p95 |\n")
257+
fmt.Fprintf(writer, "|-----------|-------|-------|-----|-----|-----|\n")
256258

257259
for opType, durations := range typeStats {
258260
if len(durations) == 0 {
@@ -266,23 +268,19 @@ func (s *Stats) PrintSummary(writer io.Writer) {
266268
durationsSecs[i] = d.Seconds()
267269
}
268270

269-
percs := calculatePercentiles(durationsSecs, []float64{0.75, 0.90, 0.95})
271+
percentiles := calculatePercentiles(durationsSecs, []float64{0.75, 0.90, 0.95})
270272

271273
typeLabel := string(opType)
272-
fmt.Fprintf(writer, " %-10s %4d operations total: %s",
273-
typeLabel+":",
274+
p75 := formatDuration(percentiles[0.75])
275+
p90 := formatDuration(percentiles[0.90])
276+
p95 := formatDuration(percentiles[0.95])
277+
278+
fmt.Fprintf(writer, "| %s | %d | %s | %s | %s | %s |\n",
279+
typeLabel,
274280
len(durations),
275281
formatDuration(total),
282+
p75, p90, p95,
276283
)
277-
278-
if len(percs) > 0 {
279-
fmt.Fprintf(writer, " p75: %s p90: %s p95: %s",
280-
formatDuration(percs[0.75]),
281-
formatDuration(percs[0.90]),
282-
formatDuration(percs[0.95]),
283-
)
284-
}
285-
fmt.Fprintln(writer)
286284
}
287285
fmt.Fprintln(writer)
288286
}

internal/timing/stats_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,6 @@ func TestPrintSummaryWriter(t *testing.T) {
254254
s.PrintSummary(&buf)
255255

256256
assert.Contains(t, buf.String(), "Update Statistics")
257-
assert.Contains(t, buf.String(), "Overall:")
258-
assert.Contains(t, buf.String(), "Http:")
257+
assert.Contains(t, buf.String(), "**Overall:**")
258+
assert.Contains(t, buf.String(), "#### Http")
259259
}

0 commit comments

Comments
 (0)