Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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.

Bound and reclaim the runner's saved checkouts

f594f1a16f6f8a8a3ee2867a44249b72a2c76c96 · Keenan Nemetz · 5d ago · parent de84c32b

Bound and reclaim the runner's saved checkouts

The runner already downloaded each repository once into --dir and cloned every
job from that copy, but nothing kept the copies bounded: they grew with every
tag, every branch, and every commit a job pinned by id, and a force-push left the
old history reachable through the remote-tracking reflogs. A long-lived runner
filled its disk and then could not check anything out at all.

Repack a copy that is most of the cap, dropping the objects no branch or tag can
reach and the temporary refs a killed runner leaves behind; then delete the least
recently used copy when the cap is still reached. --git-cache-size sets the cap
(10G by default, 0 keeps every copy). The repository this runner is about to
build is never the one deleted, and a copy another process holds is passed over.

Reading and writing a saved checkout now takes a per-repository lock, so a repack
or a deletion in one runner process cannot run underneath another's clone, or
underneath the site and release pushes that are written from the same cache.
The first clone skips the working-tree checkout, which nothing ever read.

docs/runner.md +28−3

@@ -132,7 +132,9 @@ instance or kept on the runner, so each of those channels has a bound:
132 132 evicting whole namespaces. Each repository gets its own namespace, and each
133 133 contributor account gets its own inside a repository, so one contributor's
134 134 branch cannot seed the cache another contributor's branch builds from. A
135 protected branch never shares a cache with a contributor branch.
135 protected branch never shares a cache with a contributor branch. The saved
136 checkouts jobs clone from are bounded separately; see
137 [Saved checkouts](#saved-checkouts).
136 138 - **Container.** `--container-memory`, `--container-cpus` and `--container-pids`
137 139 pass the runtime's own limits. `--container-network` is what this machine can
138 140 offer — `none` (the default) or `bridge` — and a job's own `network:` decides what
@@ -165,8 +167,9 @@ that build. Prefer rootless Podman, and pin the base image by digest.
165 167 One container is created per job and removed afterward. The workflow's steps run
166 168 inside it, but the runner process stays on the host: the SSH key, the trusted Git
167 169 cache, and the finish channel are never exposed to the job. Each job gets a fresh
168 clone, so a job cannot leave a Git hook or a modified checkout behind for the next
169 one.
170 clone of the runner's saved copy of the repository (see
171 [Saved checkouts](#saved-checkouts)), so a job cannot leave a Git hook or a modified
172 checkout behind for the next one.
170 173
171 174 Container defaults are intentionally closed:
172 175
@@ -331,6 +334,28 @@ the runner trims at startup and after each job. `--no-cache` keeps nothing
331 334 between jobs: the directory is still mounted — an image that points at `/cache`
332 335 has to work either way — but it is emptied after every job.
333 336
337 ## Saved checkouts
338
339 The first job for a repository downloads it once, and the runner keeps that copy
340 under `--dir`. A later job clones from the copy and fetches only the commits it does
341 not have — usually none at all — so history crosses the network once instead of once
342 per run. The copy is never mounted into a job: the workflow gets a clone with its own
343 `.git`, which is why a hook it writes still dies with the job.
344
345 Two things keep the copies from filling the disk:
346
347 - **Repacking.** A copy that is most of the cap is repacked. That drops history no
348 branch or tag can reach any more: commits from a force-push or a deleted branch,
349 and a commit a job once pinned by id. Nothing a job can still ask for is touched,
350 so the next job for the same revision is still a local copy with no download.
351 - **Deleting.** When the cap is still reached, the copy nobody has built from in the
352 longest time is deleted, whole. The next job for that repository downloads it
353 again, which is why it is the last resort rather than the first.
354
355 `--git-cache-size` caps all saved checkouts together (10G by default; `0` keeps
356 every one). A copy another runner process is using is passed over, and the
357 repository this runner is about to build is never the one deleted.
358
334 359 ## Who can run jobs and report results
335 360
336 361 A job runs repository code, so Vole uses a runner allowlist for both taking a job