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.

ci/agent/agent.go +35

@@ -252,6 +252,12 @@ type Options struct {
252 252 NoCache bool
253 253 // CacheMaxBytes caps that cache. Zero means the default; see ci/cache.
254 254 CacheMaxBytes int64
255
256 // GitCacheMaxBytes caps the saved checkouts of the repositories this runner has
257 // built. A job clones from the saved checkout instead of downloading the history
258 // again, so the cap trades disk for network. Negative means the default, and zero
259 // keeps every checkout; see DefaultGitCacheBytes and gitcache.go.
260 GitCacheMaxBytes int64
255 261 }
256 262
257 263 // Run leases and executes CI jobs until the connection fails, the context ends,
@@ -282,6 +288,14 @@ func Run(ctx context.Context, opts Options) error {
282 288 }
283 289 cleanupJobTrees(cache)
284 290 cleanupStaleContainers(box)
291 // Bound the saved checkouts before work starts, the same way the job cache is
292 // bound below: a runner whose disk filled up while it was not watching should
293 // not start by failing a job.
294 gitCacheMax := opts.GitCacheMaxBytes
295 if gitCacheMax < 0 {
296 gitCacheMax = DefaultGitCacheBytes
297 }
298 maintainTrustedCaches(cache, gitCacheMax, "")
285 299
286 300 // The directory is mounted whether or not it is reused: an image that sets
287 301 // GOCACHE=/cache/go-build must work under --no-cache too, and a non-root job
@@ -396,8 +410,13 @@ func Run(ctx context.Context, opts Options) error {
396 410 }
397 411 sess.run(opts.Remote, cache, rep.Run, lease, box)
398 412 if opts.Once {
413 // This process is done; the next one bounds the cache at startup instead,
414 // before it leases anything.
399 415 return nil
400 416 }
417 // Tidy between jobs, never during one: the checkout this runner just used is
418 // the one the next job is most likely to want, so it is passed over as active.
419 maintainTrustedCaches(cache, gitCacheMax, rep.Run.Repo)
401 420 }
402 421 }
403 422
@@ -950,6 +969,14 @@ func publishReleases(remote string, co checkout, job *runnerJob, rel []runnerArt
950 969 if err != nil {
951 970 return err
952 971 }
972 // Opening the branch fetches into the trusted cache and the write adds objects to
973 // it. Hold the cache's lock so a repack or a deletion in another runner process
974 // cannot move the object store while this reads and writes it.
975 unlock, err := co.lock()
976 if err != nil {
977 return err
978 }
979 defer unlock()
953 980 c, err := client.OpenBranch(co.GitDir, url, strings.TrimPrefix(metadata.Ref, "refs/heads/"), "vole-runner")
954 981 if err != nil {
955 982 return err
@@ -1048,6 +1075,14 @@ func publishSite(remote string, co checkout, job *runnerJob, log io.Writer) erro
1048 1075 if err != nil {
1049 1076 return err
1050 1077 }
1078 // The snapshot is committed and pushed from the trusted cache, which this runner
1079 // shares with any other process given the same --dir. Take its lock, as the
1080 // release push and the maintenance pass do.
1081 unlock, err := co.lock()
1082 if err != nil {
1083 return err
1084 }
1085 defer unlock()
1051 1086 tip, err := client.LSRemote(co.GitDir, url, "refs/heads/"+job.Branch)
1052 1087 if err != nil {
1053 1088 return fmt.Errorf("check %s before deploying the site: %w", job.Branch, err)