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.
Phase 4 slice 3: checks on the gate, and statuses on the page
35eaeccffdab9d07e13fb7b346060e535697fee7 · Keenan Nemetz · 17d ago · parent b44cbfaf
Phase 4 slice 3: checks on the gate, and statuses on the page A protected branch now requires both an approved change request and passing checks for the commit being landed. Approvals say a person read the change; checks say it works. Neither substitutes for the other. The gate asks about the *approved head*, not about the merge commit somebody may be pushing: CI ran on the head, and a merge commit is a new object nobody built. That also keeps the gate consistent with what approvals already name. Details that decide whether this is usable: - A repository with no workflows is unaffected — nothing to run means nothing to wait for — so existing repositories do not start failing pushes. - A job with no run is refused with an explanation that names the branch filter, because `on: push: branches: [main]` never produces a result for a feature branch. That is the one configuration that can deadlock a merge, and the error says so instead of leaving somebody guessing. - Failed, cancelled, still-running and missing are four different messages; a reviewer should be able to tell which. - `vole acl checks <repo> on|off` turns it off, default on. Statuses are readable: the repository page lists recent runs (job, commit, status, attempts, age) and a change request shows its head's job statuses next to the approvals, so the reviewer sees the same facts the gate uses. Tests cover the three interesting shapes: approved + green lands; approved + red is refused naming the failed job; approved + no run at all is refused with the branch-filter hint. Verified on a live instance: pushing a workflow queues and runs it, the repository page shows `build · .vole/workflows/ci.yaml · success`, and the change request shows `wilbur · current` alongside `build · success`. Phase 4 is now complete except signed run records, which are Phase 7's.
internal/web/requests.go +22−1
| @@ -93,6 +93,26 @@ func (w *Web) RequestView(rw http.ResponseWriter, r *http.Request) { | |||
| 93 | 93 | Current: a.Head == req.Head && a.Base == req.Base, | |
| 94 | 94 | }) | |
| 95 | 95 | } | |
| 96 | // CI state for the commit under review: the same question the gate asks. | ||
| 97 | type checkRow struct { | ||
| 98 | Workflow, Job, Status string | ||
| 99 | Exit int | ||
| 100 | } | ||
| 101 | var checks []checkRow | ||
| 102 | if w.Runs != nil && req.Head != "" { | ||
| 103 | if res, err := w.Runs.ForSHA(m.Name, req.Head); err == nil { | ||
| 104 | for _, run := range res { | ||
| 105 | checks = append(checks, checkRow{run.Workflow, run.Job, run.Status, run.ExitCode}) | ||
| 106 | } | ||
| 107 | sort.Slice(checks, func(i, j int) bool { | ||
| 108 | if checks[i].Workflow != checks[j].Workflow { | ||
| 109 | return checks[i].Workflow < checks[j].Workflow | ||
| 110 | } | ||
| 111 | return checks[i].Job < checks[j].Job | ||
| 112 | }) | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 96 | 116 | // The diff is computed from git on every view: it is cheap for one branch and | |
| 97 | 117 | // always matches what the approver is being asked to approve. | |
| 98 | 118 | var changed []string | |
| @@ -112,8 +132,9 @@ func (w *Web) RequestView(rw http.ResponseWriter, r *http.Request) { | |||
| 112 | 132 | Request *requests.Request | |
| 113 | 133 | Description template.HTML | |
| 114 | 134 | Approvals []requestApproval | |
| 135 | Checks []checkRow | ||
| 115 | 136 | Changed []string | |
| 116 | 137 | AlreadyMerged bool | |
| 117 | 138 | }{Page: w.base(m.Name + " · " + req.Title), Name: m.Name, Request: req, | |
| 118 | Description: w.renderMarkdown(m.Name, req.Description), Approvals: approvals, Changed: changed, AlreadyMerged: alreadyMerged}, 0) | ||
| 139 | Description: w.renderMarkdown(m.Name, req.Description), Approvals: approvals, Checks: checks, Changed: changed, AlreadyMerged: alreadyMerged}, 0) | ||
| 119 | 140 | } | |