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

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 .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

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

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

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.