Files

161 lines
5.5 KiB
Bash
Raw Permalink Normal View History

2026-09-11 02:46:56 +02:00
#!/usr/bin/env bash
# ============================================================================
# SHARP-ER - check that this machine can run the project
# ----------------------------------------------------------------------------
# ./scripts/check-prereqs.sh check both setup paths, report
# ./scripts/check-prereqs.sh manual only the tools PATH A needs
# ./scripts/check-prereqs.sh docker only the tools PATH B needs
#
# Exits non-zero when a tool the chosen path REQUIRES is missing or too old, so
# it can be used as a gate in CI or in an onboarding script. Read-only: it
# starts nothing, installs nothing and writes nothing.
# ============================================================================
set -uo pipefail
MODE="${1:-all}"
case "$MODE" in
all|manual|docker) ;;
-h|--help)
printf 'usage: check-prereqs.sh [all|manual|docker]\n'
exit 0 ;;
*)
printf 'unknown mode: %s (use all, manual or docker)\n' "$MODE" >&2
exit 2 ;;
esac
# Versions this repository is developed against.
NODE_MIN="22.22.0" # React Router 8 requires >= 22.22.0
PNPM_MAJOR="9" # packageManager: pnpm@9.12.3
PG_MAJOR="16"
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
else
C_OK=""; C_WARN=""; C_ERR=""; C_DIM=""; C_OFF=""
fi
FAILED=0
pass() { printf '%s ok %s %-22s %s\n' "$C_OK" "$C_OFF" "$1" "${2:-}"; }
warn() { printf '%s warn %s %-22s %s\n' "$C_WARN" "$C_OFF" "$1" "${2:-}"; }
fail() { printf '%s fail %s %-22s %s\n' "$C_ERR" "$C_OFF" "$1" "${2:-}"; FAILED=1; }
note() { printf '%s %s %-22s %s\n' "$C_DIM" "$C_OFF" "" "$1"; }
# true when $1 >= $2, comparing dotted versions numerically.
version_ge() {
[ "$1" = "$2" ] && return 0
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
}
# Strip a leading v and anything after the numeric part: "v22.23.2" -> 22.23.2
clean_version() { printf '%s' "$1" | sed -E 's/^v//; s/[^0-9.].*$//'; }
check_node() {
if ! command -v node >/dev/null 2>&1; then
fail "node" "not installed - need >= $NODE_MIN"
note "nvm install \$(cat .nvmrc) # .nvmrc pins the version this repo uses"
return
fi
local version; version="$(clean_version "$(node --version)")"
if version_ge "$version" "$NODE_MIN"; then
pass "node $version" ">= $NODE_MIN"
else
fail "node $version" "too old - React Router 8 requires >= $NODE_MIN"
note "nvm install \$(cat .nvmrc) && nvm use"
fi
}
check_pnpm() {
if ! command -v pnpm >/dev/null 2>&1; then
fail "pnpm" "not installed - need $PNPM_MAJOR.x"
note "corepack enable && corepack prepare pnpm@9.12.3 --activate"
return
fi
local version major; version="$(clean_version "$(pnpm --version)")"; major="${version%%.*}"
if [ "$major" = "$PNPM_MAJOR" ]; then
pass "pnpm $version" "major $PNPM_MAJOR"
else
warn "pnpm $version" "repo pins pnpm@9.12.3 - the lockfile may be rewritten"
fi
}
check_postgres_client() {
if ! command -v psql >/dev/null 2>&1; then
fail "psql" "not installed - need the PostgreSQL $PG_MAJOR client"
note "sudo apt install postgresql-$PG_MAJOR postgresql-client-$PG_MAJOR"
return
fi
local version major
version="$(clean_version "$(psql --version | awk '{print $3}')")"
major="${version%%.*}"
if [ "$major" = "$PG_MAJOR" ]; then
pass "psql $version" "major $PG_MAJOR"
else
warn "psql $version" "developed against PostgreSQL $PG_MAJOR"
fi
}
check_postgres_server() {
command -v pg_isready >/dev/null 2>&1 || { note "pg_isready not found - skipped the server check"; return; }
local host="${DATABASE_HOST:-127.0.0.1}" port="${DATABASE_PORT:-5432}"
if pg_isready --host "$host" --port "$port" --timeout 3 >/dev/null 2>&1; then
pass "postgres server" "answering on $host:$port"
else
warn "postgres server" "nothing on $host:$port - start it before ./scripts/setup-db.sh"
fi
}
check_docker() {
if ! command -v docker >/dev/null 2>&1; then
fail "docker" "not installed"
note "https://docs.docker.com/engine/install/"
return
fi
pass "docker" "$(docker --version 2>/dev/null | sed 's/,.*//')"
if docker compose version >/dev/null 2>&1; then
pass "docker compose" "$(docker compose version --short 2>/dev/null)"
else
fail "docker compose" "v2 plugin missing (the old docker-compose binary is not supported)"
fi
if ! docker info >/dev/null 2>&1; then
warn "docker daemon" "not reachable - start Docker, or add your user to the docker group"
fi
}
check_env_files() {
local root; root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
[ -f "$root/.env" ] && pass ".env" "present" || warn ".env" "missing - cp .env.example .env"
if [ "$MODE" != "docker" ]; then
[ -f "$root/apps/cms/.env" ] \
&& pass "apps/cms/.env" "present" \
|| warn "apps/cms/.env" "missing - cp apps/cms/.env.example apps/cms/.env"
fi
}
printf '\nSHARP-ER prerequisites (%s)\n\n' "$MODE"
if [ "$MODE" = "manual" ] || [ "$MODE" = "all" ]; then
printf '%sPATH A - manual%s\n' "$C_DIM" "$C_OFF"
check_node
check_pnpm
check_postgres_client
check_postgres_server
printf '\n'
fi
if [ "$MODE" = "docker" ] || [ "$MODE" = "all" ]; then
printf '%sPATH B - docker%s\n' "$C_DIM" "$C_OFF"
check_docker
printf '\n'
fi
printf '%sEnvironment files%s\n' "$C_DIM" "$C_OFF"
check_env_files
printf '\n'
if [ "$FAILED" -ne 0 ]; then
printf '%sSomething required is missing - see the lines marked fail.%s\n\n' "$C_ERR" "$C_OFF"
exit 1
fi
printf '%sReady. Next: README.md, "PATH A" or "PATH B".%s\n\n' "$C_OK" "$C_OFF"