Skip to content

Commit 999aefb

Browse files
garrytanclaude
andcommitted
fix(ci): use hardlink copy instead of symlink for node_modules cache
After the bun.lock fix landed, the eval matrix STILL failed identically: Could not resolve: "smart-buffer" / "ip-address" at /opt/node_modules_cache/socks/build/client/socksclient.js But the hash-tagged image actually contains smart-buffer + ip-address + socks all flat in /opt/node_modules_cache (verified by pulling and inspecting the image). 207 packages, all present. Root cause: the workflow used `ln -s /opt/node_modules_cache node_modules` to restore deps. Bun build (and Node module resolution generally) walks a file's realpath to find sibling deps. From the symlinked /workspace/node_modules/socks/build/client/socksclient.js, realpath resolves to /opt/node_modules_cache/socks/build/client/socksclient.js, and walking up to find a node_modules/smart-buffer dir fails — there's no `node_modules` segment in the realpath. Switch `ln -s` → `cp -al` (hardlink-copy). Each file in the cache becomes a hardlink at /workspace/node_modules/<pkg>, sharing inodes (no data copy). Realpath of /workspace/node_modules/socks/.../socksclient.js stays inside /workspace/node_modules, so sibling deps resolve correctly. Speed is comparable to symlink — `cp -al` on ~200 packages on tmpfs is sub-second. Same caching story preserved. Both evals.yml and evals-periodic.yml updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 38fd67b commit 999aefb

2 files changed

Lines changed: 16 additions & 3 deletions

File tree

.github/workflows/evals-periodic.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,14 @@ jobs:
101101
echo "TMPDIR=/home/runner/.cache"
102102
} >> "$GITHUB_ENV"
103103
104+
# Hardlink copy (cp -al) instead of symlink: bun build resolves a file's
105+
# realpath when looking for sibling deps, which makes a symlinked
106+
# /opt/node_modules_cache fail to resolve transitive deps. See
107+
# evals.yml for the full explanation.
104108
- name: Restore deps
105109
run: |
106110
if [ -d /opt/node_modules_cache ] && diff -q /opt/node_modules_cache/.package.json package.json >/dev/null 2>&1; then
107-
ln -s /opt/node_modules_cache node_modules
111+
cp -al /opt/node_modules_cache node_modules
108112
else
109113
bun install
110114
fi

.github/workflows/evals.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,20 @@ jobs:
110110
echo "TMPDIR=/home/runner/.cache"
111111
} >> "$GITHUB_ENV"
112112
113-
# Restore pre-installed node_modules from Docker image via symlink (~0s vs ~15s install)
113+
# Restore pre-installed node_modules from Docker image via hardlink copy.
114+
# Why hardlinks instead of symlink: bun build (and Node module resolution
115+
# in general) resolves a file's realpath when walking up to find
116+
# node_modules/<dep>. With `ln -s /opt/node_modules_cache node_modules`,
117+
# resolution from inside socks/build/client/socksclient.js walks the
118+
# /opt/node_modules_cache/... realpath, where there is no parent
119+
# node_modules dir containing smart-buffer/ip-address. With `cp -al`,
120+
# each file's realpath is /workspace/node_modules/..., so sibling deps
121+
# resolve correctly. Speed is comparable to symlink (hardlinks share
122+
# inodes, no actual data copy).
114123
- name: Restore deps
115124
run: |
116125
if [ -d /opt/node_modules_cache ] && diff -q /opt/node_modules_cache/.package.json package.json >/dev/null 2>&1; then
117-
ln -s /opt/node_modules_cache node_modules
126+
cp -al /opt/node_modules_cache node_modules
118127
else
119128
bun install
120129
fi

0 commit comments

Comments
 (0)