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.
Test the merge result before landing it
e5478085e5b53a2892d0649d6f07c6f0a5653378 · Keenan Nemetz · 11d ago · parent db5e4382
Test the merge result before landing it A protected branch could receive a non-fast-forward landing whose tree was never built: the gate asked for checks on the request head, and the merge commit was made at landing time. A pushed merge with the right parents but extra content was accepted too. Publish the merge commit under refs/vole/candidates/, test it as the base branch, and make the gate require its results. Approvals name the head and the base branch, so a moved base retests without a new approval. A merger who clicks while that run is still going waits for it instead of clicking again. A pushed merge commit now has to carry exactly the tree a clean merge would produce. Closes: 01M61QQ56TESQ1HTMB1Z5C3733
docs/change-requests.md +50−5
| @@ -126,11 +126,55 @@ its issues open. | |||
| 126 | 126 | ||
| 127 | 127 | ## How approvals work | |
| 128 | 128 | ||
| 129 | A protected branch only accepts an update if the new tip is the approved commit, | ||
| 130 | or a merge of that commit with the current branch. That is why "merge, then push" | ||
| 131 | works, but adding an extra commit after approval does not. Because a request follows | ||
| 132 | its branch, pushing that extra commit moves the request's head and leaves the old | ||
| 133 | approval naming a commit that is no longer the head. | ||
| 129 | A protected branch only accepts an update that carries the reviewed head. When | ||
| 130 | nothing else has landed, the update is the head itself. When the branch has moved, | ||
| 131 | Vole builds the merge, tests that, and lands the commit it tested. | ||
| 132 | |||
| 133 | The approval names the base branch and the head, not the commit the base currently | ||
| 134 | points at. A base move therefore retests the combined result but does not invalidate | ||
| 135 | the approval: nobody has to rebase or re-approve just because something else landed | ||
| 136 | first. | ||
| 137 | |||
| 138 | Adding a commit to the branch after approval still does not carry the old approval. | ||
| 139 | The request follows its branch, so the push moves its head and the approval names a | ||
| 140 | commit that is no longer the head. | ||
| 141 | |||
| 142 | "Merge, then push" is accepted only when the pushed merge commit has the tree a | ||
| 143 | clean merge of the branch tip and the reviewed head would produce, and the combined | ||
| 144 | result has passed. A merge commit with extra content, or with conflicts resolved by | ||
| 145 | hand, is refused: it is content nobody reviewed under that approval. The **Merge** | ||
| 146 | button and `vole change merge` are the supported path. | ||
| 147 | |||
| 148 | ## The combined result | ||
| 149 | |||
| 150 | For a landing that needs a merge commit, Vole builds it and runs the repository's | ||
| 151 | workflows against that tree. A combined result is tested when a head is pushed, and | ||
| 152 | again when the base moves if the request is under review or already waiting to | ||
| 153 | merge. The server looks at open requests on a short interval, so a burst of | ||
| 154 | work-in-progress pushes settles on the head that is actually there before a run is | ||
| 155 | queued. | ||
| 156 | |||
| 157 | The combined result uses the workflow files at the merge, so a workflow change on | ||
| 158 | the base applies to the landing. It is a normal run for the base branch, but it does | ||
| 159 | not deploy the repository's site. A combined result derived from a contributor | ||
| 160 | branch is still untrusted code: it runs only after that exact head was allowed, and | ||
| 161 | it uses the contributor's cache namespace. | ||
| 162 | |||
| 163 | The request page shows one status: | ||
| 164 | |||
| 165 | - **Passed with the latest main** — Merge lands immediately. | ||
| 166 | - **Testing the merge with the latest main** — Merge becomes **Merge when ready** | ||
| 167 | and lands as soon as the run passes. | ||
| 168 | - **The combined result failed** — the runs name the job. Pushing a fix starts a new | ||
| 169 | review, because the head changes. | ||
| 170 | - **Conflicts with the latest main** — resolve them on the branch; the server does | ||
| 171 | not invent a resolution. | ||
| 172 | |||
| 173 | **Merge when ready** survives the base moving. It stops when the head changes, the | ||
| 174 | request closes, the actor loses merge permission, or the request conflicts. Every | ||
| 175 | landing re-checks the approvals, the actor's permission, and the combined result at | ||
| 176 | the moment the branch moves. `vole change merge` does the same thing from the | ||
| 177 | terminal, and `vole change show` prints the combined result and whether it is ready. | ||
| 134 | 178 | ||
| 135 | 179 | An approval can also be recorded from the request page in the browser when the | |
| 136 | 180 | signed-in account has maintain access. The approver is the session's account name, not | |
| @@ -164,6 +208,7 @@ Together they show: | |||
| 164 | 208 | ||
| 165 | 209 | - the changes compared with the branch it would merge into, | |
| 166 | 210 | - CI checks for the head commit, | |
| 211 | - the combined result that would land, and whether it has passed, | ||
| 167 | 212 | - how many approvals are required and who approved which commit, | |
| 168 | 213 | - whether the branch still merges cleanly. | |
| 169 | 214 | ||