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.
Create accounts with a key and find the instance config
c36d507302c0f7fb24af6eba390c4121d67318c3 · Keenan Nemetz · 16d ago · parent 3abbe749
Create accounts with a key and find the instance config An account is now written with its first key in one transaction, so a name cannot be claimed without a credential. add-key remains for additional keys, and remote user create forwards the key on stdin. Local commands now resolve the config from --config, VOLE_CONFIG, ./vole.toml, or ~/.vole/vole.toml before falling back to defaults. init prints --config in its next steps when the instance will not be discovered automatically.
forge/config/config.go +34
| @@ -94,6 +94,40 @@ func Load(path string) (*Config, error) { | |||
| 94 | 94 | return c, nil | |
| 95 | 95 | } | |
| 96 | 96 | ||
| 97 | // DefaultPath returns the config file to use when the operator did not name | ||
| 98 | // one. VOLE_CONFIG wins, then a vole.toml in the current directory, then the | ||
| 99 | // per-user instance at ~/.vole/vole.toml. If none exists it returns "vole.toml", | ||
| 100 | // which Load treats as the built-in defaults. | ||
| 101 | func DefaultPath() string { | ||
| 102 | if p := strings.TrimSpace(os.Getenv("VOLE_CONFIG")); p != "" { | ||
| 103 | return p | ||
| 104 | } | ||
| 105 | if fileExists("vole.toml") { | ||
| 106 | return "vole.toml" | ||
| 107 | } | ||
| 108 | if home, err := os.UserHomeDir(); err == nil { | ||
| 109 | p := filepath.Join(home, ".vole", "vole.toml") | ||
| 110 | if fileExists(p) { | ||
| 111 | return p | ||
| 112 | } | ||
| 113 | } | ||
| 114 | return "vole.toml" | ||
| 115 | } | ||
| 116 | |||
| 117 | // ResolvePath applies DefaultPath when path is empty, so a command that did not | ||
| 118 | // receive --config still finds the instance the operator initialised. | ||
| 119 | func ResolvePath(path string) string { | ||
| 120 | if path != "" { | ||
| 121 | return path | ||
| 122 | } | ||
| 123 | return DefaultPath() | ||
| 124 | } | ||
| 125 | |||
| 126 | func fileExists(path string) bool { | ||
| 127 | st, err := os.Stat(path) | ||
| 128 | return err == nil && !st.IsDir() | ||
| 129 | } | ||
| 130 | |||
| 97 | 131 | func (c *Config) ReposDir() string { return filepath.Join(c.DataDir, "repos") } | |
| 98 | 132 | ||
| 99 | 133 | // UsersDB is the authoritative user roster: accounts, the names they have ever | |