#!/usr/bin/env bash
dev_harness_venv_covers_cheap_gates() {
local python_bin="$1"
[ -n "$python_bin" ] || return 1
"$python_bin" -c 'import dotenv, yaml' >/dev/null 2>&1
}
dev_harness_python_floor_ok() {
local python_bin="$1"
[ -n "$python_bin" ] || return 1
"$python_bin" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)' >/dev/null 2>&1
}
dev_harness_venv_python_is_pinned_311() {
[ -n "$1" ] || return 1
"$1" -c 'import sys; raise SystemExit(0 if (3, 11) <= sys.version_info[:2] < (3, 12) else 1)' >/dev/null 2>&1
}
dev_harness_backend_pinned_prefixes() {
cat <<'PREFIXES'
/opt/homebrew/opt/python@3.11/bin/python3.11
/usr/local/opt/python@3.11/bin/python3.11
/opt/homebrew/bin/python3.11
/usr/local/bin/python3.11
PREFIXES
}
dev_harness_backend_pinned_python() {
if [ -n "${PYTHON:-}" ] && dev_harness_venv_python_is_pinned_311 "$PYTHON"; then
printf '%s\n' "$PYTHON"
return 0
fi
if command -v python3.11 >/dev/null 2>&1 \
&& dev_harness_venv_python_is_pinned_311 python3.11; then
printf '%s\n' python3.11
return 0
fi
local prefix_candidate
for prefix_candidate in $(dev_harness_backend_pinned_prefixes); do
if [ -x "$prefix_candidate" ] \
&& dev_harness_venv_python_is_pinned_311 "$prefix_candidate"; then
printf '%s\n' "$prefix_candidate"
return 0
fi
done
local uv_bin candidate
uv_bin="$(command -v uv 2>/dev/null)" || return 1
candidate="$("$uv_bin" python find 3.11 2>/dev/null)" || return 1
[ -n "$candidate" ] || return 1
if command -v cygpath >/dev/null 2>&1; then
candidate="$(cygpath -u "$candidate" 2>/dev/null || printf '%s' "$candidate")"
fi
if [ -x "$candidate" ] && dev_harness_venv_python_is_pinned_311 "$candidate"; then
printf '%s\n' "$candidate"
return 0
fi
return 1
}
dev_harness_canonical_python() {
local repo_root candidate
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
for candidate in \
"$repo_root/backend/.venv/bin/python" \
"$repo_root/backend/.venv/Scripts/python.exe"; do
if [ -x "$candidate" ]; then
if ! dev_harness_venv_covers_cheap_gates "$candidate"; then
echo "dev-harness: $candidate exists but cannot import yaml and dotenv (incomplete venv); ignoring it. Run: make lane-bootstrap" >&2
continue
fi
if ! dev_harness_python_floor_ok "$candidate"; then
echo "dev-harness: $candidate is below the 3.10 floor the pre-push check scripts require; ignoring it. Run: make lane-bootstrap" >&2
continue
fi
printf '%s\n' "$candidate"
return
fi
done
return 1
}
dev_harness_python() {
if [ -n "${PYTHON:-}" ]; then
printf '%s\n' "$PYTHON"
return
fi
local repo_root candidate
if candidate="$(dev_harness_canonical_python)"; then
printf '%s\n' "$candidate"
return
fi
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
for candidate in \
"$repo_root/backend/venv/bin/python" \
"$repo_root/backend/venv/Scripts/python.exe"; do
if [ -x "$candidate" ]; then
if ! dev_harness_venv_covers_cheap_gates "$candidate"; then
echo "dev-harness: $candidate exists but cannot import yaml and dotenv (incomplete venv); ignoring it. Run: make lane-bootstrap" >&2
continue
fi
if ! dev_harness_python_floor_ok "$candidate"; then
echo "dev-harness: $candidate is below the 3.10 floor the pre-push check scripts require; ignoring it. Run: make lane-bootstrap" >&2
continue
fi
printf '%s\n' "$candidate"
return
fi
done
local fallback floor_only
floor_only=""
for fallback in python3.14 python3.13 python3.12 python3.11 python3.10 python3; do
if dev_harness_python_floor_ok "$fallback"; then
if dev_harness_venv_covers_cheap_gates "$fallback"; then
printf '%s\n' "$fallback"
return
fi
[ -n "$floor_only" ] || floor_only="$fallback"
fi
done
local prefix_bin version
for prefix_bin in \
/opt/homebrew/opt/python@3.14/bin \
/opt/homebrew/opt/python@3.13/bin \
/opt/homebrew/opt/python@3.12/bin \
/opt/homebrew/opt/python@3.11/bin \
/opt/homebrew/opt/python@3.10/bin \
/opt/homebrew/bin \
/usr/local/opt/python@3.14/bin \
/usr/local/opt/python@3.13/bin \
/usr/local/opt/python@3.12/bin \
/usr/local/opt/python@3.11/bin \
/usr/local/opt/python@3.10/bin \
/usr/local/bin; do
[ -d "$prefix_bin" ] || continue
for version in 3.14 3.13 3.12 3.11 3.10; do
candidate="$prefix_bin/python$version"
if dev_harness_python_floor_ok "$candidate"; then
if dev_harness_venv_covers_cheap_gates "$candidate"; then
printf '%s\n' "$candidate"
return
fi
[ -n "$floor_only" ] || floor_only="$candidate"
fi
done
done
if [ -n "$floor_only" ]; then
printf '%s\n' "$floor_only"
return
fi
printf '%s\n' "python3"
}
dev_harness_python_floor_advisory() {
cat <<'ADVISORY'
The dev harness selected bare `python3`, but it reports a version below the
3.10 floor the pre-push check scripts require (PEP 604 unions at module scope).
On macOS the PATH `python3` is often Xcode CLT Python 3.9.
Fix: run `make lane-bootstrap` to create backend/.venv (the canonical
interpreter), or install Python >= 3.10 (e.g. `brew install python@3.12`) and
put it on PATH, or select one explicitly with `PYTHON=/path/to/python3.12`.
ADVISORY
}
dev_harness_python_uses_windows_paths() {
local python_bin="$1"
local resolved resolved_lower
resolved="$(command -v "$python_bin" 2>/dev/null || printf '%s' "$python_bin")"
resolved_lower="$(printf '%s' "$resolved" | tr '[:upper:]' '[:lower:]')"
case "$resolved_lower" in
*.exe)
return 0
;;
esac
return 1
}
dev_harness_pythonpath() {
local python_bin="$1"
shift
local separator=":"
local entry
local value=""
if dev_harness_python_uses_windows_paths "$python_bin"; then
separator=";"
fi
for entry in "$@"; do
[ -n "$entry" ] || continue
if [ -n "$value" ]; then
value="${value}${separator}${entry}"
else
value="$entry"
fi
done
if [ -n "${PYTHONPATH:-}" ]; then
if [ -n "$value" ]; then
value="${value}${separator}${PYTHONPATH}"
else
value="$PYTHONPATH"
fi
fi
printf '%s\n' "$value"
}
dev_harness_require_cli() {
local python_bin="$1"
local pythonpath="$2"
local probe
if probe="$(PYTHONPATH="$pythonpath" "$python_bin" -c 'import dev_harness.cli' 2>&1)"; then
return 0
fi
{
echo "Omi dev harness is not provisioned: $python_bin cannot import dev_harness.cli"
printf '%s\n' "$probe" | tail -n 1 | sed 's/^/ /'
echo "Run \`make lane-bootstrap\` first (Python 3.11 venv + cheap-gate packages). Uvicorn/pyright: \`make lane-backend\`. Locked pylock sync remains \`make setup-backend\`."
} >&2
return 1
}