#!/usr/bin/env bash # Pin Mathlib and the Lean toolchain to each other. # # Mathlib tags do not exist for every Lean point release, so hardcoding both is how # image builds break six weeks later. Instead: choose a Mathlib revision, then take # *its* lean-toolchain as the truth and write both into the Lean project. # # resolve-mathlib.sh [revision] # # revision defaults to "auto": the newest Mathlib tag whose toolchain matches the # toolchain already in the project, else the newest tag, else master. set -euo pipefail PROJECT="${1:?usage: resolve-mathlib.sh [revision]}" REQUESTED="${2:-auto}" REPO="https://github.com/leanprover-community/mathlib4.git" RAW="https://raw.githubusercontent.com/leanprover-community/mathlib4" TOOLCHAIN_FILE="$PROJECT/lean-toolchain" LAKEFILE="$PROJECT/lakefile.toml" toolchain_of() { # toolchain_of -> prints e.g. leanprover/lean4:v4.33.0 curl -fsSL --retry 3 --max-time 30 "$RAW/$1/lean-toolchain" 2>/dev/null | tr -d '[:space:]' } candidates() { git ls-remote --tags --refs "$REPO" 'v*' 2>/dev/null \ | awk '{print $2}' | sed 's#refs/tags/##' \ | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ | sort -V -r } wanted="$(tr -d '[:space:]' < "$TOOLCHAIN_FILE" 2>/dev/null || true)" rev="" if [ "$REQUESTED" != "auto" ]; then rev="$REQUESTED" echo "resolve-mathlib: using requested revision $rev" else echo "resolve-mathlib: looking for a Mathlib tag built on ${wanted:-}" all="$(candidates || true)" if [ -n "$wanted" ]; then # only the tags sharing a major.minor with what we asked for are plausible series="$(printf '%s' "$wanted" | sed -E 's#.*:v([0-9]+\.[0-9]+).*#v\1#')" for tag in $(printf '%s\n' "$all" | grep -E "^${series}\." || true); do if [ "$(toolchain_of "$tag")" = "$wanted" ]; then rev="$tag" echo "resolve-mathlib: $tag matches $wanted exactly" break fi done if [ -z "$rev" ]; then rev="$(printf '%s\n' "$all" | grep -E "^${series}\." | head -1 || true)" [ -n "$rev" ] && echo "resolve-mathlib: no exact match; newest in $series series is $rev" fi fi if [ -z "$rev" ]; then rev="$(printf '%s\n' "$all" | head -1 || true)" echo "resolve-mathlib: falling back to newest tag ${rev:-master}" fi rev="${rev:-master}" fi actual="$(toolchain_of "$rev" || true)" if [ -z "$actual" ]; then echo "resolve-mathlib: could not read the toolchain for $rev; keeping ${wanted:-project default}" >&2 actual="$wanted" fi printf '%s\n' "$actual" > "$TOOLCHAIN_FILE" # rewrite only the rev line inside the [[require]] block for mathlib python3 - "$LAKEFILE" "$rev" <<'PY' import re import sys path, rev = sys.argv[1], sys.argv[2] with open(path, encoding="utf-8") as stream: text = stream.read() text = re.sub(r'(?m)^rev\s*=\s*".*"$', f'rev = "{rev}"', text, count=1) with open(path, "w", encoding="utf-8") as stream: stream.write(text) PY echo "resolve-mathlib: mathlib $rev on $actual"