Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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.

Let the CLI do what the browser does for the same account

7e0b5950c190caf11089e1aaac10f18678074fed · Keenan Nemetz · 6d ago · parent 6e1fca51

Let the CLI do what the browser does for the same account

A contributor could file an issue, comment, manage the item it wrote, and open a
change from its own branch — but only in the browser, because a push to
`refs/heads/vole` required `maintain`. The role was not the reason: the hook had
no per-path authorization for that branch, so the role gate stood in for one.

The per-path checker is now applied to every session, not only to run
credentials. It asks the ACL the questions the browser's forms ask —
CanDiscuss, CanOpenRequest, CanTriage, CanApprove — so a contributor's key can
write what a contributor may write, and a maintainer's key is unchanged. A
non-maintainer's new file must name that account as its author, which is what the
browser does for it; a maintainer keeps writing an imported issue under its
original author, because that is a trust decision rather than a capability.

Creating the issues branch still needs a maintainer, as it does from the browser,
and an approval still cannot approve its author's own change — the tree validator
already refused that, and now every transport gets the same answer. The one rule
that moved is self-approval for a maintainer's push, which is now refused earlier
rather than only at the landing gate.

With the branch no longer bundled into `maintain`, CONTRIBUTING and the guides stop
telling contributors to use the web. Both transports are one authorization model.

Part of #168, stacked on #167, which built the checker this generalises.

server/hook/hook_test.go +7−1

@@ -153,7 +153,6 @@ func TestContributorCanPushOnlyTheirNamespace(t *testing.T) {
153 153 "refs/heads/main",
154 154 "refs/heads/contrib/someone-else/topic",
155 155 "refs/heads/contrib/contributor",
156 "refs/heads/vole",
157 156 "refs/tags/contrib/contributor/v1",
158 157 } {
159 158 code, out := f.run("contributor", ref, 0)
@@ -164,6 +163,13 @@ func TestContributorCanPushOnlyTheirNamespace(t *testing.T) {
164 163 t.Fatalf("%s: unclear rejection: %s", ref, out)
165 164 }
166 165 }
166 // The issues branch is not a namespace rule: a contributor may write it. This push
167 // would create it, which is a maintainer's act, and that is the refusal it gets.
168 if code, out := f.run("contributor", "refs/heads/vole", 0); code == 0 {
169 t.Fatal("a contributor created the issues branch")
170 } else if !strings.Contains(out, "creating the issues branch needs a maintainer") {
171 t.Fatalf("unclear rejection: %s", out)
172 }
167 173 }
168 174
169 175 func TestInstanceRoleAppliesOnlyToPublicReposAndAdminEverywhere(t *testing.T) {