vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Tail run logs in the browser and make steps accordions
34d6fd7a9210fca265ea4aea1d5733b3eeeb2569 · Keenan Nemetz · 10d ago · parent 5ece70a5
Tail run logs in the browser and make steps accordions The log endpoint now serves from a byte offset and marks an attempt's log final, so the run page can poll only while the job runs. The job output accordion follows the stream; each step is its own accordion with its slice or a clear empty state. The steps table is gone; the logs card is the whole viewer.
server/web/templates/run.html +23−41
| @@ -58,52 +58,34 @@ | |||
| 58 | 58 | {{if .Attestation}}<p class="muted">reported by <strong>{{.Signer}}</strong> · the instance accepted this result from that account's runner connection, and logged it</p>{{end}} | |
| 59 | 59 | {{if .Counts}}<p class="muted">counts as a check for this repository</p>{{else if .CountsWhy}}<p class="muted">does not count as a check: {{.CountsWhy}}</p>{{end}} | |
| 60 | 60 | ||
| 61 | <h2>{{icon "list"}}Steps</h2> | ||
| 62 | {{if .Steps}} | ||
| 63 | <div class="panel run-steps"> | ||
| 64 | <table> | ||
| 65 | <thead> | ||
| 66 | <tr><th>#</th><th>Step</th><th>Result</th><th>Duration</th><th>Log</th></tr> | ||
| 67 | </thead> | ||
| 68 | <tbody> | ||
| 69 | {{range .Steps}} | ||
| 70 | <tr> | ||
| 71 | <td class="fit muted">{{.Number}}</td> | ||
| 72 | <td>{{.Name}}</td> | ||
| 73 | <td class="fit"><span class="status {{.Status}}">{{.Label}}</span></td> | ||
| 74 | <td class="nowrap">{{if .Duration}}{{duration .Duration}}{{else}}<span class="muted">—</span>{{end}}</td> | ||
| 75 | <td>{{if .LogURL}}<a href="{{.LogURL}}">open</a>{{else}}<span class="muted">—</span>{{end}}</td> | ||
| 76 | </tr> | ||
| 77 | {{if .LogURL}} | ||
| 78 | <tr class="run-step-log"> | ||
| 79 | <td></td> | ||
| 80 | <td colspan="4"> | ||
| 81 | <details class="run-log" hx-get="{{.LogURL}}" hx-trigger="toggle once" hx-target="find pre" hx-swap="textContent"> | ||
| 82 | <summary>{{icon "terminal"}}Show step log</summary> | ||
| 83 | <pre class="run-log-text"></pre> | ||
| 84 | </details> | ||
| 85 | </td> | ||
| 86 | </tr> | ||
| 87 | {{end}} | ||
| 88 | {{end}} | ||
| 89 | </tbody> | ||
| 90 | </table> | ||
| 91 | </div> | ||
| 92 | {{else}} | ||
| 93 | <p class="muted">This run has no steps recorded.</p> | ||
| 94 | {{end}} | ||
| 95 | |||
| 96 | 61 | <h2>{{icon "terminal"}}Logs</h2> | |
| 97 | 62 | {{if .HasLog}} | |
| 98 | 63 | <div class="run-logbar"> | |
| 99 | <span class="muted">{{bytes .LogBytes}} archived log{{if gt (len .Logs) 1}} · {{len .Logs}} attempts{{end}}</span> | ||
| 100 | <a href="/r/{{.Name}}/runs/{{.Run.ID}}/log">Open full log</a> | ||
| 64 | <span class="muted">{{bytes .LogBytes}} {{if .Live}}streaming{{else}}log{{end}}</span> | ||
| 101 | 65 | {{if gt (len .Logs) 1}}{{range .Logs}}<a class="chip" href="/r/{{$.Name}}/runs/{{$.Run.ID}}/log?attempt={{.Attempt}}">{{icon "file"}}attempt {{.Attempt}}</a> {{end}}{{end}} | |
| 102 | 66 | </div> | |
| 103 | <details class="run-log" hx-get="/r/{{.Name}}/runs/{{.Run.ID}}/log" hx-trigger="toggle once" hx-target="find pre" hx-swap="textContent"> | ||
| 104 | <summary>{{icon "terminal"}}Show the full job log</summary> | ||
| 105 | <pre class="run-log-text"></pre> | ||
| 106 | </details> | ||
| 67 | <div class="card run-log-card"> | ||
| 68 | <details class="run-log run-log-live" {{if .Live}}open{{end}}> | ||
| 69 | <summary> | ||
| 70 | {{icon "terminal"}}<span class="run-log-title">Job output</span> | ||
| 71 | <span class="run-log-meta">attempt {{.LogAttempt}}{{if .Live}} · live{{else}} · complete{{end}}</span> | ||
| 72 | </summary> | ||
| 73 | <pre class="run-log-text" data-log-stream | ||
| 74 | data-log-url="/r/{{.Name}}/runs/{{.Run.ID}}/log?attempt={{.LogAttempt}}" | ||
| 75 | data-log-final="{{if .Live}}0{{else}}1{{end}}"></pre> | ||
| 76 | </details> | ||
| 77 | {{range .Steps}} | ||
| 78 | <details class="run-log{{if .LogURL}} run-step-log{{end}}"{{if .LogURL}} hx-get="{{.LogURL}}" hx-trigger="toggle once" hx-target="find pre" hx-swap="textContent"{{end}}> | ||
| 79 | <summary> | ||
| 80 | <span class="run-step-num">{{.Number}}</span> | ||
| 81 | <span class="run-step-name">{{.Name}}</span> | ||
| 82 | <span class="status {{.Status}}">{{.Label}}</span> | ||
| 83 | <span class="run-step-meta">{{if .Duration}}{{duration .Duration}}{{else}}—{{end}}</span> | ||
| 84 | </summary> | ||
| 85 | {{if .LogURL}}<pre class="run-log-text"></pre>{{else}}<p class="run-log-empty">{{if eq .Status "running"}}This step is running; its output is in the job output above.{{else if eq .Status "pending"}}Not started yet.{{else if eq .Status "queued"}}Waiting for a runner.{{else if eq .Status "not run"}}Not run.{{else}}No output was captured for this step.{{end}}</p>{{end}} | ||
| 86 | </details> | ||
| 87 | {{end}} | ||
| 88 | </div> | ||
| 107 | 89 | {{else}} | |
| 108 | 90 | <p class="muted">No log was archived for this run.</p> | |
| 109 | 91 | {{end}} | |