Commit 3bcdce33fb
3bcdce33fb9a2309312854331359d376171c7368
parent: bb4c0aeb12
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-28 08:26 UTC
web: step fold shows the step's first line; changelog: deploy order
Ref #266
Layout: unified · split
CHANGELOG.org
+4 −1
| @@ -144,7 +144,10 @@ for the eighteen commands whose CLI path differs from the registry's |
| 144 | a scratch repository per the CI page. (#260) |
144 | a scratch repository per the CI page. (#260) |
| 145 | - =build show= names a failed build's step and duration; =build log |
145 | - =build show= names a failed build's step and duration; =build log |
| 146 | --step <n>|failed --tail <lines>= reads one step's output or the |
146 | --step <n>|failed --tail <lines>= reads one step's output or the |
| 147 | last lines of the stored log. (#266) |
147 | last lines of the stored log. Deploy gitbayd before the runner: an |
| |
148 | older server refuses the runner's =--step= and =--reason= (exit 2), |
| |
149 | and its failed builds stay running until the reaper fails them. |
| |
150 | (#266) |
| 148 | |
151 | |
| 149 | * v1.36.0 — 2026-09-23 |
152 | * v1.36.0 — 2026-09-23 |
| 150 | |
153 | |
internal/httpd/buildpages_test.go
+24
| @@ -230,3 +230,27 @@ func TestBuildPageFoldsStepsAndOpensFailure(t *testing.T) { |
| 230 | t.Errorf("%d failed anchors, want 1", n) |
230 | t.Errorf("%d failed anchors, want 1", n) |
| 231 | } |
231 | } |
| 232 | } |
232 | } |
| |
233 | |
| |
234 | // A multi-line step's fold summary shows only its first line, as build |
| |
235 | // show does; the whole step is in the log (#266). |
| |
236 | func TestBuildPageStepSummaryIsFirstLine(t *testing.T) { |
| |
237 | step := "set -e\ngo test ./..." |
| |
238 | b := control.BuildOut{Number: 62, Job: "test", Status: "failure", |
| |
239 | SHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b", Ref: "main", |
| |
240 | CreatedAt: "2026-08-28T04:42:54Z", FinishedAt: "2026-08-28T04:43:54Z", DurationS: 60, |
| |
241 | Steps: []string{step}, FailedStep: 1, FailedReason: "exit 1"} |
| |
242 | log := "$ git clone x (ff6271a9d4)\n$ " + step + "\n--- FAIL: TestCLI\nstep 1/1 failed: exit 1\n" |
| |
243 | v := buildView{repoPage: testRepoPage(), Build: b, Log: log, Duration: "1m0s"} |
| |
244 | v.Steps, v.Failed = logSteps(log, b) |
| |
245 | var sb strings.Builder |
| |
246 | if err := web.Render(&sb, "build.html", v); err != nil { |
| |
247 | t.Fatalf("render: %v", err) |
| |
248 | } |
| |
249 | out := sb.String() |
| |
250 | if !strings.Contains(out, "<code>set -e</code>") { |
| |
251 | t.Errorf("summary does not show the step's first line:\n%s", out) |
| |
252 | } |
| |
253 | if strings.Contains(out, "<code>set -e\ngo test") { |
| |
254 | t.Errorf("summary shows the whole multi-line step") |
| |
255 | } |
| |
256 | } |
internal/httpd/builds.go
+5 −1
| @@ -306,6 +306,9 @@ type buildView struct { |
| 306 | |
306 | |
| 307 | type logStep struct { |
307 | type logStep struct { |
| 308 | control.LogSection |
308 | control.LogSection |
| |
309 | // Head is the step's first line, which its fold's summary shows, as |
| |
310 | // build show does; the full step is in the log below it. |
| |
311 | Head string |
| 309 | Failed bool |
312 | Failed bool |
| 310 | } |
313 | } |
| 311 | |
314 | |
| @@ -326,7 +329,8 @@ func logSteps(log string, b control.BuildOut) ([]logStep, bool) { |
| 326 | failed := control.FailedSection(sections, b.Status, b.FailedStep) |
329 | failed := control.FailedSection(sections, b.Status, b.FailedStep) |
| 327 | out := make([]logStep, len(sections)) |
330 | out := make([]logStep, len(sections)) |
| 328 | for i, s := range sections { |
331 | for i, s := range sections { |
| 329 | out[i] = logStep{LogSection: s, Failed: i == failed} |
332 | head, _, _ := strings.Cut(s.Step, "\n") |
| |
333 | out[i] = logStep{LogSection: s, Head: head, Failed: i == failed} |
| 330 | } |
334 | } |
| 331 | return out, failed >= 0 |
335 | return out, failed >= 0 |
| 332 | } |
336 | } |
internal/web/templates/build.html
+1 −1
| @@ -16,7 +16,7 @@ |
| 16 | <pre class="code buildlog" tabindex="0">{{.Log}}</pre> |
16 | <pre class="code buildlog" tabindex="0">{{.Log}}</pre> |
| 17 | {{else if .Steps}}{{$total := len .Build.Steps}}{{range .Steps}} |
17 | {{else if .Steps}}{{$total := len .Build.Steps}}{{range .Steps}} |
| 18 | <details class="difffold buildstep"{{if .Failed}} id="failed" open{{end}}> |
18 | <details class="difffold buildstep"{{if .Failed}} id="failed" open{{end}}> |
| 19 | <summary>{{if .N}}<span>step {{.N}}/{{$total}}</span> <code>{{.Step}}</code>{{else}}<span>setup</span>{{end}}{{if .Failed}} <span class="chip check-failure">failed</span>{{end}}</summary> |
19 | <summary>{{if .N}}<span>step {{.N}}/{{$total}}</span> <code>{{.Head}}</code>{{else}}<span>setup</span>{{end}}{{if .Failed}} <span class="chip check-failure">failed</span>{{end}}</summary> |
| 20 | <pre class="code buildlog" tabindex="0">{{.Text}}</pre> |
20 | <pre class="code buildlog" tabindex="0">{{.Text}}</pre> |
| 21 | </details>{{end}} |
21 | </details>{{end}} |
| 22 | {{else if .Log}}<pre class="code buildlog" tabindex="0">{{.Log}}</pre>{{else}}<p class="empty-note">no log yet</p>{{end}} |
22 | {{else if .Log}}<pre class="code buildlog" tabindex="0">{{.Log}}</pre>{{else}}<p class="empty-note">no log yet</p>{{end}} |