feat: add preflight environment checklist script
scripts/preflight.sh checks all required dependencies: - Rust/Cargo build toolchain - RabbitMQ (installed + reachable + management plugin) - MongoDB (installed + reachable) - MariaDB (installed + reachable) - Apache + PHP + php-mongodb (observer tool requirements) Modes: --check report only, no installs (default) check + apt install missing packages (Debian/Ubuntu) Idempotent, no sudo prompts during check-only mode. Detection uses dpkg/systemctl rather than PATH-dependent command -v.
This commit is contained in:
296
scripts/preflight.sh
Executable file
296
scripts/preflight.sh
Executable file
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# preflight.sh — BEDS environment preflight checklist
|
||||
#
|
||||
# Checks that all required services and tools are present and reachable.
|
||||
# Installs anything missing (Debian/Ubuntu only).
|
||||
# Idempotent — safe to run multiple times.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/preflight.sh # check + install missing
|
||||
# ./scripts/preflight.sh --check # check only, no installs
|
||||
#
|
||||
# Requirements: bash 5+, sudo access (for installs)
|
||||
# Supported: Debian 11+ / Ubuntu 22.04+
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- config ------------------------------------------------------------------
|
||||
RABBITMQ_HOST="${RABBITMQ_HOST:-localhost}"
|
||||
RABBITMQ_PORT="${RABBITMQ_PORT:-5672}"
|
||||
MONGO_HOST="${MONGO_HOST:-localhost}"
|
||||
MONGO_PORT="${MONGO_PORT:-27017}"
|
||||
MARIADB_HOST="${MARIADB_HOST:-localhost}"
|
||||
MARIADB_PORT="${MARIADB_PORT:-3306}"
|
||||
|
||||
CHECK_ONLY=false
|
||||
if [[ "${1:-}" == "--check" ]]; then
|
||||
CHECK_ONLY=true
|
||||
fi
|
||||
|
||||
# --- output helpers ----------------------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
RESET='\033[0m'
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
WARN=0
|
||||
|
||||
pass() { echo -e " ${GREEN}[PASS]${RESET} $1"; PASS=$(( PASS + 1 )); }
|
||||
fail() { echo -e " ${RED}[FAIL]${RESET} $1"; FAIL=$(( FAIL + 1 )); }
|
||||
warn() { echo -e " ${YELLOW}[WARN]${RESET} $1"; WARN=$(( WARN + 1 )); }
|
||||
info() { echo -e " ${CYAN} ${RESET} $1"; }
|
||||
section() { echo; echo -e "${BOLD}$1${RESET}"; echo "$(printf '%.0s-' {1..60})"; }
|
||||
|
||||
# --- distro check ------------------------------------------------------------
|
||||
detect_distro() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
source /etc/os-release
|
||||
DISTRO_ID="${ID:-unknown}"
|
||||
DISTRO_LIKE="${ID_LIKE:-}"
|
||||
else
|
||||
DISTRO_ID="unknown"
|
||||
DISTRO_LIKE=""
|
||||
fi
|
||||
}
|
||||
|
||||
is_debian_family() {
|
||||
[[ "$DISTRO_ID" == "debian" || "$DISTRO_ID" == "ubuntu" || "$DISTRO_LIKE" == *"debian"* ]]
|
||||
}
|
||||
|
||||
apt_install() {
|
||||
if $CHECK_ONLY; then
|
||||
fail "$1 — not installed (run without --check to install)"
|
||||
return
|
||||
fi
|
||||
info "Installing $*..."
|
||||
sudo apt-get install -y "$@" 2>&1 | tail -3
|
||||
}
|
||||
|
||||
# Check via systemd (more reliable than searching sbin paths)
|
||||
svc_installed() {
|
||||
systemctl cat "$1" &>/dev/null
|
||||
}
|
||||
|
||||
svc_active() {
|
||||
systemctl is-active --quiet "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
# Check via dpkg (fast, no PATH issues)
|
||||
pkg_installed() {
|
||||
dpkg -s "$1" &>/dev/null && dpkg -s "$1" | grep -q 'Status: install ok installed'
|
||||
}
|
||||
|
||||
# --- tcp reachability --------------------------------------------------------
|
||||
tcp_check() {
|
||||
local host="$1" port="$2"
|
||||
timeout 2 bash -c ">/dev/tcp/${host}/${port}" 2>/dev/null
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
section "BEDS Preflight Checklist"
|
||||
echo " Mode: $( $CHECK_ONLY && echo 'check only' || echo 'check + install')"
|
||||
detect_distro
|
||||
echo " Distro: ${DISTRO_ID:-unknown} ${VERSION_ID:-}"
|
||||
echo
|
||||
|
||||
# =============================================================================
|
||||
section "1. Build Toolchain"
|
||||
|
||||
if command -v rustc &>/dev/null; then
|
||||
RUST_VER=$(rustc --version)
|
||||
pass "Rust — $RUST_VER"
|
||||
else
|
||||
fail "Rust — not found"
|
||||
info "Install: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
|
||||
info "(rustup is not managed by apt — install manually)"
|
||||
fi
|
||||
|
||||
if command -v cargo &>/dev/null; then
|
||||
CARGO_VER=$(cargo --version)
|
||||
pass "Cargo — $CARGO_VER"
|
||||
else
|
||||
fail "Cargo — not found (install Rust via rustup)"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
section "2. RabbitMQ"
|
||||
|
||||
if pkg_installed rabbitmq-server; then
|
||||
pass "RabbitMQ — installed"
|
||||
else
|
||||
if $CHECK_ONLY; then
|
||||
fail "RabbitMQ — not installed"
|
||||
else
|
||||
if is_debian_family; then
|
||||
info "Adding RabbitMQ apt repository..."
|
||||
curl -fsSL https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/rabbitmq-archive-keyring.gpg 2>/dev/null
|
||||
echo "deb [signed-by=/usr/share/keyrings/rabbitmq-archive-keyring.gpg] https://packagecloud.io/rabbitmq/rabbitmq-server/debian/ $(source /etc/os-release && echo $VERSION_CODENAME) main" \
|
||||
| sudo tee /etc/apt/sources.list.d/rabbitmq.list > /dev/null
|
||||
sudo apt-get update -qq
|
||||
apt_install rabbitmq-server
|
||||
else
|
||||
fail "RabbitMQ — not installed; automatic install only supported on Debian/Ubuntu"
|
||||
info "See: https://www.rabbitmq.com/docs/install-debian"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if tcp_check "$RABBITMQ_HOST" "$RABBITMQ_PORT"; then
|
||||
pass "RabbitMQ reachable at ${RABBITMQ_HOST}:${RABBITMQ_PORT}"
|
||||
else
|
||||
fail "RabbitMQ not reachable at ${RABBITMQ_HOST}:${RABBITMQ_PORT}"
|
||||
info "Start: sudo systemctl start rabbitmq-server"
|
||||
fi
|
||||
|
||||
if svc_active rabbitmq-server; then
|
||||
# Check management plugin via the HTTP API (no sudo needed)
|
||||
if tcp_check localhost 15672; then
|
||||
pass "RabbitMQ management plugin enabled (port 15672 open)"
|
||||
else
|
||||
warn "RabbitMQ management plugin not enabled"
|
||||
info "Enable: sudo rabbitmq-plugins enable rabbitmq_management"
|
||||
info " (provides the admin UI at http://localhost:15672)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
section "3. MongoDB"
|
||||
|
||||
if svc_installed mongod; then
|
||||
MONGO_VER=$(mongod --version 2>/dev/null | head -1 || echo 'installed')
|
||||
pass "MongoDB — $MONGO_VER"
|
||||
else
|
||||
if $CHECK_ONLY; then
|
||||
fail "MongoDB — not installed"
|
||||
else
|
||||
if is_debian_family; then
|
||||
info "Adding MongoDB apt repository..."
|
||||
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg 2>/dev/null
|
||||
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg] https://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main" \
|
||||
| sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list > /dev/null
|
||||
sudo apt-get update -qq
|
||||
apt_install mongodb-org
|
||||
else
|
||||
fail "MongoDB — not installed; automatic install only supported on Debian/Ubuntu"
|
||||
info "See: https://www.mongodb.org/docs/manual/installation/"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if tcp_check "$MONGO_HOST" "$MONGO_PORT"; then
|
||||
pass "MongoDB reachable at ${MONGO_HOST}:${MONGO_PORT}"
|
||||
else
|
||||
fail "MongoDB not reachable at ${MONGO_HOST}:${MONGO_PORT}"
|
||||
info "Start: sudo systemctl start mongod"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
section "4. MariaDB"
|
||||
|
||||
if svc_installed mariadb; then
|
||||
pass "MariaDB — installed"
|
||||
else
|
||||
if $CHECK_ONLY; then
|
||||
fail "MariaDB — not installed"
|
||||
else
|
||||
if is_debian_family; then
|
||||
apt_install mariadb-server
|
||||
else
|
||||
fail "MariaDB — not installed; automatic install only supported on Debian/Ubuntu"
|
||||
info "See: https://mariadb.org/download/"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if tcp_check "$MARIADB_HOST" "$MARIADB_PORT"; then
|
||||
pass "MariaDB reachable at ${MARIADB_HOST}:${MARIADB_PORT}"
|
||||
else
|
||||
fail "MariaDB not reachable at ${MARIADB_HOST}:${MARIADB_PORT}"
|
||||
info "Start: sudo systemctl start mariadb"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
section "5. Apache + PHP (Observer Tool)"
|
||||
|
||||
if svc_installed apache2; then
|
||||
APACHE_VER=$(apache2 -v 2>/dev/null | head -1 | awk '{print $3}' || echo 'installed')
|
||||
pass "Apache — $APACHE_VER"
|
||||
else
|
||||
if $CHECK_ONLY; then
|
||||
fail "Apache — not installed"
|
||||
else
|
||||
if is_debian_family; then
|
||||
apt_install apache2
|
||||
else
|
||||
fail "Apache — not installed; automatic install only supported on Debian/Ubuntu"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if tcp_check localhost 80; then
|
||||
pass "Apache reachable at localhost:80"
|
||||
else
|
||||
fail "Apache not reachable at localhost:80"
|
||||
info "Start: sudo systemctl start apache2"
|
||||
fi
|
||||
|
||||
if pkg_installed php; then
|
||||
PHP_VER=$(php --version 2>/dev/null | head -1 || echo 'installed')
|
||||
pass "PHP — $PHP_VER"
|
||||
else
|
||||
if $CHECK_ONLY; then
|
||||
fail "PHP — not installed"
|
||||
else
|
||||
if is_debian_family; then
|
||||
apt_install php libapache2-mod-php php-mongodb php-cli
|
||||
sudo systemctl restart apache2
|
||||
else
|
||||
fail "PHP — not installed; automatic install only supported on Debian/Ubuntu"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check php-mongodb extension specifically
|
||||
if pkg_installed php-mongodb; then
|
||||
pass "PHP mongodb extension — installed"
|
||||
else
|
||||
warn "PHP mongodb extension — not installed"
|
||||
info "Install: sudo apt install php-mongodb && sudo systemctl restart apache2"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
section "Summary"
|
||||
|
||||
TOTAL=$(( PASS + FAIL + WARN ))
|
||||
echo
|
||||
echo -e " Checked : $TOTAL"
|
||||
echo -e " ${GREEN}Passed : $PASS${RESET}"
|
||||
if [[ $WARN -gt 0 ]]; then
|
||||
echo -e " ${YELLOW}Warnings: $WARN${RESET}"
|
||||
fi
|
||||
if [[ $FAIL -gt 0 ]]; then
|
||||
echo -e " ${RED}Failed : $FAIL${RESET}"
|
||||
echo
|
||||
if $CHECK_ONLY; then
|
||||
echo -e " Run ${BOLD}./scripts/preflight.sh${RESET} (without --check) to install missing dependencies."
|
||||
else
|
||||
echo -e " Some checks failed. Review output above and resolve before running BEDS."
|
||||
fi
|
||||
exit 1
|
||||
else
|
||||
echo
|
||||
echo -e " ${GREEN}${BOLD}All checks passed. Environment is ready.${RESET}"
|
||||
if ! $CHECK_ONLY; then
|
||||
echo
|
||||
echo -e " Next steps:"
|
||||
echo -e " 1. Copy config template: cp config/beds.toml.example config/beds.toml"
|
||||
echo -e " 2. Edit credentials: \$EDITOR config/env_dev.toml"
|
||||
echo -e " 3. Build and run: cargo run"
|
||||
fi
|
||||
fi
|
||||
Reference in New Issue
Block a user