File size: 8,359 Bytes
bcc8d35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/env bash
# Install the pinned Lean 4 + Mathlib + REPL toolchain under /apps (Track L / P1).
#
# WHY this exists as a script and not as Python: the toolchain is an EXTERNAL BINARY
# (see docs/DEPENDENCY_POLICY.md, "Lean toolchain"). It is installed once, outside the
# Python environment, and `align/verify/lean` reaches it only through subprocess.
#
# Idempotent: every step is guarded by a marker/exists check, so re-running after a
# partial failure resumes instead of redoing a 5 GB download. Every network or build
# step runs under `timeout`, so a stalled mirror cannot hang the shell.
#
# Host rules (/apps/CLAUDE.md, binding): nothing is ever written to `/` or `/tmp`.
# elan, the Mathlib cache, git temp files and XDG caches are all redirected below.
# The preflight refuses on <45GB MemAvailable, load >= 2x cores, disk queue >= 64, or
# less than 40GB free on /apps. A Mathlib cache decompress is I/O heavy; it is not a
# GPU job so it does not take the GPU heavy-job lock.
#
# THE PIN (see docs/LEAN_VERIFIER.md section "Pin"):
#   Goedel-Prover-V2 follows DeepSeek-Prover-V1.5: its `mathlib4` git submodule is
#   xinhjBrant/mathlib4 @ 2f65ba7f1a9144b20c8e7358513548e317d26de1, a single commit on
#   top of leanprover-community/mathlib4 @ d1d1e4b72c6e009edd97cabe1d661b021fe8eeae
#   (2024-06-12) that ONLY edits lakefile.lean/lake-manifest.json to add DeepSeek's REPL
#   fork as a dependency. Its lean-toolchain is `leanprover/lean4:v4.9.0-rc1` (nominally
#   "Lean 4.9"; the binary reports 4.9.0-rc1). We pin the UPSTREAM parent: the Mathlib
#   .olean cache is keyed on file hashes that include lakefile.lean, so the DeepSeek
#   edit would orphan the cache, while the parent's .oleans are byte-for-byte the
#   Mathlib sources Goedel proofs were checked against. The REPL is upstream
#   leanprover-community/repl at the tag whose toolchain equals ours.
#
# Usage:  bash scripts/lean_setup.sh            # full install
#         LEAN_ROOT=... bash scripts/lean_setup.sh
#         bash scripts/lean_setup.sh --check    # only verify an existing install
set -euo pipefail

LEAN_ROOT="${LEAN_ROOT:-/apps/development/rlhf/lean}"
LEAN_TOOLCHAIN="${LEAN_TOOLCHAIN:-leanprover/lean4:v4.9.0-rc1}"
MATHLIB_REV="${MATHLIB_REV:-d1d1e4b72c6e009edd97cabe1d661b021fe8eeae}"
REPL_TAG="${REPL_TAG:-v4.9.0-rc1}"
REPL_URL="${REPL_URL:-https://github.com/leanprover-community/repl}"
MATHLIB_URL="https://github.com/leanprover-community/mathlib4"

