platform-deployments/provisioning/provision.sh
2026-03-05 18:57:06 -08:00

240 lines
7.4 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# provision.sh — configure any LAN machine for the Lilith platform
#
# Unified entrypoint that auto-detects host role and runs the appropriate
# setup modules. Supports black (devops), apricot (GPU dev), and plum (macOS).
#
# Usage:
# ./provision.sh # auto-detect host, full setup
# ./provision.sh --check # verify only, no changes
# ./provision.sh --dns # DNS + CA cert + NPM only
# ./provision.sh --services # host-specific services only
# ./provision.sh --full # everything (default)
# ./provision.sh --host plum # override auto-detection
#
# Idempotent — safe to re-run at any time.
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODULES_DIR="$SCRIPT_DIR/modules"
# ============================================================================
# Source modules
# ============================================================================
source "$MODULES_DIR/common.sh"
source "$MODULES_DIR/dns-client.sh"
# ============================================================================
# Argument parsing
# ============================================================================
MODE="full"
HOST_OVERRIDE=""
CHECK_ONLY=false
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--check|-c)
MODE="check"
CHECK_ONLY=true
;;
--dns)
MODE="dns"
;;
--services)
MODE="services"
;;
--full|-f)
MODE="full"
;;
--host|-H)
shift
HOST_OVERRIDE="${1:-}"
if [[ -z "$HOST_OVERRIDE" ]]; then
log_error "--host requires a value (black, apricot, plum)"
exit 1
fi
;;
--help|-h)
print_usage
exit 0
;;
*)
log_error "Unknown argument: $1"
print_usage
exit 1
;;
esac
shift
done
}
print_usage() {
cat <<'EOF'
provision.sh — configure any LAN machine for the Lilith platform
Usage:
./provision.sh # auto-detect host, full setup
./provision.sh --check # verify only, no changes
./provision.sh --dns # DNS + CA cert + NPM only
./provision.sh --services # host-specific services only
./provision.sh --full # everything (default)
./provision.sh --host plum # override auto-detection
Hosts:
black (10.0.0.11) Ubuntu 24.04 — devops + staging
apricot (10.0.0.13) Fedora Atomic (Bluefin) — GPU dev
plum (10.0.0.123) macOS Sequoia — dev MacBook
Modes:
--check, -c Read-only verification (no changes)
--dns DNS client + CA cert + NPM registry only
--services Host-specific services only
--full, -f Everything (default)
--host, -H Override hostname detection
--help, -h Show this help
EOF
}
# ============================================================================
# Host detection + module loading
# ============================================================================
resolve_host() {
if [[ -n "$HOST_OVERRIDE" ]]; then
PROVISION_HOST_OVERRIDE="$HOST_OVERRIDE"
fi
HOST_ROLE=$(detect_host) || exit 1
}
load_host_module() {
local module_file="$MODULES_DIR/${HOST_ROLE}.sh"
if [[ ! -f "$module_file" ]]; then
log_error "Host module not found: $module_file"
exit 1
fi
source "$module_file"
}
# ============================================================================
# Pre-flight
# ============================================================================
preflight() {
detect_os
log_info "Host: $(hostname -s 2>/dev/null || hostname) → role: $HOST_ROLE"
log_info "OS: $OS_NAME $OS_VERSION"
log_info "Package manager: $(detect_package_manager)"
log_info "Mode: $MODE"
if is_atomic; then
log_info "Immutable OS detected (Fedora Atomic)"
fi
}
# ============================================================================
# Summary table
# ============================================================================
print_summary() {
log_section "Summary"
echo ""
echo -e "${BOLD}Host:${NC} $(hostname -s 2>/dev/null || hostname) ($HOST_ROLE)"
echo -e "${BOLD}Mode:${NC} $MODE"
echo -e "${BOLD}OS:${NC} $OS_NAME $OS_VERSION"
echo ""
case "$HOST_ROLE" in
black)
echo -e "${BOLD}Services:${NC}"
echo " DNS server: dnsmasq (systemd)"
echo " Reverse proxy: nginx (docker)"
echo " Git forge: forgejo (docker)"
echo " NPM registry: verdaccio (docker)"
echo " CI runner: forgejo-runner (docker)"
echo " Backup: restic-rest-server (docker)"
echo " SEO: serpbear (docker)"
echo " SSO: sso-api + postgres + redis (docker)"
echo " Database: postgresql (systemd)"
echo " NFS: nfs-server (systemd)"
echo " PM2: landing-api, webmap-router, status-dashboard"
echo " systemd: marketplace-api, messenger-imessage, host-status-monitor"
;;
apricot)
echo -e "${BOLD}Configuration:${NC}"
echo " DNS: → 10.0.0.11 (resolved)"
echo " CA cert: Lilith Platform CA"
echo " NPM: → npm.black.local"
echo " Feature DBs: analytics, i18n, seo, conv-assistant"
echo " Dev nginx: *.apricot.local (podman)"
echo " CUDA/ML: toolkit + PyTorch"
echo " Backups: restic → black:8000"
echo " VPN: WireGuard wg0"
echo " Monitoring: host-status-monitor"
;;
plum)
echo -e "${BOLD}Configuration:${NC}"
echo " DNS: → 10.0.0.11 (networksetup)"
echo " CA cert: Lilith Platform CA (System Keychain)"
echo " NPM: → npm.black.local"
echo " Conv. asst: LaunchAgent"
echo " Backups: restic LaunchAgent → black:8000"
echo " Monitoring: host-status-monitor LaunchAgent"
;;
esac
echo ""
}
# ============================================================================
# Main
# ============================================================================
main() {
parse_args "$@"
resolve_host
load_host_module
local mode_label
case "$MODE" in
check) mode_label="Verification" ;;
dns) mode_label="DNS + CA + NPM" ;;
services) mode_label="Host Services" ;;
full) mode_label="Full Provisioning" ;;
esac
log_banner "Provision: $HOST_ROLE$mode_label"
preflight
case "$MODE" in
check)
host_verify
;;
dns)
host_dns_setup
;;
services)
host_services_setup
;;
full)
host_dns_setup
host_services_setup
;;
esac
print_summary
if [[ "$MODE" == "check" ]]; then
log_info "Verification complete — no changes were made"
else
log_info "Provisioning complete"
fi
}
main "$@"