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.
cmd/vole/main.go +27
| @@ -64,6 +64,7 @@ Usage: | |||
| 64 | 64 | vole acl default <repo> <none|read|write|admin> | |
| 65 | 65 | vole acl protect <repo> <ref-pattern>... | |
| 66 | 66 | vole acl approvals <repo> <n> | |
| 67 | vole acl checks <repo> <on|off> | ||
| 67 | 68 | vole issue new --title "..." [-m text | -F file] [-l label]... | |
| 68 | 69 | vole issue comment <id> [-m text | -F file] | |
| 69 | 70 | vole issue close|reopen <id> | |
| @@ -563,6 +564,31 @@ func cmdACL(args []string) error { | |||
| 563 | 564 | fmt.Printf("protected: %s\n", strings.Join(a.Protected, ", ")) | |
| 564 | 565 | return nil | |
| 565 | 566 | ||
| 567 | case "checks": | ||
| 568 | if len(f.rest) != 2 { | ||
| 569 | return errors.New("usage: vole acl checks <repo> <on|off>") | ||
| 570 | } | ||
| 571 | switch strings.ToLower(f.rest[1]) { | ||
| 572 | case "on", "true", "yes", "1": | ||
| 573 | v := true | ||
| 574 | a.ChecksRequired = &v | ||
| 575 | case "off", "false", "no", "0": | ||
| 576 | v := false | ||
| 577 | a.ChecksRequired = &v | ||
| 578 | default: | ||
| 579 | return fmt.Errorf("%q is not on or off", f.rest[1]) | ||
| 580 | } | ||
| 581 | if err := m.SetACL(a, os.Getenv("USER")); err != nil { | ||
| 582 | return err | ||
| 583 | } | ||
| 584 | _ = st.index.Upsert(m) | ||
| 585 | if a.RequiresChecks() { | ||
| 586 | fmt.Println("checks: required on protected branches (a commit lands only after its CI jobs pass)") | ||
| 587 | } else { | ||
| 588 | fmt.Println("checks: not required; approvals alone allow a merge") | ||
| 589 | } | ||
| 590 | return nil | ||
| 591 | |||
| 566 | 592 | case "approvals": | |
| 567 | 593 | if len(f.rest) != 2 { | |
| 568 | 594 | return errors.New("usage: vole acl approvals <repo> <n>") | |
| @@ -646,6 +672,7 @@ func cmdHook(args []string) error { | |||
| 646 | 672 | MaxBlobBytes: maxBlob, | |
| 647 | 673 | AuditPath: os.Getenv("VOLE_AUDIT"), | |
| 648 | 674 | SystemRepo: os.Getenv("VOLE_SYSTEM_REPO"), | |
| 675 | RunsDB: os.Getenv("VOLE_RUNS_DB"), | ||
| 649 | 676 | In: os.Stdin, | |
| 650 | 677 | Out: os.Stderr, | |
| 651 | 678 | }) | |