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.
Publish repositories as Go modules and move the module path to rodent.software
d37d301fd4881ee37855387377db5a7aed843017 · Keenan Nemetz · 17h ago · parent 1c363d53
Publish repositories as Go modules and move the module path to rodent.software The module named a GitHub repository that does not exist, and a module path under the forge could not be fetched anyway: the go command resolves an import path by asking it for a go-import tag first, and the forge served only pages. Answer the <host>/<repository> discovery probe from the catch-all route with the repository's tag, naming its Git transport and that prefix, so a public repository is importable with no vanity service in front of the instance. Only a repository an anonymous reader can read answers: the tag is fetched without credentials, so a tag for a private repository would confirm its existence to anyone who guessed the name. The prefix host comes from public_url, never the request's Host header, for the same reason the clone URL does. With the path resolvable, rename the module to rodent.software/vole: imports, the build line in the getting-started guide, and the packaged units and metadata follow it. docs/go-modules.md explains the lookup, what the instance must have, and why a repository named after a route cannot claim the prefix.
docs/go-modules.md +84
| @@ -0,0 +1,84 @@ | |||
| 1 | # Go modules | ||
| 2 | |||
| 3 | A public repository on a Vole instance is a Go module. The import path is the | ||
| 4 | instance's host and the repository name: | ||
| 5 | |||
| 6 | ```text | ||
| 7 | https://<host>/<repository> | ||
| 8 | ``` | ||
| 9 | |||
| 10 | Vole serves the metadata the `go` command looks for, so an import path needs no | ||
| 11 | vanity service, no redirect, and no separate web host in front of the forge. | ||
| 12 | Vole itself is published this way: `rodent.software/vole`. | ||
| 13 | |||
| 14 | ## How the lookup works | ||
| 15 | |||
| 16 | `go get` cannot clone an arbitrary URL from an import path, so it first asks the | ||
| 17 | path for directions: it fetches `https://<host>/<repository>?go-get=1` and reads | ||
| 18 | the `<meta name="go-import">` tag in the answer. Vole answers for a public | ||
| 19 | repository with that repository's Git transport: | ||
| 20 | |||
| 21 | ```console | ||
| 22 | $ curl -s 'https://rodent.software/vole?go-get=1' | ||
| 23 | <!doctype html> | ||
| 24 | ... | ||
| 25 | <meta name="go-import" content="rodent.software/vole git https://rodent.software/git/vole.git"> | ||
| 26 | ``` | ||
| 27 | |||
| 28 | Then `go` clones the repository over the smart-HTTP transport and resolves the | ||
| 29 | package from the tag it finds there, exactly as it would for a module on any | ||
| 30 | other forge. A package inside the module (`rodent.software/vole/server/web`) | ||
| 31 | resolves from the same tag: the go command asks the full path first, and the tag | ||
| 32 | it gets names the repository prefix. | ||
| 33 | |||
| 34 | Add the repository to a project as usual: | ||
| 35 | |||
| 36 | ```console | ||
| 37 | $ go get rodent.software/vole | ||
| 38 | $ go doc rodent.software/vole/server/web | ||
| 39 | ``` | ||
| 40 | |||
| 41 | ## What the instance must have | ||
| 42 | |||
| 43 | - `public_url` set to the origin people use, in `[server]` in the | ||
| 44 | configuration file. The metadata's import prefix is that origin's host name; | ||
| 45 | without `public_url` Vole falls back to the request's host, which is not | ||
| 46 | something the go command's discovery can rely on. | ||
| 47 | - The host in the import path is the host name alone. A module path cannot carry | ||
| 48 | a port, so an instance reached only at `https://forge.example.com:8443` can | ||
| 49 | still serve its pages, but its repositories are not importable paths; clone | ||
| 50 | them by URL instead. | ||
| 51 | |||
| 52 | ## Which repositories answer | ||
| 53 | |||
| 54 | Only a repository an anonymous reader may read, which is the same rule that makes | ||
| 55 | its pages public. The tag is fetched without credentials — that is the discovery | ||
| 56 | protocol — so answering for a private repository would confirm to anyone who | ||
| 57 | guessed the name that it exists. Vole answers 404 for those, like every other | ||
| 58 | private path. | ||
| 59 | |||
| 60 | A private repository is therefore not resolvable from its import path. Clone it | ||
| 61 | where the build runs and point a `replace` at that checkout, or mirror the code in | ||
| 62 | a repository that is public. | ||
| 63 | |||
| 64 | ## Naming | ||
| 65 | |||
| 66 | The import path is `<host>/<repository>`, so the repository name is also the last | ||
| 67 | element of every import path inside the module. A repository named after a page | ||
| 68 | the forge already serves at the top level — `r`, `u`, `admin`, `activity`, | ||
| 69 | `login`, `static`, and so on — keeps its pages and its clone URL but cannot claim | ||
| 70 | that module path; pick another name if the code is meant to be imported. | ||
| 71 | |||
| 72 | ## Vole's own module | ||
| 73 | |||
| 74 | The Vole repository is `rodent.software/vole`, so fetching the source is: | ||
| 75 | |||
| 76 | ```console | ||
| 77 | $ go get rodent.software/vole | ||
| 78 | ``` | ||
| 79 | |||
| 80 | and building from a checkout uses the same path: | ||
| 81 | |||
| 82 | ```sh | ||
| 83 | go build -tags libsqlite3,sqlite_fts5 -o vole rodent.software/vole/cmd/vole | ||
| 84 | ``` | ||