Skip to content

Commit 38bd5b7

Browse files
authored
Reuse Lake builds in Codex worktrees (#165)
1 parent 7972b55 commit 38bd5b7

3 files changed

Lines changed: 140 additions & 15 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
2+
version = 1
3+
name = "talos-lean"
4+
5+
[setup]
6+
script = '''
7+
set -euo pipefail
8+
9+
exec "${CODEX_SOURCE_TREE_PATH}/scripts/codex-worktree-setup.sh"
10+
'''
11+
12+
[cleanup]
13+
script = '''
14+
# No cleanup is needed. Codex removes the worktree-local copy-on-write cache
15+
# together with the worktree.
16+
'''

.codex/hooks.json

Lines changed: 0 additions & 15 deletions
This file was deleted.

scripts/codex-worktree-setup.sh

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env bash
2+
# Warm a new Codex worktree from the local checkout's existing Lake artifacts.
3+
#
4+
# On APFS, clonefile creates copy-on-write directory trees: the new worktree
5+
# gets an isolated cache immediately, while unchanged artifacts continue to
6+
# share disk blocks with the source checkout. Lake then rebuilds only artifacts
7+
# whose inputs differ at the worktree's selected commit.
8+
9+
set -euo pipefail
10+
11+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12+
source_tree="${CODEX_SOURCE_TREE_PATH:-$(cd "${script_dir}/.." && pwd)}"
13+
worktree="${CODEX_WORKTREE_PATH:-$(git -C "${PWD}" rev-parse --show-toplevel)}"
14+
15+
if [[ ! -d "${source_tree}/.git" && ! -f "${source_tree}/.git" ]]; then
16+
echo "codex-worktree-setup: source checkout is not a Git worktree: ${source_tree}" >&2
17+
exit 1
18+
fi
19+
if [[ ! -d "${worktree}/.git" && ! -f "${worktree}/.git" ]]; then
20+
echo "codex-worktree-setup: destination is not a Git worktree: ${worktree}" >&2
21+
exit 1
22+
fi
23+
24+
clone_tree() {
25+
local src="$1"
26+
local dst="$2"
27+
28+
[[ -d "${src}" ]] || return 0
29+
30+
# A cancelled Lake command can leave an empty directory behind. It is safe
31+
# to remove that exact empty destination and retry the seed.
32+
if [[ -d "${dst}" && ! -L "${dst}" ]] &&
33+
! find "${dst}" -mindepth 1 -print -quit | grep -q .; then
34+
rmdir "${dst}"
35+
fi
36+
if [[ -e "${dst}" || -L "${dst}" ]]; then
37+
echo " keep ${dst#${worktree}/}"
38+
return 0
39+
fi
40+
41+
mkdir -p "$(dirname "${dst}")"
42+
43+
if [[ "$(uname -s)" == "Darwin" ]]; then
44+
# clonefile(2) recursively clones a directory on APFS without copying
45+
# its data blocks. Calling it once also avoids a slow user-space walk.
46+
/usr/bin/python3 - "${src}" "${dst}" <<'PY'
47+
import ctypes
48+
import os
49+
import sys
50+
51+
libc = ctypes.CDLL("/usr/lib/libc.dylib", use_errno=True)
52+
libc.clonefile.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint32]
53+
libc.clonefile.restype = ctypes.c_int
54+
55+
src = os.fsencode(sys.argv[1])
56+
dst = os.fsencode(sys.argv[2])
57+
if libc.clonefile(src, dst, 0) != 0:
58+
error = ctypes.get_errno()
59+
raise OSError(error, os.strerror(error), sys.argv[1], sys.argv[2])
60+
PY
61+
elif cp --help 2>/dev/null | grep -q -- '--reflink'; then
62+
cp -a --reflink=auto "${src}" "${dst}"
63+
else
64+
# Portable fallback for non-APFS filesystems. This is a real copy, but
65+
# still avoids recompilation and preserves worktree isolation.
66+
cp -R -p "${src}" "${dst}"
67+
fi
68+
69+
seeded_artifacts=true
70+
echo " clone ${dst#${worktree}/}"
71+
}
72+
73+
seeded_artifacts=false
74+
dependency_manifests_differ=false
75+
for manifest in \
76+
interpreter/lake-manifest.json \
77+
codelib/lake-manifest.json \
78+
programs/lean/lake-manifest.json; do
79+
if ! cmp -s "${source_tree}/${manifest}" "${worktree}/${manifest}"; then
80+
dependency_manifests_differ=true
81+
break
82+
fi
83+
done
84+
85+
if [[ "${source_tree}" != "${worktree}" ]]; then
86+
echo "Seeding Lake artifacts from ${source_tree}"
87+
88+
# Third-party sources and their cached oleans (Mathlib, Iris, and transitives).
89+
clone_tree "${source_tree}/.lake/packages" "${worktree}/.lake/packages"
90+
91+
# Project-owned Lake artifacts. Keep these worktree-local so simultaneous
92+
# builds cannot overwrite one another.
93+
package_dirs=(
94+
interpreter
95+
codelib
96+
programs/lean
97+
verifier
98+
docbuild
99+
)
100+
for package_dir in "${package_dirs[@]}"; do
101+
clone_tree \
102+
"${source_tree}/${package_dir}/.lake/build" \
103+
"${worktree}/${package_dir}/.lake/build"
104+
clone_tree \
105+
"${source_tree}/${package_dir}/.lake/config" \
106+
"${worktree}/${package_dir}/.lake/config"
107+
done
108+
else
109+
echo "Using Lake artifacts from the local checkout."
110+
fi
111+
112+
if ! command -v lake >/dev/null 2>&1; then
113+
echo "codex-worktree-setup: 'lake' is not on PATH" >&2
114+
exit 1
115+
fi
116+
117+
if [[ "${seeded_artifacts}" == true && "${dependency_manifests_differ}" == true ]]; then
118+
echo "Dependency revisions differ; fetching their matching binary cache..."
119+
lake -d "${worktree}/programs/lean" update
120+
lake -d "${worktree}/programs/lean" exe cache get
121+
fi
122+
123+
echo "Reconciling the cloned artifacts with this worktree..."
124+
lake -d "${worktree}/programs/lean" build

0 commit comments

Comments
 (0)