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.
Name the configured host in the SSH clone link
268b93688db5dd045cf92f61dc0922919b074fc6 · Keenan Nemetz · 19h ago · parent 2c988ecd
Name the configured host in the SSH clone link ssh_addr is a bind address, so an instance listening on 0.0.0.0 showed ssh://git@0.0.0.0:2222/... in the clone menu. Take the host from public_url, which is the configured name, and keep ssh_addr for the port. The instance repository page reads the same helper.
server/web/codebrowser.go +29−5
| @@ -11,6 +11,7 @@ import ( | |||
| 11 | 11 | "fmt" | |
| 12 | 12 | "html/template" | |
| 13 | 13 | "io" | |
| 14 | "net" | ||
| 14 | 15 | "net/http" | |
| 15 | 16 | "sort" | |
| 16 | 17 | "strconv" | |
| @@ -18,6 +19,7 @@ import ( | |||
| 18 | 19 | "unicode/utf8" | |
| 19 | 20 | ||
| 20 | 21 | "github.com/rodent-software/vole/core/git" | |
| 22 | "github.com/rodent-software/vole/forge/config" | ||
| 21 | 23 | "github.com/rodent-software/vole/forge/repo" | |
| 22 | 24 | ) | |
| 23 | 25 | ||
| @@ -424,17 +426,39 @@ func (w *Web) siteURL(r *http.Request, name string) string { | |||
| 424 | 426 | return scheme + "://" + name + "." + w.Cfg.PagesDomain | |
| 425 | 427 | } | |
| 426 | 428 | ||
| 427 | func sshURL(addr, name string) string { | ||
| 428 | host, port, ok := strings.Cut(addr, ":") | ||
| 429 | if !ok { | ||
| 430 | host = addr | ||
| 429 | // sshURL is the SSH transport the repository header offers. The host is the | ||
| 430 | // instance's configured name, from public_url, when there is one: ssh_addr is | ||
| 431 | // the address the SSH listener binds, and a bind address such as 0.0.0.0 is not | ||
| 432 | // something a person can connect to. The port always comes from ssh_addr, | ||
| 433 | // because that is the port the listener answers on. Without a public_url there | ||
| 434 | // is no configured name, so the listener's own address is all there is. | ||
| 435 | func (w *Web) sshURL(name string) string { | ||
| 436 | host, port := "", "" | ||
| 437 | if w.Cfg != nil { | ||
| 438 | host, port = splitSSHAddr(w.Cfg.SSHAddr) | ||
| 439 | if u, err := config.ParsePublicURL(w.Cfg.PublicURL); err == nil && u != nil { | ||
| 440 | host = u.Hostname() | ||
| 441 | } | ||
| 431 | 442 | } | |
| 432 | 443 | return fmt.Sprintf("ssh://git@%s/%s.git", netJoin(host, port), name) | |
| 433 | 444 | } | |
| 434 | 445 | ||
| 446 | // splitSSHAddr separates a listen address into the host and the port, tolerating | ||
| 447 | // a bare address with no port the way config validation does. An IPv6 host is | ||
| 448 | // returned bare; netJoin puts the brackets back. | ||
| 449 | func splitSSHAddr(addr string) (host, port string) { | ||
| 450 | if h, p, err := net.SplitHostPort(addr); err == nil { | ||
| 451 | return h, p | ||
| 452 | } | ||
| 453 | return addr, "" | ||
| 454 | } | ||
| 455 | |||
| 435 | 456 | func netJoin(host, port string) string { | |
| 436 | 457 | if port == "" || port == "22" { | |
| 458 | if strings.Contains(host, ":") { | ||
| 459 | return "[" + host + "]" | ||
| 460 | } | ||
| 437 | 461 | return host | |
| 438 | 462 | } | |
| 439 | return host + ":" + port | ||
| 463 | return net.JoinHostPort(host, port) | ||
| 440 | 464 | } | |