Werkator imported every werkdock.rootfs into the store and never took one out: a replaced rootfs left its image behind, one to two GiB each. mih09 held two for weeks, and the disk trend read the steps as growth. BuildEnvironmentPruner removes the werkator-buildenv-* images whose source no repository's host configuration names (werkdock rmi) and the download caches of URL sources no longer named. The watcher runs it once per cycle for the whole instance, at most once an hour, never while a build executes. Images anything else pulled are not touched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #77
6.0 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
Werkator imports every werkdock.rootfs into werkdock's image store and never takes one out again.
A rootfs replaced by its successor — a new JDK, an added tool — leaves its image behind, one to two GiB each.
On mih09 the store held two such images for weeks: the predecessor of werkjournal's rootfs, and the pre-ssh variant of the shared one whose archive had already been deleted.
The System page's disk trend read the steps from imports and replacements as a disk growing half a gigabyte a day and advised an upgrade.
The same happens one level down: a URL rootfs is downloaded into .git/werkator/buildenv/<hash>/, and a changed URL leaves the old download there.
Non-Goals
- Not touching what Werkator did not import.
The store is shared by every process of the OS user; a
postgres:16-alpinea Testcontainers test pulled is that test's business. - No age-based expiry. What is stale is what no configuration names; an image referenced by a repository stays however old it is.
- No cleanup of sandbox homes (
buildenv/home) or of archives an operator placed underbuildenv/by hand.
The Scenarios
Feature: build environments no configuration names are removed
Background
- Werkator imports a
werkdock.rootfssource as the imagewerkator-buildenv-<hash>, the hash over the source string. werkdock.rootfsis pinned: a branch cannot name a source, so the host layers of the registered repositories are the complete set of references.
Scenario#77.01: Unreferenced images go, everything else stays
- Given a store holding an image for the current rootfs, one for a replaced rootfs, and images pulled by something else
- When the build environments are pruned with the current rootfs as the only reference
- Then the replaced rootfs's image is removed with
werkdock rmi- and the current one and the foreign images are not touched
Verified by
Scenario#77.02: A cleanup that fails fails alone
- Given a store that cannot be listed, or an image
rmirefuses - When the build environments are pruned
- Then nothing is thrown; the failure is logged and the next round tries again
Verified by
- BuildEnvironmentPrunerTest — an image that cannot be removed is skipped, not thrown
- BuildEnvironmentPrunerTest — a store that cannot be listed prunes nothing
Scenario#77.03: Download caches of unnamed sources go, the rest of buildenv/ stays
- Given
.git/werkator/buildenv/holding the cache of a replaced source, the cache of the current one, the sandbox home, and an archive placed there by hand - When the download caches are pruned
- Then only the replaced source's cache directory is removed
Verified by
Scenario#77.04: The watcher prunes once per cycle for the whole instance, at most hourly
- Given a repository whose definitions name a rootfs and a werkdock binary
- When two poll cycles run within an hour
- Then the images are pruned once, through the configured binary, with every definition's rootfs as reference
- and the repository's download caches are pruned once
Verified by
Scenario#77.05: Nothing is pruned while a build executes
So that a running sandbox never loses the image it is bound to.
Verified by
Scenario#77.06: A host without a sandbox is never asked for werkdock
So that a Docker or native host does not log a missing binary every hour.
Verified by
The Solution
BuildEnvironmentPruner does two things: pruneImages lists the store with werkdock images, keeps every werkator-buildenv-* name whose source is referenced, and removes the rest with werkdock rmi; pruneDownloadCaches removes the <hash>/ directories under a repository's buildenv/ whose hash no referenced source produces.
Both derive the names from the source strings exactly as the runner does — sourceKey and imageName moved to WerkdockBuildRunner's companion so there is one place that knows the naming.
The watcher calls it after every repository is polled, once per cycle for the whole instance, because the store belongs to the OS user and not to a repository.
At most once an hour — the store is asked through the CLI and changes when a configuration does — and never while a build executes, since a running sandbox binds its image.
The referenced sources are every definition's werkdock.rootfs from every repository's primary configuration.
A failure anywhere in the cleanup is logged and never reaches the poll cycle.
Additional Changes
- On mih09 the two stale images (1.6 GiB) and werkjournal's replaced rootfs archive (207 MiB) were removed by hand on 2026-09-17, ahead of this PR.
Follow-up PRs
.konan, the Kotlin/Native toolchain cache in keluna's sandbox home, could share a directory likewerkdock.gradleHomedoes (PR#76) once a second repository needs it.