Files
werkator/docs/plan/16-git-in-docker-builds.md
T
mhoennigandClaude Opus 5 35f06ec1ec Rename GitTally to Werkator
`gitTally` is the name of another product in the git space, so the
rename is a precaution; nothing about what the build system does changes.

The name follows one rule: `Werkator` where it is prose, capitalized
where it is a Kotlin type and its file, lowercase everywhere a machine
reads it — the command, packages, paths, configuration keys and values,
the Gitea check context. Environment variables keep their convention and
are uppercase throughout.

Every configuration file is still found under its pre-rename name
(`ConfigFiles`): `.gittally.yml` at the repository root, in a build
worktree and as committed on a branch, `.git/gittally/.gittally.yml` for
the machine layer. The current name wins where both exist, and the old
file is then ignored rather than merged — two files side by side are a
half-done rename, not a layering. Without the fallback an installation
that updated without renaming would not fail: a configuration that is
not found leaves every setting at its default, so it would come up
looking healthy while having forgotten its credentials and its builds.

`docs/werkator-migrationsplan.md` lists what the fallback does not
cover and has to be moved by hand — above all the state directory
`.git/werkator/`, which holds the build history, the control token and
the worktrees, and has no fallback of its own.

`docs/migration-from-legacy.md` is deleted with this: it mapped the
legacy script's environment variables, and every host it addressed has
long since moved to the YAML configuration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 19:39:55 +02:00

3.5 KiB

Step 16: Git Access Inside Docker Build Containers

Prerequisites: steps 11, 15. Read README.md first. Motivated by the vm4006 rollout (step 15, fourth finding): hs.hsadmin.ng's build calls git in several places; :prQuickCheck failed hard with "fatal: not a git repository", other call sites swallowed the failure silently.

Problem

Builds run in git worktrees under .git/werkator/worktrees/<branchKey>. A worktree's .git is a pointer file into the primary repository's .git/worktrees/<key>, and DockerBuildRunner bind-mounts only the worktree — so every git call inside the build container fails. The legacy script did not have this problem because it built in the primary checkout with the real .git present (read-write, including all secrets stored next to it — full exposure). Hard invariant to preserve: a branch build must never be able to reach credentials; .git/werkator/.werkator.yml (git.token) and the control token live under .git.

Considered Options

  • Read-only .git mount with .git/werkator/ masked (chosen) — three layered mounts, no config key, no workspace mutation; strictly less privileged than legacy.
  • Copy minimal git metadata into the workspace (admin dir plus objects/info/alternates) — mutates the workspace, still needs the object database mounted, more moving parts.
  • Document the limitation and require git-free build commands — pushes the problem onto every watched project; hsadmin-ng shows real builds do call git.

Design

DockerBuildRunner.gitMetadataMounts(workspace, repoDir) adds three mounts when (and only when) the workspace is a worktree of repoDir (detected via the gitdir: pointer file, which must resolve into repoDir/.git):

  1. repoDir/.git → same path, read-only: objects, refs, and the worktree admin metadata become resolvable; object and ref writes stay impossible.
  2. An empty tmpfs over repoDir/.git/werkator: masks the machine config (git.token), the control token, and all Werkator state; the workspace bind (deeper path, Docker nests mounts by target depth) resurfaces only this build's own worktree inside the masked directory.
  3. repoDir/.git/worktrees/<key> → same path, read-write: the worktree's admin dir (HEAD, index), so index-refreshing commands like git status work.

No configuration key: the exposure is strictly smaller than the legacy baseline, and a knob would join the pinned sandbox-policy set without a known use case. Remaining, documented exposure: the rest of .git — including .git/config — is readable by builds; Werkator never stores credentials there (fetch auth uses a secret-free GIT_ASKPASS with env-passed credentials).

Tests

  • DockerBuildRunnerTest: worktree workspace → the three mounts with :ro and --tmpfs; non-worktree workspace → no git metadata mounts (also keeps the exact-argv test valid).

Acceptance Criteria

  • ./gradlew ktlintFormat then ./gradlew build is green.
  • In a real Docker build worktree: git log/git status succeed inside the container, .git/werkator/.werkator.yml and control-token are not readable, and a git push/ref write fails.
  • docs/configuration.md (docker notes) and the architecture skill describe the mounts.

Result (2026-08-10)

Implemented as designed; verified on vm4006 (see below) and in unit tests. sh -c 'git log -1 && git status --short && cat .../.git/werkator/.werkator.yml' inside a build container of the hs.hsadmin.ng worktree: git commands succeed, the machine config read fails with "No such file or directory", git update-ref fails on the read-only filesystem.