vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Make the run list say what triggered each run
57dc79afa8097830de03fc2fc2c19561470b6d36 · Keenan Nemetz · 6d ago · parent 338a19dd
Make the run list say what triggered each run The runs table said only which ref a run used, so a push, an issue event and a manual dispatch looked alike. Give each run a TriggerTitle and show it in a "Triggered by" column; the run page uses the same words. Drop the separate workflows page. The run list is where a job is found, and the run page now links the workflow definition it ran, so a second page listing the same jobs only split the answer. Show the full log viewer only while a run is going. A finished run has per-step logs, and the full log is a download rather than a second copy of every step's output.
server/workflows_test.go +9−14
| @@ -1,6 +1,7 @@ | |||
| 1 | 1 | package server_test | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | "net/http" | ||
| 4 | 5 | "net/url" | |
| 5 | 6 | "strings" | |
| 6 | 7 | "testing" | |
| @@ -9,7 +10,7 @@ import ( | |||
| 9 | 10 | "github.com/rodent-software/vole/ci/runs" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| 12 | func TestWorkflowListAndRunActions(t *testing.T) { | ||
| 13 | func TestRunPageLinksWorkflowAndProvidesRunActions(t *testing.T) { | ||
| 13 | 14 | t.Parallel() | |
| 14 | 15 | f := newFixture(t) | |
| 15 | 16 | f.publish(f.repo) | |
| @@ -50,6 +51,13 @@ jobs: | |||
| 50 | 51 | t.Fatalf("run page does not show %q:\n%s", want, body) | |
| 51 | 52 | } | |
| 52 | 53 | } | |
| 54 | workflowURL := "/r/" + f.repo + "/blob/" + pushed.SHA + "/.vole/workflows/ci.yaml" | ||
| 55 | if !strings.Contains(body, `href="`+workflowURL+`"`) { | ||
| 56 | t.Fatalf("run page does not link the workflow definition at %s:\n%s", workflowURL, body) | ||
| 57 | } | ||
| 58 | if code, _ := f.get("/r/" + f.repo + "/workflows"); code != http.StatusNotFound { | ||
| 59 | t.Fatalf("the removed workflows page = %d, want 404", code) | ||
| 60 | } | ||
| 53 | 61 | ||
| 54 | 62 | enqueue := func(id, status string) { | |
| 55 | 63 | run := runs.Run{ | |
| @@ -68,19 +76,6 @@ jobs: | |||
| 68 | 76 | } | |
| 69 | 77 | enqueue("01M5R8XR12EZ5WYJDM0F1928D1", runs.StatusQueued) | |
| 70 | 78 | ||
| 71 | code, body = f.get("/r/" + f.repo + "/workflows") | ||
| 72 | if code != 200 { | ||
| 73 | t.Fatalf("workflows page = %d", code) | ||
| 74 | } | ||
| 75 | for _, want := range []string{ | ||
| 76 | "Verify main", "Build and test every push.", ".vole/workflows/ci.yaml", | ||
| 77 | "Unit tests", "Runs the test suite.", "test", "alpine:3.22", "any runner", "queued", | ||
| 78 | } { | ||
| 79 | if !strings.Contains(body, want) { | ||
| 80 | t.Fatalf("workflows page does not show %q:\n%s", want, body) | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | 79 | sess, cookie := f.browserSession(t, f.ownerID) | |
| 85 | 80 | resp, body := f.browserGet(t, "/r/"+f.repo+"/runs/01M5R8XR12EZ5WYJDM0F1928D1", cookie) | |
| 86 | 81 | if resp.StatusCode != 200 || !strings.Contains(body, "Cancel queued run") || strings.Contains(body, ">Rerun<") { | |