Files
werkator/docs/prs/2026-09-17-PR#76-one-gradle-home-for-all.md
83f38c6ffa
werkator build succeeded after 02:19
werkator/deploy build succeeded after 00:07
feat(werkdock): one Gradle user home for all sandboxes of a host (#76)
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
2026-09-17 15:08:14 +02:00

102 lines
5.6 KiB
Markdown

> **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 `.gradle` left under `/root` stays 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.gradleHome` names 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.gradleHome` set
- **When** the sandbox is started
- **Then** the directory is mounted read-write as `/gradle-user-home`, after the `/root` mount
- **and** `GRADLE_USER_HOME=/gradle-user-home` is set inside the sandbox
- **and** the directory exists on the host before the command starts
##### Verified by
- [WerkdockBuildRunnerTest — mounts a shared Gradle home after /root and points GRADLE_USER_HOME at it](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
#### 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_HOME` are added; Gradle keeps its cache under `/root`
##### Verified by
- [WerkdockBuildRunnerTest — without a shared Gradle home the sandbox keeps its own under /root](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
#### 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`
- **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](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderTest.kt)
- [BuildDefinitionTest — every docker and werkdock setting can be said in a build definition](../../src/test/kotlin/de/hoennig/werkator/config/BuildDefinitionTest.kt) (the structural guard from PR#72, which is what made adding the key to `WerkdockOverrides` mandatory)
## 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:
```yaml
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.