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.
Add packaging, a CI workflow, and containerised local runs
3b39a84c913cabe15bfa6a60dd176ff2dd28d241 · Keenan Nemetz · 14d 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.
docs/reference/image-reference.md +19
| @@ -174,6 +174,25 @@ runs each step with `/bin/sh -c` and keeps the container alive between steps. If | |||
| 174 | 174 | `run` is non-empty, the base image used for `from` must also provide `/bin/sh`, | |
| 175 | 175 | because build commands are rendered that way. | |
| 176 | 176 | ||
| 177 | Nothing a job writes inside the container survives the run, except what it puts in | ||
| 178 | the cache: the runner mounts a directory at `/cache` and sets no variables for it, | ||
| 179 | so `env` is where a definition opts in. Point the toolchain's own variables at it | ||
| 180 | and its downloads are reused between jobs: | ||
| 181 | |||
| 182 | ```yaml | ||
| 183 | env: | ||
| 184 | GOMODCACHE: "/cache/go-mod" | ||
| 185 | GOCACHE: "/cache/go-build" | ||
| 186 | ``` | ||
| 187 | |||
| 188 | A job may also set its own `env`, which is applied after the image's and wins; it | ||
| 189 | is the way to point at `/cache` when the image is a registry reference rather than a | ||
| 190 | definition. `/tmp` is a memory-backed tmpfs and `HOME` points there for everything | ||
| 191 | else; the XDG convention (`XDG_CACHE_HOME: /cache/xdg`) covers tools that follow it. | ||
| 192 | See [Runners § Caching](../runner.md#caching). | ||
| 193 | Check that anything calling `getpwuid` has a passwd entry for the uid the job runs | ||
| 194 | as — see [Runners § How a job runs](../runner.md#how-a-job-runs). | ||
| 195 | |||
| 177 | 196 | ## Validation and pinning | |
| 178 | 197 | ||
| 179 | 198 | - `version` must be `1`. | |