222 lines
8.3 KiB
Bash
Executable File
222 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Verify prerequisites for every recipe. For each missing item, prints the
|
|
# EXACT command to install it on the detected platform. Exits non-zero if
|
|
# any hard requirement is missing; warns but continues for PATH hygiene
|
|
# and the web-browser check (headless systems can still use login-headless).
|
|
|
|
set -euo pipefail
|
|
|
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
# shellcheck disable=SC1091
|
|
. "$here/common.sh"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Platform detection
|
|
# ---------------------------------------------------------------------------
|
|
uname_s="$(uname -s 2>/dev/null || echo unknown)"
|
|
pm="" # one of: apt dnf pacman zypper brew ""
|
|
case "$uname_s" in
|
|
Linux)
|
|
if [ -r /etc/os-release ]; then
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
id_all=" ${ID:-} ${ID_LIKE:-} "
|
|
case "$id_all" in
|
|
*' debian '*|*' ubuntu '*) pm=apt ;;
|
|
*' fedora '*|*' rhel '*|*' centos '*) pm=dnf ;;
|
|
*' arch '*) pm=pacman ;;
|
|
*' opensuse '*|*' suse '*) pm=zypper ;;
|
|
esac
|
|
fi
|
|
if [ -z "$pm" ]; then
|
|
for candidate in apt-get dnf pacman zypper; do
|
|
if command -v "$candidate" >/dev/null 2>&1; then
|
|
case "$candidate" in
|
|
apt-get) pm=apt ;;
|
|
dnf) pm=dnf ;;
|
|
pacman) pm=pacman ;;
|
|
zypper) pm=zypper ;;
|
|
esac
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
;;
|
|
Darwin)
|
|
pm=brew
|
|
;;
|
|
esac
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Render a package-install command for the current platform.
|
|
# pkg_cmd <apt> <dnf> <pacman> <brew> <upstream-url>
|
|
# ---------------------------------------------------------------------------
|
|
pkg_cmd() {
|
|
local apt_pkg="$1" dnf_pkg="$2" pac_pkg="$3" brew_pkg="$4" url="$5"
|
|
case "$pm" in
|
|
apt) echo "sudo apt-get update && sudo apt-get install -y $apt_pkg" ;;
|
|
dnf) echo "sudo dnf install -y $dnf_pkg" ;;
|
|
pacman) echo "sudo pacman -S --noconfirm $pac_pkg" ;;
|
|
zypper) echo "sudo zypper install -y $dnf_pkg" ;;
|
|
brew) echo "brew install $brew_pkg" ;;
|
|
*) echo "# install manually from: $url" ;;
|
|
esac
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Accumulators
|
|
# ---------------------------------------------------------------------------
|
|
missing=0
|
|
fix_commands=()
|
|
|
|
record_miss() {
|
|
local name="$1" detail="$2" fix="$3"
|
|
printf ' %s %-12s -> %s\n' "$(_fc_tag "$_FC_RED" miss)" "$name" "$detail"
|
|
printf ' fix: %s\n' "$fix"
|
|
fix_commands+=("$fix")
|
|
missing=$((missing + 1))
|
|
}
|
|
|
|
check_cmd() {
|
|
local cmd="$1" fix="$2"
|
|
if command -v "$cmd" >/dev/null 2>&1; then
|
|
printf ' %s %-12s -> %s\n' "$(_fc_tag "$_FC_GREEN" ok)" "$cmd" "$(command -v "$cmd")"
|
|
else
|
|
record_miss "$cmd" "not found on PATH" "$fix"
|
|
fi
|
|
}
|
|
|
|
info 'checking prerequisites...'
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Required tools
|
|
# ---------------------------------------------------------------------------
|
|
check_cmd git "$(pkg_cmd git git git git https://git-scm.com/)"
|
|
check_cmd bash "$(pkg_cmd bash bash bash bash https://www.gnu.org/software/bash/)"
|
|
check_cmd curl "$(pkg_cmd curl curl curl curl https://curl.se/)"
|
|
check_cmd install "$(pkg_cmd coreutils coreutils coreutils coreutils https://www.gnu.org/software/coreutils/)"
|
|
check_cmd python3 "$(pkg_cmd python3 python3 python python3 https://www.python.org/)"
|
|
|
|
# just and uv do not ship in most package managers, so prefer the upstream
|
|
# installers (no sudo, land binaries in ~/.local/bin / ~/.cargo/bin).
|
|
check_cmd just 'curl --proto "=https" --tlsv1.2 -LsSf https://just.systems/install.sh | bash -s -- --to "$HOME/.local/bin"'
|
|
check_cmd uv 'curl -LsSf https://astral.sh/uv/install.sh | sh'
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Web browser (warning only: headless is supported via login-headless)
|
|
# ---------------------------------------------------------------------------
|
|
if command -v xdg-open >/dev/null 2>&1 || command -v open >/dev/null 2>&1; then
|
|
printf ' %s %-12s -> (detected)\n' "$(_fc_tag "$_FC_GREEN" ok)" 'web browser'
|
|
else
|
|
printf ' %s %-12s -> no xdg-open/open on PATH.\n' "$(_fc_tag "$_FC_YELLOW" warn)" 'web browser'
|
|
printf ' if this is a headless machine, run: just login-headless\n'
|
|
case "$pm" in
|
|
apt) printf ' otherwise install: sudo apt-get install -y xdg-utils\n' ;;
|
|
dnf) printf ' otherwise install: sudo dnf install -y xdg-utils\n' ;;
|
|
pacman) printf ' otherwise install: sudo pacman -S --noconfirm xdg-utils\n' ;;
|
|
esac
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Python 3.11+ : accepted via system python3 OR uv-managed interpreter
|
|
# ---------------------------------------------------------------------------
|
|
py_ok=0
|
|
py_via=""
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
if python3 - <<'PY' >/dev/null 2>&1
|
|
import sys
|
|
sys.exit(0 if sys.version_info >= (3, 11) else 1)
|
|
PY
|
|
then
|
|
py_ok=1
|
|
py_via="$(python3 -V 2>&1)"
|
|
fi
|
|
fi
|
|
if [ "$py_ok" -eq 0 ] && command -v uv >/dev/null 2>&1; then
|
|
if uv_py_path="$(uv python find '>=3.11' 2>/dev/null)" && [ -n "$uv_py_path" ]; then
|
|
py_ok=1
|
|
py_via="$uv_py_path (via uv)"
|
|
fi
|
|
fi
|
|
|
|
if [ "$py_ok" -eq 1 ]; then
|
|
printf ' %s %-12s -> %s\n' "$(_fc_tag "$_FC_GREEN" ok)" 'python>=3.11' "$py_via"
|
|
else
|
|
detail='python3 missing'
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
detail="python3 is $(python3 -V 2>&1 | awk '{print $2}'); need 3.11+"
|
|
fi
|
|
# Primary fix: uv-managed Python. Works on every platform, no sudo, and
|
|
# the orchestrator (a uv project) will resolve it automatically via
|
|
# `uv run` / `uv sync`.
|
|
py_fix='uv python install 3.11'
|
|
# Per-distro alternative (commented so users can copy one or the other):
|
|
case "$pm" in
|
|
apt)
|
|
py_alt='sudo add-apt-repository -y ppa:deadsnakes/ppa && sudo apt-get update && sudo apt-get install -y python3.11 python3.11-venv'
|
|
;;
|
|
dnf)
|
|
py_alt='sudo dnf install -y python3.11'
|
|
;;
|
|
pacman)
|
|
py_alt='sudo pacman -S --noconfirm python'
|
|
;;
|
|
zypper)
|
|
py_alt='sudo zypper install -y python311'
|
|
;;
|
|
brew)
|
|
py_alt='brew install python@3.11'
|
|
;;
|
|
*)
|
|
py_alt=''
|
|
;;
|
|
esac
|
|
printf ' %s %-12s -> %s\n' "$(_fc_tag "$_FC_RED" miss)" 'python>=3.11' "$detail"
|
|
printf ' fix: %s\n' "$py_fix"
|
|
if [ -n "$py_alt" ]; then
|
|
printf ' alt: %s\n' "$py_alt"
|
|
fi
|
|
fix_commands+=("$py_fix")
|
|
missing=$((missing + 1))
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# PATH hygiene: warning, not a hard miss.
|
|
# ---------------------------------------------------------------------------
|
|
case ":$PATH:" in
|
|
*":$LOCAL_BIN:"*)
|
|
printf ' %s %-12s -> %s is on PATH\n' "$(_fc_tag "$_FC_GREEN" ok)" 'PATH' "$LOCAL_BIN"
|
|
;;
|
|
*)
|
|
rc='~/.bashrc'
|
|
[ -n "${ZSH_VERSION:-}" ] && rc='~/.zshrc'
|
|
printf ' %s %-12s -> %s is NOT on PATH.\n' "$(_fc_tag "$_FC_YELLOW" warn)" 'PATH' "$LOCAL_BIN"
|
|
# shellcheck disable=SC2016
|
|
printf " fix: echo 'export PATH=\"%s:\$PATH\"' >> %s && exec \$SHELL -l\n" "$LOCAL_BIN" "$rc"
|
|
;;
|
|
esac
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Summary
|
|
# ---------------------------------------------------------------------------
|
|
if [ "$missing" -gt 0 ]; then
|
|
printf '\n'
|
|
warn "$missing prerequisite(s) missing. Run the following to fix them:"
|
|
printf '\n'
|
|
# De-duplicate while preserving order.
|
|
seen=''
|
|
for c in "${fix_commands[@]}"; do
|
|
case "$seen" in
|
|
*"<<$c>>"*) continue ;;
|
|
esac
|
|
seen="$seen<<$c>>"
|
|
printf ' %s\n' "$c"
|
|
done
|
|
printf '\n'
|
|
warn 'then re-run: just doctor'
|
|
exit 1
|
|
fi
|
|
|
|
info 'all prerequisites present'
|