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.
Refuse a protected-ref pattern that can never match
93c7cae8ebb7d6d853dca476851e2bd0cbe10079 · Keenan Nemetz · 19h ago · parent 2c988ecd
Refuse a protected-ref pattern that can never match acl protect stored any string: refs/head/main read like a protected branch in acl show while the hook compared it against refs/heads/main and matched nothing, so approvals and required checks were off on the branch everything lands on. Validate the pattern in forge/access, where the rule is stored, and call it from both acl protect add|set and the repository settings form. Only the two shapes IsProtected understands are accepted, exact ref and one trailing wildcard, the ref part must be a name git could store, and a singular category is named as the typo it is.
server/integration_test.go +54
| @@ -3854,6 +3854,60 @@ func TestACLCommandsNameTheFieldTheyChange(t *testing.T) { | |||
| 3854 | 3854 | } | |
| 3855 | 3855 | } | |
| 3856 | 3856 | ||
| 3857 | // TestACLProtectRefusesAPatternThatCannotMatch: protection is configured once and | ||
| 3858 | // assumed afterwards, so a typo in the category must fail at the command rather | ||
| 3859 | // than leave the branch open behind a line that reads like a correct one. | ||
| 3860 | func TestACLProtectRefusesAPatternThatCannotMatch(t *testing.T) { | ||
| 3861 | t.Parallel() | ||
| 3862 | f := newFixture(t) | ||
| 3863 | f.publish(f.repo) | ||
| 3864 | cfg := f.cfgFile(t) | ||
| 3865 | protected := func() string { | ||
| 3866 | t.Helper() | ||
| 3867 | a, err := mustRepo(t, f).ACL() | ||
| 3868 | if err != nil { | ||
| 3869 | t.Fatal(err) | ||
| 3870 | } | ||
| 3871 | return strings.Join(a.Protected, ",") | ||
| 3872 | } | ||
| 3873 | |||
| 3874 | _, errs, err := f.cli("", "acl", "protect", "add", f.repo, "refs/head/main", "--config", cfg) | ||
| 3875 | if err == nil { | ||
| 3876 | t.Fatalf("a pattern that cannot match was stored:\n%s", errs) | ||
| 3877 | } | ||
| 3878 | if !strings.Contains(errs, `refs/heads/main`) { | ||
| 3879 | t.Fatalf("the refusal does not name the fix:\n%s", errs) | ||
| 3880 | } | ||
| 3881 | if got := protected(); got != "" { | ||
| 3882 | t.Fatalf("the refusal stored %q", got) | ||
| 3883 | } | ||
| 3884 | |||
| 3885 | // One bad pattern in a list refuses the whole list rather than storing part | ||
| 3886 | // of it, which would read as a complete rule set. | ||
| 3887 | if _, errs, err := f.cli("", "acl", "protect", "add", f.repo, "refs/heads/main", "refs/heads/ma in", "--config", cfg); err == nil { | ||
| 3888 | t.Fatalf("a list with a bad pattern was accepted:\n%s", errs) | ||
| 3889 | } | ||
| 3890 | if got := protected(); got != "" { | ||
| 3891 | t.Fatalf("a refused list stored part of itself: %q", got) | ||
| 3892 | } | ||
| 3893 | if _, errs, err := f.cli("", "acl", "protect", "set", f.repo, "heads/main", "--config", cfg); err == nil { | ||
| 3894 | t.Fatalf("protect set stored a pattern that cannot match:\n%s", errs) | ||
| 3895 | } | ||
| 3896 | |||
| 3897 | for _, pattern := range []string{"refs/heads/main", "refs/heads/release/*"} { | ||
| 3898 | if _, errs, err := f.cli("", "acl", "protect", "add", f.repo, pattern, "--config", cfg); err != nil { | ||
| 3899 | t.Fatalf("%s: %v\n%s", pattern, err, errs) | ||
| 3900 | } | ||
| 3901 | } | ||
| 3902 | if got, want := protected(), "refs/heads/main,refs/heads/release/*"; got != want { | ||
| 3903 | t.Fatalf("protected = %q, want %q", got, want) | ||
| 3904 | } | ||
| 3905 | out, _, err := f.cli("", "acl", "show", f.repo, "--config", cfg) | ||
| 3906 | if err != nil || !strings.Contains(out, "refs/heads/release/*") { | ||
| 3907 | t.Fatalf("acl show does not round-trip the pattern:\n%s", out) | ||
| 3908 | } | ||
| 3909 | } | ||
| 3910 | |||
| 3857 | 3911 | // TestFailingCheckBlocksTheMerge: a green approval does not outvote a red build. | |
| 3858 | 3912 | func TestFailingCheckBlocksTheMerge(t *testing.T) { | |
| 3859 | 3913 | t.Parallel() | |