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/terraform/aws/outputs.tf +14
| @@ -27,3 +27,17 @@ output "dns_record" { | |||
| 27 | 27 | description = "The record created when create_dns is true." | |
| 28 | 28 | value = var.create_dns ? "${var.domain} A ${aws_eip.vole.public_ip}" : "not managed here; point ${var.domain} at ${aws_eip.vole.public_ip}" | |
| 29 | 29 | } | |
| 30 | |||
| 31 | output "runner_ipv4" { | ||
| 32 | description = "The runner's public address, or null when runner is false. Its only use is a shell." | ||
| 33 | value = var.runner ? aws_instance.runner[0].public_ip : null | ||
| 34 | } | ||
| 35 | |||
| 36 | output "runner_setup" { | ||
| 37 | description = "The commands that let the runner take jobs. Run them from a machine that holds your admin key." | ||
| 38 | value = var.runner ? join("\n", [ | ||
| 39 | "ssh -i <private-key> ubuntu@${aws_instance.runner[0].public_ip} sudo cat /var/lib/vole-runner/.ssh/id_ed25519.pub | vole admin --remote ssh://git@${var.domain}:2222 account create ci-runner-1 --bot --key -", | ||
| 40 | "vole admin --remote ssh://git@${var.domain}:2222 acl runners add <repo> ci-runner-1", | ||
| 41 | "vole admin --remote ssh://git@${var.domain}:2222 acl set-role <repo> ci-runner-1 read", | ||
| 42 | ]) : "runner is false: pass -var runner=true to create one" | ||
| 43 | } | ||