case "$LEAN_ROOT" in
  /apps/*) ;;
  *) echo "refusing: LEAN_ROOT=$LEAN_ROOT is not under /apps (host rule: never write to / or /tmp)" >&2; exit 2 ;;
esac

export ELAN_HOME="$LEAN_ROOT/elan"
export MATHLIB_CACHE_DIR="$LEAN_ROOT/mathlib-cache"
export XDG_CACHE_HOME="$LEAN_ROOT/xdg-cache"
export XDG_CONFIG_HOME="$LEAN_ROOT/xdg-config"
export TMPDIR="$LEAN_ROOT/tmp"
export GIT_TERMINAL_PROMPT=0
export PATH="$ELAN_HOME/bin:$PATH"
WS="$LEAN_ROOT/workspace"
REPL_DIR="$LEAN_ROOT/repl"
LOGS="$LEAN_ROOT/logs"
mkdir -p "$LEAN_ROOT" "$MATHLIB_CACHE_DIR" "$XDG_CACHE_HOME" "$XDG_CONFIG_HOME" "$TMPDIR" "$LOGS"

log() { printf '[lean_setup %s] %s\n' "$(date +%H:%M:%S)" "$*"; }

preflight() {
  local avail_kb load cores q free_gb
  avail_kb=$(awk '/^MemAvailable:/{print $2}' /proc/meminfo)
  load=$(awk '{print $1}' /proc/loadavg)
  cores=$(nproc)
  q=$(awk '$3=="nvme2n1"{print $12}' /proc/diskstats)
  q=${q:-0}
  free_gb=$(df -BG --output=avail /apps | tail -1 | tr -dc 0-9)
  log "preflight: MemAvailable=$((avail_kb/1024/1024))GB load1=$load cores=$cores diskq=$q /apps_free=${free_gb}GB"
  [ "$avail_kb" -ge $((45*1024*1024)) ] || { log "REFUSED: MemAvailable < 45GB"; exit 83; }
  awk -v l="$load" -v c="$cores" 'BEGIN{exit !(l < 2*c)}' || { log "REFUSED: load >= 2x cores"; exit 83; }
  [ "$q" -lt 64 ] || { log "REFUSED: disk queue >= 64"; exit 83; }
  [ "$free_gb" -ge 40 ] || { log "REFUSED: < 40GB free on /apps"; exit 83; }
}

check_install() {
  [ -x "$ELAN_HOME/bin/lake" ] || { log "missing elan/lake"; return 1; }
  [ -x "$REPL_DIR/.lake/build/bin/repl" ] || { log "missing repl binary"; return 1; }
  [ -f "$WS/.lake/packages/mathlib/.lake/build/lib/Mathlib.olean" ] || { log "missing Mathlib.olean (cache not fetched)"; return 1; }
  log "install present"
}

if [ "${1:-}" = "--check" ]; then check_install; exit $?; fi

preflight

# ---- 1. elan -----------------------------------------------------------------------
if [ ! -x "$ELAN_HOME/bin/elan" ]; then
  log "installing elan into $ELAN_HOME"
  timeout 120 curl -sSfL https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -o "$LEAN_ROOT/elan-init.sh"
  # --no-modify-path: never touch ~/.profile / ~/.bashrc. --default-toolchain none: the
  # toolchain is installed explicitly below so the pin is visible in this file.
  timeout 600 sh "$LEAN_ROOT/elan-init.sh" -y --no-modify-path --default-toolchain none
fi
elan --version

# ---- 2. toolchain ------------------------------------------------------------------
if ! elan toolchain list | grep -qF "${LEAN_TOOLCHAIN##*:}"; then
  log "installing $LEAN_TOOLCHAIN"
  timeout 900 elan toolchain install "$LEAN_TOOLCHAIN"
fi

# ---- 3. workspace (Lake project requiring Mathlib at the pinned rev) ---------------
mkdir -p "$WS"
if [ ! -f "$WS/lakefile.lean" ]; then
  log "writing workspace lakefile"
  printf '%s\n' "$LEAN_TOOLCHAIN" > "$WS/lean-toolchain"
  cat > "$WS/lakefile.lean" <<EOF
import Lake
open Lake DSL

package «align_lean_ws»

require mathlib from git
  "$MATHLIB_URL" @ "$MATHLIB_REV"
EOF
fi
if [ ! -f "$WS/lake-manifest.json" ]; then
  log "lake update (clones mathlib + its pinned deps; several minutes)"
  (cd "$WS" && timeout 3000 lake update) 2>&1 | tee "$LOGS/lake_update.log" | tail -5
fi
# Guard against silent drift: the manifest must name exactly the pinned rev.
grep -q "$MATHLIB_REV" "$WS/lake-manifest.json" || { log "FATAL: manifest does not pin $MATHLIB_REV"; exit 3; }

# ---- 4. prebuilt Mathlib cache -----------------------------------------------------
if [ ! -f "$WS/.lake/packages/mathlib/.lake/build/lib/Mathlib.olean" ]; then
  log "lake exe cache get (downloads + unpacks the prebuilt oleans)"
  (cd "$WS" && timeout 3000 lake exe cache get) 2>&1 | tee "$LOGS/cache_get.log" | tail -8
fi
# Deps (batteries, Qq, aesop, proofwidgets) are built from source by lake when the cache
# does not cover them; this is a no-op when it does.
if ! (cd "$WS" && timeout 120 lake build --no-build Mathlib >/dev/null 2>&1); then
  log "lake build Mathlib (deps not fully cached; building the remainder)"
  (cd "$WS" && timeout 5400 lake build Mathlib) 2>&1 | tee "$LOGS/lake_build.log" | tail -5
fi

# ---- 5. REPL at the matching tag ---------------------------------------------------
if [ ! -d "$REPL_DIR/.git" ]; then
  log "cloning repl $REPL_TAG"
  timeout 300 git clone --branch "$REPL_TAG" --depth 1 "$REPL_URL" "$REPL_DIR"
fi
REPL_TC=$(cat "$REPL_DIR/lean-toolchain")
[ "$REPL_TC" = "$LEAN_TOOLCHAIN" ] || { log "FATAL: repl toolchain $REPL_TC != $LEAN_TOOLCHAIN"; exit 3; }
if [ ! -x "$REPL_DIR/.lake/build/bin/repl" ]; then
  log "building repl"
  (cd "$REPL_DIR" && timeout 1200 lake build) 2>&1 | tee "$LOGS/repl_build.log" | tail -5
fi

# ---- 6. smoke test: import Mathlib through the built REPL --------------------------
log "smoke test"
SMOKE=$(cd "$WS" && printf '%s\n\n%s\n\n' \
  '{"cmd": "import Mathlib\ntheorem t (a b : Nat) : a + b = b + a := by omega"}' \
  '{"cmd": "#print axioms t", "env": 0}' \
  | timeout 300 lake env "$REPL_DIR/.lake/build/bin/repl")
echo "$SMOKE" | head -20
echo "$SMOKE" | grep -q '"env"' || { log "FATAL: REPL smoke failed"; exit 4; }

cat > "$LEAN_ROOT/PINS.json" <<EOF
{"lean_toolchain": "$LEAN_TOOLCHAIN", "mathlib_rev": "$MATHLIB_REV",
 "repl_tag": "$REPL_TAG", "repl_rev": "$(git -C "$REPL_DIR" rev-parse HEAD)",
 "installed": "$(date -Iseconds)",
 "source_of_pin": "Goedel-Prover-V2 submodule xinhjBrant/mathlib4@2f65ba7f (parent leanprover-community/mathlib4@d1d1e4b7)"}
EOF
cat > "$LEAN_ROOT/env.sh" <<EOF
# source me. Redirects every cache the Lean toolchain writes to /apps.
export LEAN_ROOT="$LEAN_ROOT"
export ELAN_HOME="$ELAN_HOME"
export MATHLIB_CACHE_DIR="$MATHLIB_CACHE_DIR"
export XDG_CACHE_HOME="$XDG_CACHE_HOME"
export TMPDIR="$TMPDIR"
export PATH="\$ELAN_HOME/bin:\$PATH"
EOF
log "disk: $(du -sh "$LEAN_ROOT" 2>/dev/null | cut -f1) total under $LEAN_ROOT"
log "done"