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.
Ship the runner's unit and let a deployment create a runner machine
d0d4239f3f7236197c87a752d7965de238430f98 · Keenan Nemetz · 3d ago · parent dbae2e01
Ship the runner's unit and let a deployment create a runner machine The package installs one binary that is the forge, the runner, and the CLI, so it now carries a second unit. It is not enabled, and it refuses to start with a message until an operator fills in /etc/vole/runner.env: who runs builds on a machine is that machine owner's decision. The unit runs as a dedicated account, because rootless podman needs a home directory and a range of subordinate ids, and because a job should not find the forge's own account on the same disk. The arguments live in that one file rather than in the unit, split at spaces by systemd, so a single unit serves every machine. Each Terraform configuration takes runner=true and creates that machine beside the forge: its own firewall, no backups (its checkouts and caches are copies), the same key for a shell, and Cloud-init that installs the package, creates the account and its subuids, generates the runner's key, pins the instance's host key once, and enables the unit. The runner_setup output prints the three commands that let it take work, filled in with the addresses this apply created. The first-boot file cannot register the runner: the allowlist is per repository, and at first boot there are no repositories. That step stays with an instance admin, and the docs say so.
deploy/README.md +8−2
| @@ -6,13 +6,19 @@ The files here put a Vole instance on a fresh machine. The guides are in | |||
| 6 | 6 | | Path | What it is | | |
| 7 | 7 | |---|---| | |
| 8 | 8 | | `cloud-init/vole.yaml` | The first-boot file. Installs the package, writes the configuration, creates the instance admin, starts the service. Used by every path below | | |
| 9 | | `cloud-init/render.sh` | Renders that file for a machine you create by hand, with no Terraform | | ||
| 10 | | `terraform/hetzner`, `terraform/digitalocean`, `terraform/aws` | One complete Terraform configuration per cloud: machine, firewall, address, and optional DNS record. Each is used as a root module | | ||
| 9 | | `cloud-init/runner.yaml` | The CI runner's first-boot file: rootless Podman, an unprivileged account, a pinned host key, and the unit enabled | | ||
| 10 | | `cloud-init/render.sh` | Renders either first-boot file for a machine you create by hand, with no Terraform | | ||
| 11 | | `terraform/hetzner`, `terraform/digitalocean`, `terraform/aws` | One complete Terraform configuration per cloud: machine, firewall, address, optional DNS record, and `-var runner=true` for a second machine that runs CI jobs. Each is used as a root module | | ||
| 11 | 12 | | `docker/Dockerfile` | The server in one image, built from this repository | | |
| 12 | 13 | | `docker/compose.yaml` | That image, with a volume for the instance and a health check | | |
| 13 | 14 | | `docker/vole.toml` | The configuration the image starts with, and the file to copy for a deployment | | |
| 14 | 15 | | `docker/entrypoint.sh` | Runs `vole admin init` once, then the command you asked for | | |
| 15 | 16 | ||
| 17 | The runner's unit and its configuration example ship in the package, not here: | ||
| 18 | `packaging/systemd/vole-runner.service` and `packaging/runner.env`. One binary is | ||
| 19 | the forge, the runner, the CLI, and the library, so one package carries both | ||
| 20 | units. | ||
| 21 | |||
| 16 | 22 | Two things are deliberate: | |
| 17 | 23 | ||
| 18 | 24 | - **The cloud-init file is the single source of truth.** The Terraform | |