Every sandboxed build kept its own Gradle home under /root: on mih09 six repositories held six dependency caches and six provisioned JDKs, about 5 GiB of a 16 GiB SSD. The Docker runner has had a persistent Gradle volume since step 11; the werkdock runner never got the equivalent. werkdock.gradleHome names a host directory that every sandbox naming the same path mounts as /gradle-user-home, with GRADLE_USER_HOME pointing there — the same pair the Docker runner uses. Beside /root rather than over /root/.gradle, so a leftover per-repository cache is unused, not hidden. Pinned and opt-in: the path is mounted read-write, and a shared cache is shared trust. Set once in the defaults block of ~/.werkator.yml. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #76
5.6 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
Every sandboxed build gets its own persistent home under .git/werkator/buildenv/home, mounted as /root, and with it its own Gradle user home.
On mih09 that is six repositories with six dependency caches, six provisioned JDKs and six Gradle wrapper distributions, mostly of the same versions: about 5 GiB of a 16 GiB SSD, growing with every repository added.
The Docker runner has had a persistent Gradle volume since step 11; the werkdock runner never got the equivalent, because per-repository isolation was the simpler default.
Non-Goals
- No sharing of anything else in the sandbox home:
.konan,.npm,.vaadin, SSH keys stay per repository. - No change to the Docker runner's per-repository volume.
- No migration of existing per-repository caches: a
.gradleleft under/rootstays where it is, unused, until deleted by hand. - Not the default. See the trust note under The Solution.
The Scenarios
Feature: one Gradle user home for all sandboxes of a host
Background
werkdock.gradleHomenames a directory on the host; empty (the default) keeps the per-repository Gradle home under/root.- Gradle locks its caches for concurrent use by several builds on one machine.
Scenario#76.01: A configured Gradle home is mounted and announced
So that Gradle inside the sandbox uses the shared cache and nothing else has to know about it.
- Given a build with
werkdock.gradleHomeset - When the sandbox is started
- Then the directory is mounted read-write as
/gradle-user-home, after the/rootmount- and
GRADLE_USER_HOME=/gradle-user-homeis set inside the sandbox - and the directory exists on the host before the command starts
- and
Verified by
Scenario#76.02: Without the key nothing changes
- Given a build without
werkdock.gradleHome - When the sandbox is started
- Then no extra mount and no
GRADLE_USER_HOMEare added; Gradle keeps its cache under/root
Verified by
Scenario#76.03: A branch cannot choose the directory
So that a branch cannot mount a host directory of its choosing read-write into its sandbox.
- Given a host configuration without
werkdock.gradleHome- and a branch whose committed configuration sets it to
/home/ci/.ssh
- and a branch whose committed configuration sets it to
- When the branch's build settings are resolved
- Then the key is empty: the branch's value is stripped like the other pinned werkdock keys
Verified by
- ConfigLoaderTest — a branch cannot disable its werkdock sandbox or substitute a foreign rootfs through a build definition
- BuildDefinitionTest — every docker and werkdock setting can be said in a build definition (the structural guard from PR#72, which is what made adding the key to
WerkdockOverridesmandatory)
The Solution
One new pinned key, werkdock.gradleHome.
When set, WerkdockBuildRunner creates the directory, adds -v <dir>:/gradle-user-home after the /root mount and -e GRADLE_USER_HOME=/gradle-user-home — the same pair the Docker runner has used with its volume since step 11.
The mount goes to its own path, not to /root/.gradle.
Mounting over /root/.gradle would have shadowed a per-repository cache that is still there; mounting beside it leaves that cache visible and unused, so it can be deleted at leisure and nothing is hidden from du.
Why opt-in and pinned. The path is a host directory mounted read-write into a sandbox whose whole point is filesystem isolation, so a branch that could set it could reach any directory of the host user. And a shared cache is shared trust: a build that can write it can feed a tampered dependency to every other repository on the host. Both are host decisions. On mih09 every repository belongs to the same person; on a host serving several parties the key should stay empty, or point to one directory per party.
Rollout on mih09.
One line in ~/.werkator.yml, so that every repository gets it without touching six fragments:
defaults:
builds:
default:
werkdock:
gradleHome: /home/pacs/mih09/users/werkator/.cache/werkator/gradle
The first build of each repository fills the shared cache from scratch; from the second repository on, the dependencies and the JDK are already there.
Afterwards the per-repository .gradle directories under */.git/werkator/buildenv/home/ can be deleted — that is where the 5 GiB come back.
Open Questions
- Whether the shared Gradle home should count as a build environment in the System page's disk breakdown. Today the breakdown walks the repositories, so a shared directory outside them falls under whatever category the filesystem total leaves; the per-repository figure shrinks accordingly. Left for when the breakdown is next touched.
Follow-up PRs
- The same idea for the Kotlin/Native toolchain (
.konan, 1.7 GiB for one repository on mih09) once a second repository needs it.