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.
Answer whoami --role from the repository's rules
eec0e60344bf5686fc278ba5a8415dc0777ba9e2 · Keenan Nemetz · 6d ago · parent f422c24f
Answer whoami --role from the repository's rules A role is per repository, so whoami cannot print one truth without knowing which repository is meant. Keep the default answer name-only, for the scripts that call it, and make the role an explicit query on the remote's repository. The server's vole-whoami verb now accepts a repository path and returns the effective role after the same private-repository rule as a fetch. A run credential may ask only about its own repository. Update the CLI help and the contributor and account docs to point at the new query.
client/remote.go +28
| @@ -11,6 +11,7 @@ import ( | |||
| 11 | 11 | "strings" | |
| 12 | 12 | ||
| 13 | 13 | voleid "github.com/rodent-software/vole/core/id" | |
| 14 | "github.com/rodent-software/vole/core/role" | ||
| 14 | 15 | ) | |
| 15 | 16 | ||
| 16 | 17 | // HostKeys is how this process verifies the SSH host key of the instance it talks | |
| @@ -250,6 +251,33 @@ func AskIdentity(gitDir, remote string) (*Actor, error) { | |||
| 250 | 251 | return a, nil | |
| 251 | 252 | } | |
| 252 | 253 | ||
| 254 | // AskRole asks the instance for the effective role the key holds on the | ||
| 255 | // repository named by remote. The default whoami answer stays the account name | ||
| 256 | // alone; this is the explicit query for the role that decides where the account | ||
| 257 | // may push and whether it may approve. | ||
| 258 | func AskRole(gitDir, remote string) (role.Role, error) { | ||
| 259 | host, port, path, err := SSHTarget(gitDir, remote) | ||
| 260 | if err != nil { | ||
| 261 | return role.None, err | ||
| 262 | } | ||
| 263 | if RepoNameFromPath(path) == "" { | ||
| 264 | return role.None, fmt.Errorf("%s does not name a repository", remote) | ||
| 265 | } | ||
| 266 | out, err := RunSSH(host, port, "vole-whoami "+quoteSSHArg(path)) | ||
| 267 | if err != nil { | ||
| 268 | return role.None, err | ||
| 269 | } | ||
| 270 | fields := strings.Split(strings.TrimSpace(out), "\t") | ||
| 271 | if len(fields) < 3 { | ||
| 272 | return role.None, fmt.Errorf("whoami: the instance did not answer with a role; it may be older than this client") | ||
| 273 | } | ||
| 274 | r, err := role.Parse(fields[2]) | ||
| 275 | if err != nil { | ||
| 276 | return role.None, fmt.Errorf("whoami: %w", err) | ||
| 277 | } | ||
| 278 | return r, nil | ||
| 279 | } | ||
| 280 | |||
| 253 | 281 | // ResolveActor asks the instance who the pushing key is, falling back to the name | |
| 254 | 282 | // git config suggests so that `--no-push` on a train still commits something. The | |
| 255 | 283 | // fallback is loudly reported: a wrong author is worse than a slow command. | |