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.
Show run durations and lazy step logs
c765ae2e18691b3f98d93b00b4eb418ce781e3f1 · Keenan Nemetz · 13d ago · parent bdf6a0de
server/integration_test.go +33−2
| @@ -2836,9 +2836,40 @@ jobs: | |||
| 2836 | 2836 | if rec.Log == nil || rec.Log.OID != done.LogOID || rec.Exit != 0 || rec.Job != done.Job { | |
| 2837 | 2837 | t.Fatalf("the record does not describe the run: %+v", rec) | |
| 2838 | 2838 | } | |
| 2839 | // The runner also records what each step did and where its output lives in the | ||
| 2840 | // archived log. A step range is a slice of the same bytes, not a second upload. | ||
| 2841 | if len(rec.Steps) != 1 || rec.Steps[0].Name != "say hello" { | ||
| 2842 | t.Fatalf("the record does not describe the step: %+v", rec.Steps) | ||
| 2843 | } | ||
| 2844 | if rec.Steps[0].LogSize == 0 || rec.Steps[0].Duration() <= 0 { | ||
| 2845 | t.Fatalf("the step has no timing or log range: %+v", rec.Steps[0]) | ||
| 2846 | } | ||
| 2847 | code, stepLog := f.get("/r/" + f.repo + "/runs/" + done.ID + "/log?step=1") | ||
| 2848 | if code != 200 || !strings.Contains(stepLog, "hello from the runner") { | ||
| 2849 | t.Fatalf("step log = %d, body:\n%s", code, stepLog) | ||
| 2850 | } | ||
| 2851 | if len(stepLog) >= len(body) { | ||
| 2852 | t.Fatalf("step log is %d bytes, no smaller than the whole %d-byte log", len(stepLog), len(body)) | ||
| 2853 | } | ||
| 2854 | // The archived object is served directly, so a range request works without the | ||
| 2855 | // server reading the whole log. | ||
| 2856 | req, err := http.NewRequest(http.MethodGet, f.http.URL+"/r/"+f.repo+"/runs/"+done.ID+"/log", nil) | ||
| 2857 | if err != nil { | ||
| 2858 | t.Fatal(err) | ||
| 2859 | } | ||
| 2860 | req.Header.Set("Range", "bytes=0-3") | ||
| 2861 | resp, err := http.DefaultClient.Do(req) | ||
| 2862 | if err != nil { | ||
| 2863 | t.Fatal(err) | ||
| 2864 | } | ||
| 2865 | defer resp.Body.Close() | ||
| 2866 | if resp.StatusCode != http.StatusPartialContent || resp.Header.Get("Content-Range") == "" { | ||
| 2867 | t.Fatalf("range request = %d, Content-Range %q", resp.StatusCode, resp.Header.Get("Content-Range")) | ||
| 2868 | } | ||
| 2839 | 2869 | // Attribution is the boundary's, and the page says who it accepted the result from. | |
| 2840 | if _, page := f.get("/r/" + f.repo + "/runs/" + done.ID); !strings.Contains(page, "reported by") { | ||
| 2841 | t.Fatalf("the run page does not say who reported the result:\n%s", page) | ||
| 2870 | if _, page := f.get("/r/" + f.repo + "/runs/" + done.ID); !strings.Contains(page, "reported by") || | ||
| 2871 | !strings.Contains(page, "say hello") || !strings.Contains(page, "?step=1") { | ||
| 2872 | t.Fatalf("the run page does not show the result, the step, or its log link:\n%s", page) | ||
| 2842 | 2873 | } | |
| 2843 | 2874 | if done.SHA != mustSHA(t, work) { | |
| 2844 | 2875 | t.Fatalf("run recorded sha %s, want the pushed commit", done.SHA) | |