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.
Add packaging, a CI workflow, and containerised local runs
3b39a84c913cabe15bfa6a60dd176ff2dd28d241 · Keenan Nemetz · 15d ago · parent 30a72490
Add packaging, a CI workflow, and containerised local runs A v* tag now builds a .deb and a .rpm for amd64 and arm64 with nfpm and attaches them, with checksums, to the release. The package ships the systemd unit, a config at /etc/vole/vole.toml, and a postinstall that creates the service account and prepares the instance that config names; `vole admin init --config` is what makes that possible, and the binary's version is stamped from the tag instead of the source tree. scripts/ci.sh becomes .vole/workflows/ci.yaml, so `vole workflow run ci` executes exactly what a push runs, and the Markdown link check moves to scripts/check-links.py. Local runs now always execute in the job image and share ci/container with the runner; --no-container is gone, because a run in a different environment proves nothing about CI. The runner mounts a per-repository namespace at /cache and sets no variable for it: a job opts in through its image's env or the new job-level env, which is pinned with the queue and delivered with the lease. Whole namespaces are evicted least-recently-written first so a module cache is never half-deleted, and contributor branches get their own namespace so an untrusted tip cannot seed the cache a protected branch builds into. Running the suite in that container found four bugs worth naming: /tmp was mounted noexec, which broke `go test` and `go run`; the read-only root is gone, because images make toolchain directories world-writable deliberately and the mount set is what protects the host; OpenSSH needs a passwd entry for the job's uid; and a lost metadata push reported as "cannot lock ref" was not classified as a race, so concurrent CLI writes gave up on the first collision. That retry now backs off exponentially with jitter.
packaging/vole.toml +23
| @@ -0,0 +1,23 @@ | |||
| 1 | # Configuration for a packaged instance. The package installs this at | ||
| 2 | # /etc/vole/vole.toml, and `vole admin init --config /etc/vole/vole.toml` uses it | ||
| 3 | # to create the repository tree and user database it names. | ||
| 4 | data_dir = "/var/lib/vole/data" | ||
| 5 | |||
| 6 | # Read-only: the web UI and anonymous git clone. | ||
| 7 | # TLS is not Vole's job: terminate it in a proxy in front of this address and keep | ||
| 8 | # the Host header intact, because site requests are routed by hostname. | ||
| 9 | http_addr = "127.0.0.1:8080" | ||
| 10 | |||
| 11 | # The only write transport. Identity is the SSH key presented at handshake. | ||
| 12 | # 2222 keeps this listener off sshd's port; open it in the firewall. | ||
| 13 | ssh_addr = "0.0.0.0:2222" | ||
| 14 | |||
| 15 | # Where a browser reads this instance. Set it to the canonical https URL to | ||
| 16 | # enable passkey login; WebAuthn is bound to this origin. The CLI also uses it to | ||
| 17 | # print the page for something it just created. Trailing slash is ignored. | ||
| 18 | # public_url = "https://forge.example.com" | ||
| 19 | |||
| 20 | # Let anyone create an account by registering a passkey. New accounts have no | ||
| 21 | # repository-specific access; each repository's default role still applies. | ||
| 22 | # Off unless you are running an instance that wants public contributions. | ||
| 23 | # open_registration = true | ||