Skip to content

Commit 8248c1c

Browse files
sjarmakclaude
andcommitted
perf: pre-create claude user + chown at Docker build time
Adds `adduser claude` and `chown -R claude:claude` to all Dockerfiles at build time so Harbor's runtime chown is a near-instant no-op. Saves 15-30 min per large-repo task (Firefox 5.4GB, Chromium, LLVM, etc). Covers /workspace, /app, /testbed, /logs with existence checks. Updated generator script, both J2 templates, and regenerated all 516 Dockerfiles (255 sg_only, 85 artifact_only, 85 artifact_baseline, 81 base). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6237486 commit 8248c1c

File tree

544 files changed

+8818
-1624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

544 files changed

+8818
-1624
lines changed
Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,32 @@
11
# bustub-hyperloglog-impl-001 — sg_only_env variant
2-
# Uses TAC base image (provides /utils/eval.py, python_default) with workspace truncated.
3-
# Agent uses Sourcegraph MCP exclusively for code access.
2+
# No local repo clone — agent uses Sourcegraph MCP exclusively for code access.
43

5-
FROM ghcr.io/theagentcompany/sde-implement-hyperloglog-image:1.0.0
4+
FROM ubuntu:22.04
65

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/bustub--d5f79431
6+
ENV DEBIAN_FRONTEND=noninteractive
87

9-
# TAC environment variables (needed by verifier)
10-
ENV TAC_SERVER_HOSTNAME=localhost
11-
ENV DECRYPTION_KEY="theagentcompany is all you need"
12-
13-
# Create logs directory for Harbor compatibility
14-
RUN mkdir -p /logs/agent /logs/verifier /workspace
15-
16-
# Truncate workspace — agent must use MCP to discover code
17-
RUN find /workspace -type f -name '*.py' -o -name '*.cpp' -o -name '*.h' -o -name '*.c' \
18-
-o -name '*.java' -o -name '*.js' -o -name '*.ts' -o -name '*.go' -o -name '*.rs' \
19-
-o -name '*.rb' -o -name '*.sh' -o -name '*.md' -o -name '*.txt' -o -name '*.json' \
20-
-o -name '*.yaml' -o -name '*.yml' -o -name '*.toml' -o -name '*.cfg' -o -name '*.ini' \
21-
| while read f; do : > "$f"; done 2>/dev/null || true
8+
RUN apt-get update && apt-get install -y --no-install-recommends \
9+
git \
10+
ca-certificates \
11+
python3 \
12+
curl \
13+
&& rm -rf /var/lib/apt/lists/*
2214

2315
WORKDIR /workspace
2416

2517
# Empty git repo so agent can commit work
26-
RUN git init 2>/dev/null || (git config --global init.defaultBranch main && git init) && \
18+
RUN git init && \
2719
git config user.email "agent@example.com" && \
2820
git config user.name "Agent"
2921

22+
RUN mkdir -p /logs/agent /logs/verifier
23+
3024
# Mark sg_only mode so verifiers can skip local-path checks
3125
RUN touch /tmp/.sg_only_mode
3226

33-
# Clone manifest for sgonly_verifier_wrapper.sh to restore repo at verify time
34-
RUN echo '{"repos":[{"mirror":"sg-evals/bustub--d5f79431","dest":"/workspace"}]}' > /tmp/.sg_only_clone_manifest.json
27+
# Pre-create claude user and set ownership at build time so Harbor's
28+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
29+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
30+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
3531

3632
ENTRYPOINT []
Lines changed: 150 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
#!/bin/bash
2-
# SG-only verifier wrapper: restore truncated source files from backup
2+
# SG-only verifier wrapper: restore full repo + overlay agent changes
33
#
44
# Source this at the TOP of test.sh for build-requiring tasks that use
5-
# sg_only_env mode. It detects /tmp/.sg_only_mode and restores truncated
6-
# (0-byte) source files from /repo_full/ while preserving agent changes.
5+
# sg_only_env mode. It detects /tmp/.sg_only_mode and:
76
#
8-
# Key insight: the sg_only Dockerfile truncates source files to 0 bytes.
9-
# Agent-written files are non-zero. So restoring only 0-byte files from
10-
# /repo_full/ recovers the full codebase without touching agent work.
11-
# This avoids the expensive delete+copy cycle on large repos (e.g. 9GB
12-
# ProtonMail monorepo with 135K node_modules entries).
7+
# PRIMARY PATH (clone manifest):
8+
# 1. Reads clone manifest from /tmp/.sg_only_clone_manifest.json
9+
# 2. Backs up agent-written files (non-empty, non-git, non-test)
10+
# 3. Clones each mirror repo with --depth 1
11+
# 4. Re-runs inject_defects.sh if specified in manifest
12+
# 5. Overlays agent changes on top
13+
#
14+
# LEGACY FALLBACK (pre-v2 images):
15+
# If manifest is missing but /repo_full/ exists, restores from /repo_full/
16+
# as before. This ensures unregenerated images still work during rollout.
1317
#
1418
# For non-sg_only runs, this script is a no-op.
1519
#
1620
# Usage in test.sh:
1721
# #!/bin/bash
18-
# [ -f /tmp/.sg_only_mode ] && [ -f /tests/sgonly_verifier_wrapper.sh ] && source /tests/sgonly_verifier_wrapper.sh
22+
# # Source the sg_only wrapper (no-op if not in sg_only mode)
23+
# if [ -f /tests/sgonly_verifier_wrapper.sh ]; then
24+
# source /tests/sgonly_verifier_wrapper.sh
25+
# fi
1926
# # ... rest of test.sh as normal ...
2027

2128
if [ ! -f /tmp/.sg_only_mode ]; then
@@ -25,6 +32,130 @@ fi
2532

2633
echo "[sg_only_verifier] Detected sg_only mode, restoring full repo..."
2734

35+
# ---------------------------------------------------------------------------
36+
# Helper: back up agent-written files from a directory
37+
# ---------------------------------------------------------------------------
38+
backup_agent_files() {
39+
local srcdir="$1"
40+
if [ ! -d "$srcdir" ]; then
41+
return
42+
fi
43+
cd "$srcdir"
44+
mkdir -p /tmp/agent_work
45+
find . -type f -size +0 \
46+
! -path './.git/*' \
47+
! -path './tests/*' \
48+
! -path './.claude/*' \
49+
-print0 | while IFS= read -r -d '' f; do
50+
mkdir -p "/tmp/agent_work/$(dirname "$f")"
51+
cp "$f" "/tmp/agent_work/$f"
52+
done
53+
echo "[sg_only_verifier] Backed up agent-written files from $srcdir"
54+
}
55+
56+
# ---------------------------------------------------------------------------
57+
# Helper: overlay agent-written files back onto a directory
58+
# ---------------------------------------------------------------------------
59+
overlay_agent_files() {
60+
local targetdir="$1"
61+
if [ ! -d /tmp/agent_work ]; then
62+
return
63+
fi
64+
cd /tmp/agent_work
65+
find . -type f -print0 | while IFS= read -r -d '' f; do
66+
local target="${targetdir}/${f#./}"
67+
mkdir -p "$(dirname "$target")"
68+
cp "$f" "$target"
69+
done
70+
echo "[sg_only_verifier] Overlaid agent changes onto $targetdir"
71+
}
72+
73+
# ---------------------------------------------------------------------------
74+
# PRIMARY PATH: clone manifest
75+
# ---------------------------------------------------------------------------
76+
MANIFEST="/tmp/.sg_only_clone_manifest.json"
77+
78+
if [ -f "$MANIFEST" ]; then
79+
echo "[sg_only_verifier] Found clone manifest, using clone-at-verify strategy"
80+
81+
# Parse manifest with python3 (always available in our images)
82+
WORKDIR=$(python3 -c "import json; m=json.load(open('$MANIFEST')); print(m.get('workdir', '/workspace'))")
83+
echo "[sg_only_verifier] Working directory: $WORKDIR"
84+
85+
# 1. Back up agent-written files
86+
backup_agent_files "$WORKDIR"
87+
88+
# 2. Clone each mirror repo
89+
REPO_COUNT=$(python3 -c "import json; m=json.load(open('$MANIFEST')); print(len(m.get('repos', [])))")
90+
for i in $(seq 0 $((REPO_COUNT - 1))); do
91+
MIRROR=$(python3 -c "import json; m=json.load(open('$MANIFEST')); print(m['repos'][$i]['mirror'])")
92+
TARGET_DIR=$(python3 -c "import json; m=json.load(open('$MANIFEST')); print(m['repos'][$i].get('target_dir', '.'))")
93+
CLONE_URL="https://github.com/${MIRROR}.git"
94+
95+
if [ "$TARGET_DIR" = "." ]; then
96+
CLONE_TARGET="$WORKDIR"
97+
else
98+
CLONE_TARGET="${WORKDIR}/${TARGET_DIR}"
99+
fi
100+
101+
echo "[sg_only_verifier] Cloning $MIRROR -> $CLONE_TARGET"
102+
103+
# Remove existing directory contents (truncated files) but preserve .git
104+
# for target_dir="." we need to be careful with the working directory
105+
if [ "$TARGET_DIR" = "." ]; then
106+
# For root workspace: remove everything except .git, then clone into temp and move
107+
TMPCLONE=$(mktemp -d)
108+
if git clone --depth 1 "$CLONE_URL" "$TMPCLONE" 2>/dev/null; then
109+
# Remove old files (except .git and tests)
110+
find "$CLONE_TARGET" -mindepth 1 -maxdepth 1 \
111+
! -name '.git' ! -name 'tests' ! -name '.claude' \
112+
-exec rm -rf {} + 2>/dev/null || true
113+
# Copy cloned files (except .git)
114+
cd "$TMPCLONE"
115+
find . -mindepth 1 -maxdepth 1 ! -name '.git' -exec cp -a {} "$CLONE_TARGET/" \;
116+
cd /
117+
rm -rf "$TMPCLONE"
118+
echo "[sg_only_verifier] Restored $MIRROR to $CLONE_TARGET"
119+
else
120+
echo "[sg_only_verifier] WARNING: Failed to clone $CLONE_URL"
121+
rm -rf "$TMPCLONE"
122+
fi
123+
else
124+
# For subdirectory: remove and re-clone
125+
rm -rf "$CLONE_TARGET"
126+
if git clone --depth 1 "$CLONE_URL" "$CLONE_TARGET" 2>/dev/null; then
127+
echo "[sg_only_verifier] Restored $MIRROR to $CLONE_TARGET"
128+
else
129+
echo "[sg_only_verifier] WARNING: Failed to clone $CLONE_URL"
130+
fi
131+
fi
132+
done
133+
134+
# 3. Re-run inject_defects if specified
135+
INJECT_SCRIPT=$(python3 -c "import json; m=json.load(open('$MANIFEST')); print(m.get('inject_defects', ''))")
136+
if [ -n "$INJECT_SCRIPT" ] && [ -f "$INJECT_SCRIPT" ]; then
137+
echo "[sg_only_verifier] Running defect injection: $INJECT_SCRIPT"
138+
cd "$WORKDIR"
139+
chmod +x "$INJECT_SCRIPT"
140+
bash "$INJECT_SCRIPT"
141+
echo "[sg_only_verifier] Defect injection complete"
142+
fi
143+
144+
# 4. Overlay agent changes
145+
overlay_agent_files "$WORKDIR"
146+
147+
# Return to working directory
148+
cd "$WORKDIR"
149+
echo "[sg_only_verifier] Clone-at-verify restore complete, proceeding with tests"
150+
151+
return 0 2>/dev/null || exit 0
152+
fi
153+
154+
# ---------------------------------------------------------------------------
155+
# LEGACY FALLBACK: /repo_full/ restore (for pre-v2 images)
156+
# ---------------------------------------------------------------------------
157+
echo "[sg_only_verifier] No clone manifest found, trying legacy /repo_full/ restore..."
158+
28159
# Read the working directory
29160
WORKDIR="$(cat /tmp/.sg_only_workdir 2>/dev/null || echo '/app')"
30161
echo "[sg_only_verifier] Working directory: $WORKDIR"
@@ -34,18 +165,16 @@ if [ ! -d /repo_full ]; then
34165
return 0 2>/dev/null || exit 0
35166
fi
36167

37-
# Restore truncated files: find 0-byte files and bulk-copy originals from
38-
# backup using tar pipe (orders of magnitude faster than individual cp on
39-
# repos with 100K+ truncated files like ProtonMail's node_modules).
40-
# Agent-written files are non-zero and will not be touched.
41-
cd "$WORKDIR"
42-
find . -type f -size 0 \
43-
! -path './.git/*' ! -path './tests/*' ! -path './.claude/*' \
44-
! -path './node_modules/*' ! -path './__pycache__/*' ! -path './vendor/*' \
45-
-print0 | (cd /repo_full && tar --null -T - -cf - 2>/dev/null) \
46-
| tar xf - -C "$WORKDIR/" 2>/dev/null
47-
echo "[sg_only_verifier] Restored truncated files from /repo_full/"
168+
# 1. Find files the agent wrote (non-empty, non-git, non-test files)
169+
backup_agent_files "$WORKDIR"
170+
171+
# 2. Restore full repo from backup
172+
rsync -a --delete /repo_full/ "$WORKDIR/"
173+
echo "[sg_only_verifier] Restored full repo from /repo_full/"
174+
175+
# 3. Overlay agent's changes
176+
overlay_agent_files "$WORKDIR"
48177

49178
# Return to working directory
50179
cd "$WORKDIR"
51-
echo "[sg_only_verifier] Restore complete, proceeding with tests"
180+
echo "[sg_only_verifier] Legacy restore complete, proceeding with tests"

benchmarks/ccb_build/camel-fix-protocol-feat-001/environment/Dockerfile.sg_only

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
FROM eclipse-temurin:17-jdk
66

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/camel--1006f047
8-
97
ENV DEBIAN_FRONTEND=noninteractive
108

119
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -31,4 +29,9 @@ RUN echo '{"workdir":"/workspace","repos":[{"mirror":"sg-evals/camel--1006f047",
3129
# Mark sg_only mode
3230
RUN touch /tmp/.sg_only_mode
3331

32+
# Pre-create claude user and set ownership at build time so Harbor's
33+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
34+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
35+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
36+
3437
ENTRYPOINT []

benchmarks/ccb_build/cgen-deps-install-001/environment/Dockerfile.sg_only

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
FROM ubuntu:22.04
66

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/cgen--dibench
8-
97
ENV DEBIAN_FRONTEND=noninteractive
108

119
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -30,4 +28,9 @@ RUN echo '{"workdir":"/app/repo","repos":[{"mirror":"sg-evals/cgen--dibench","ta
3028
# Mark sg_only mode
3129
RUN touch /tmp/.sg_only_mode
3230

31+
# Pre-create claude user and set ownership at build time so Harbor's
32+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
33+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
34+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
35+
3336
ENTRYPOINT []

benchmarks/ccb_build/codecoverage-deps-install-001/environment/Dockerfile.sg_only

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
FROM ubuntu:22.04
66

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/CodeCoverageSummary--dibench
8-
97
ENV DEBIAN_FRONTEND=noninteractive
108

119
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -30,4 +28,9 @@ RUN echo '{"workdir":"/app/repo","repos":[{"mirror":"sg-evals/CodeCoverageSummar
3028
# Mark sg_only mode
3129
RUN touch /tmp/.sg_only_mode
3230

31+
# Pre-create claude user and set ownership at build time so Harbor's
32+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
33+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
34+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
35+
3336
ENTRYPOINT []

benchmarks/ccb_build/dotenv-expand-deps-install-001/environment/Dockerfile.sg_only

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
FROM ubuntu:22.04
66

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/dotenv-expand--dibench
8-
97
ENV DEBIAN_FRONTEND=noninteractive
108

119
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -36,4 +34,9 @@ RUN echo '{"workdir":"/app/repo","repos":[{"mirror":"sg-evals/dotenv-expand--dib
3634
# Mark sg_only mode
3735
RUN touch /tmp/.sg_only_mode
3836

37+
# Pre-create claude user and set ownership at build time so Harbor's
38+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
39+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
40+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
41+
3942
ENTRYPOINT []

benchmarks/ccb_build/dotnetkoans-deps-install-001/environment/Dockerfile.sg_only

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
FROM ubuntu:22.04
66

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/DotNetKoans--dibench
8-
97
ENV DEBIAN_FRONTEND=noninteractive
108

119
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -30,4 +28,9 @@ RUN echo '{"workdir":"/app/repo","repos":[{"mirror":"sg-evals/DotNetKoans--diben
3028
# Mark sg_only mode
3129
RUN touch /tmp/.sg_only_mode
3230

31+
# Pre-create claude user and set ownership at build time so Harbor's
32+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
33+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
34+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
35+
3336
ENTRYPOINT []

benchmarks/ccb_build/envoy-grpc-server-impl-001/environment/Dockerfile.sg_only

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
FROM ubuntu:22.04
66

7-
ENV SOURCEGRAPH_REPOS=sg-evals/go-control-plane--71637ad6,sg-evals/istio--2300e245,sg-evals/emissary--3bbdbe0f
8-
97
ENV DEBIAN_FRONTEND=noninteractive
8+
ENV SOURCEGRAPH_REPOS="sg-evals/emissary--3bbdbe0f,sg-evals/go-control-plane--71637ad6,sg-evals/istio--2300e245"
9+
1010
RUN apt-get update && apt-get install -y --no-install-recommends \
1111
git \
1212
ca-certificates \
@@ -29,4 +29,9 @@ RUN echo '{"workdir":"/workspace","repos":[{"mirror":"sg-evals/go-control-plane-
2929
# Mark sg_only mode
3030
RUN touch /tmp/.sg_only_mode
3131

32+
# Pre-create claude user and set ownership at build time so Harbor's
33+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
34+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
35+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
36+
3237
ENTRYPOINT []

benchmarks/ccb_build/eslint-markdown-deps-install-001/environment/Dockerfile.sg_only

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
FROM ubuntu:22.04
66

7-
ENV SOURCEGRAPH_REPO_NAME=sg-evals/markdown--dibench
8-
97
ENV DEBIAN_FRONTEND=noninteractive
108

119
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -36,4 +34,9 @@ RUN echo '{"workdir":"/app/repo","repos":[{"mirror":"sg-evals/markdown--dibench"
3634
# Mark sg_only mode
3735
RUN touch /tmp/.sg_only_mode
3836

37+
# Pre-create claude user and set ownership at build time so Harbor's
38+
# runtime chown is a no-op (avoids 15-30 min delay on large repos).
39+
RUN (adduser --disabled-password --gecos '' claude 2>/dev/null || true) && \
40+
for d in /workspace /app /testbed /logs; do [ -d "$d" ] && chown -R claude:claude "$d"; done || true
41+
3942
ENTRYPOINT []

0 commit comments

Comments
 (0)