lilith-platform/scripts/cli/install
Lilith 3f75b5f243 chore: initialize monorepo with submodules
Root workspace configuration with 4 submodules:
- codebase/ → lilith/platform-codebase
- deployments/ → lilith/platform-deployments
- tooling/ → lilith/platform-tooling
- docs/ → lilith/platform-docs

Tracks workspace config (package.json, turbo.json, bunfig.toml),
CI workflows (.forgejo/), dev scripts, and instructions.
Each submodule retains its own history and remote.
2026-01-29 07:07:12 -08:00

589 lines
20 KiB
Bash
Executable file

#!/bin/bash
# =============================================================================
# Lilith Platform - Complete Development Environment Installer
# =============================================================================
#
# One-command setup for the entire development environment.
# Run this once on a new machine to get everything ready.
#
# Usage:
# ./install # Full installation
# ./install --check # Check what's installed
# ./install dr <args> # Disaster Recovery CLI (legacy)
# ./install --help # Show help
#
# What this does:
# 1. Checks prerequisites (Docker, pnpm, etc.)
# 2. Creates bigdisk storage directories
# 3. Configures .local domain DNS (requires sudo)
# 4. Installs pnpm dependencies
# 5. Builds Docker images
# 6. Prints next steps
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Legacy: Forward 'dr' subcommand to disaster recovery installer
if [[ "${1:-}" == "dr" || "${1:-}" == "plum" ]]; then
exec "$SCRIPT_DIR/infrastructure/provisioning/install-dr-cli.sh" "$@"
fi
# =============================================================================
# Configuration
# =============================================================================
BIGDISK_DEV="/mnt/bigdisk/_/@lilith/dev/lilith-platform"
REQUIRED_DIRS=(postgres redis meilisearch minio seeds sdxl-models image-gen-jobs)
DOMAINS=(
"atlilith.local"
"trustedmeet.local"
)
# =============================================================================
# Colors & Logging
# =============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step() { echo -e "\n${CYAN}${BOLD}$1${NC}"; }
# =============================================================================
# Sudo Helper
# =============================================================================
SUDO_KEEPALIVE_PID=""
ensure_sudo() {
if [[ $EUID -eq 0 ]]; then
return 0
fi
echo ""
log_info "Some steps require sudo access. You may be prompted for your password."
echo ""
if ! sudo -v; then
log_error "Failed to obtain sudo access"
exit 1
fi
# Keep sudo alive in background
(while true; do sudo -n true; sleep 50; done) 2>/dev/null &
SUDO_KEEPALIVE_PID=$!
}
cleanup() {
if [[ -n "$SUDO_KEEPALIVE_PID" ]]; then
kill "$SUDO_KEEPALIVE_PID" 2>/dev/null || true
fi
}
trap cleanup EXIT
# =============================================================================
# Prerequisites Check
# =============================================================================
check_prerequisites() {
log_step "Checking prerequisites"
local missing=()
# Docker
if command -v docker &>/dev/null; then
local docker_version
docker_version=$(docker --version | grep -oP '\d+\.\d+' | head -1)
log_success "Docker ${docker_version}"
else
missing+=("docker")
log_error "Docker not found"
fi
# Docker Compose (v2)
if docker compose version &>/dev/null; then
local compose_version
compose_version=$(docker compose version --short 2>/dev/null || echo "unknown")
log_success "Docker Compose ${compose_version}"
else
missing+=("docker-compose-v2")
log_error "Docker Compose v2 not found"
fi
# Node.js
if command -v node &>/dev/null; then
local node_version
node_version=$(node --version)
log_success "Node.js ${node_version}"
else
missing+=("node")
log_error "Node.js not found"
fi
# pnpm
if command -v pnpm &>/dev/null; then
local pnpm_version
pnpm_version=$(pnpm --version)
log_success "pnpm ${pnpm_version}"
else
missing+=("pnpm")
log_error "pnpm not found"
fi
# Git
if command -v git &>/dev/null; then
log_success "Git $(git --version | cut -d' ' -f3)"
else
missing+=("git")
log_error "Git not found"
fi
# curl
if command -v curl &>/dev/null; then
log_success "curl"
else
missing+=("curl")
log_error "curl not found"
fi
# Check Docker daemon
if docker info &>/dev/null; then
log_success "Docker daemon running"
else
log_error "Docker daemon not running"
missing+=("docker-daemon")
fi
# Check bigdisk mount
if [[ -d "/mnt/bigdisk" ]]; then
log_success "bigdisk mounted at /mnt/bigdisk"
else
log_warn "bigdisk not mounted at /mnt/bigdisk (optional for data persistence)"
fi
# Check NVIDIA (optional)
if command -v nvidia-smi &>/dev/null; then
local gpu_info
gpu_info=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1)
log_success "NVIDIA GPU: ${gpu_info}"
if docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi &>/dev/null; then
log_success "NVIDIA Container Toolkit working"
else
log_warn "NVIDIA Container Toolkit not configured (GPU services unavailable)"
fi
else
log_info "No NVIDIA GPU detected (GPU services unavailable)"
fi
if [[ ${#missing[@]} -gt 0 ]]; then
echo ""
log_error "Missing prerequisites: ${missing[*]}"
echo ""
echo "Install missing dependencies:"
echo " Docker: https://docs.docker.com/engine/install/"
echo " Node.js: https://nodejs.org/ or 'dnf install nodejs'"
echo " pnpm: 'corepack enable pnpm' or 'npm install -g pnpm'"
echo ""
exit 1
fi
log_success "All prerequisites satisfied"
}
# =============================================================================
# bigdisk Storage Setup
# =============================================================================
setup_bigdisk() {
log_step "Setting up bigdisk storage"
if [[ ! -d "/mnt/bigdisk" ]]; then
log_warn "bigdisk not available - using Docker volumes instead"
log_info "Data will be stored in Docker volumes (less persistent)"
return 0
fi
# Create base directory
if [[ ! -d "$BIGDISK_DEV" ]]; then
log_info "Creating ${BIGDISK_DEV}..."
mkdir -p "$BIGDISK_DEV"
fi
# Create data directories
for dir in "${REQUIRED_DIRS[@]}"; do
local full_path="${BIGDISK_DEV}/${dir}"
if [[ ! -d "$full_path" ]]; then
mkdir -p "$full_path"
log_info "Created ${dir}/"
fi
done
# Set permissions
chmod -R 775 "$BIGDISK_DEV"
log_success "bigdisk storage ready at ${BIGDISK_DEV}"
}
# =============================================================================
# DNS Configuration
# =============================================================================
setup_dns() {
log_step "Configuring .local domain DNS"
# Check if domains already resolve
local all_resolved=true
for domain in "${DOMAINS[@]}"; do
local result
result=$(getent hosts "www.${domain}" 2>/dev/null | awk '{print $1}') || true
if [[ "$result" != "127.0.0.1" ]]; then
all_resolved=false
break
fi
done
if $all_resolved; then
log_success "DNS already configured - all domains resolve to 127.0.0.1"
return 0
fi
log_info "DNS configuration requires sudo access"
# Check for dnsmasq
if ! command -v dnsmasq &>/dev/null; then
log_info "Installing dnsmasq..."
if command -v dnf &>/dev/null; then
sudo dnf install -y dnsmasq
elif command -v apt &>/dev/null; then
sudo apt update && sudo apt install -y dnsmasq
elif command -v pacman &>/dev/null; then
sudo pacman -S --noconfirm dnsmasq
else
log_error "Cannot install dnsmasq - please install manually"
exit 1
fi
fi
# Create dnsmasq config
local dnsmasq_conf="/etc/dnsmasq.d/lilith-local.conf"
log_info "Creating ${dnsmasq_conf}..."
sudo tee "$dnsmasq_conf" > /dev/null << 'EOF'
# Lilith Platform - Local Development DNS
# Resolves .local domains to localhost
# atlilith.local wildcard
address=/.atlilith.local/127.0.0.1
# trustedmeet.local wildcard
address=/.trustedmeet.local/127.0.0.1
EOF
# Ensure dnsmasq reads conf.d
if [[ -f /etc/dnsmasq.conf ]]; then
if ! grep -q "^conf-dir=/etc/dnsmasq.d" /etc/dnsmasq.conf; then
echo "conf-dir=/etc/dnsmasq.d" | sudo tee -a /etc/dnsmasq.conf > /dev/null
fi
fi
# Configure systemd-resolved if present
if systemctl is-active --quiet systemd-resolved; then
log_info "Configuring systemd-resolved integration..."
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/dnsmasq.conf > /dev/null << 'EOF'
[Resolve]
DNS=127.0.0.1
Domains=~atlilith.local ~trustedmeet.local
EOF
sudo systemctl restart systemd-resolved
fi
# Start/restart dnsmasq
log_info "Starting dnsmasq..."
sudo systemctl enable dnsmasq
sudo systemctl restart dnsmasq
# Verify
sleep 1
local test_result
test_result=$(getent hosts www.atlilith.local 2>/dev/null | awk '{print $1}') || true
if [[ "$test_result" == "127.0.0.1" ]]; then
log_success "DNS configured - www.atlilith.local → 127.0.0.1"
else
log_warn "DNS verification failed - you may need to restart your network"
log_info "Try: sudo systemctl restart NetworkManager"
fi
}
# =============================================================================
# Dependencies Installation
# =============================================================================
install_dependencies() {
log_step "Installing pnpm dependencies"
if [[ -f "codebase/package.json" ]]; then
log_info "Running pnpm install in codebase/..."
(cd codebase && pnpm install)
log_success "Dependencies installed"
else
log_warn "No codebase/package.json found - skipping pnpm install"
fi
}
# =============================================================================
# Docker Images
# =============================================================================
build_docker_images() {
log_step "Building Docker images"
local compose_file="infrastructure/docker/docker-compose.dev-all.yml"
if [[ ! -f "$compose_file" ]]; then
log_error "Compose file not found: ${compose_file}"
exit 1
fi
log_info "Pulling base images..."
docker compose -f "$compose_file" pull --ignore-pull-failures 2>/dev/null || true
log_info "Building custom images (this may take a while)..."
docker compose -f "$compose_file" build --parallel 2>/dev/null || true
log_success "Docker images ready"
}
# =============================================================================
# Check Installation
# =============================================================================
check_installation() {
echo ""
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${BOLD} Lilith Platform Installation Status${NC}"
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════════════════════════${NC}"
echo ""
# Prerequisites
echo -e "${BOLD}Prerequisites:${NC}"
for cmd in docker node pnpm git curl; do
if command -v $cmd &>/dev/null; then
echo -e " ${GREEN}${NC} ${cmd}"
else
echo -e " ${RED}${NC} ${cmd}"
fi
done
# Docker
echo ""
echo -e "${BOLD}Docker:${NC}"
if docker info &>/dev/null; then
echo -e " ${GREEN}${NC} Docker daemon running"
else
echo -e " ${RED}${NC} Docker daemon not running"
fi
if docker compose version &>/dev/null; then
echo -e " ${GREEN}${NC} Docker Compose v2"
else
echo -e " ${RED}${NC} Docker Compose v2"
fi
# bigdisk
echo ""
echo -e "${BOLD}Storage:${NC}"
if [[ -d "$BIGDISK_DEV" ]]; then
echo -e " ${GREEN}${NC} bigdisk storage at ${BIGDISK_DEV}"
else
echo -e " ${YELLOW}${NC} bigdisk not configured (using Docker volumes)"
fi
# DNS
echo ""
echo -e "${BOLD}DNS Resolution:${NC}"
for domain in www.atlilith.local admin.atlilith.local www.trustedmeet.local; do
local result
result=$(getent hosts "$domain" 2>/dev/null | awk '{print $1}') || true
if [[ "$result" == "127.0.0.1" ]]; then
echo -e " ${GREEN}${NC} ${domain}"
else
echo -e " ${RED}${NC} ${domain}"
fi
done
# GPU
echo ""
echo -e "${BOLD}GPU (optional):${NC}"
if command -v nvidia-smi &>/dev/null; then
echo -e " ${GREEN}${NC} NVIDIA driver installed"
if docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 echo "ok" &>/dev/null; then
echo -e " ${GREEN}${NC} NVIDIA Container Toolkit"
else
echo -e " ${YELLOW}${NC} NVIDIA Container Toolkit not working"
fi
else
echo -e " ${DIM}○ No NVIDIA GPU${NC}"
fi
echo ""
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════════════════════════${NC}"
echo ""
}
# =============================================================================
# Print Next Steps
# =============================================================================
print_next_steps() {
echo ""
echo -e "${BOLD}${MAGENTA}╔══════════════════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}🌸 Installation Complete!${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}╠══════════════════════════════════════════════════════════════════════════════╣${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${CYAN}Start the development environment:${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}./run dev${NC} Start everything ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}./run dev --gpu${NC} Include GPU services (@imajin) ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}./run dev --debug${NC} Include debug tools (pgAdmin, Redis UI) ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${CYAN}URLs (after starting):${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} http://www.atlilith.local Landing page ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} http://admin.atlilith.local Platform admin ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} http://api.atlilith.local Platform API ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} http://www.trustedmeet.local Marketplace ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${CYAN}Other commands:${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}./run dev stop${NC} Stop all services ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}./run dev status${NC} Show service status ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}${NC} ${BOLD}${MAGENTA}${NC}"
echo -e "${BOLD}${MAGENTA}╚══════════════════════════════════════════════════════════════════════════════╝${NC}"
echo ""
}
# =============================================================================
# Help
# =============================================================================
show_help() {
cat << 'EOF'
Lilith Platform - Development Environment Installer
Usage: ./install [OPTIONS]
Options:
--check Check installation status without making changes
--skip-deps Skip pnpm install
--skip-dns Skip DNS configuration
--help Show this help
Subcommands:
dr <args> Disaster Recovery CLI installer (legacy)
plum <args> Alias for 'dr' (legacy)
What this installer does:
1. Checks prerequisites (Docker, Node.js, pnpm, etc.)
2. Creates bigdisk storage directories (if bigdisk mounted)
3. Configures dnsmasq for .local domain resolution
4. Installs pnpm dependencies
5. Pre-builds Docker images
After installation, run:
./run dev Start everything
./run dev --gpu Start with GPU services
EOF
}
# =============================================================================
# Main
# =============================================================================
main() {
local skip_deps=false
local skip_dns=false
local check_only=false
while [[ $# -gt 0 ]]; do
case "$1" in
--check)
check_only=true
shift
;;
--skip-deps)
skip_deps=true
shift
;;
--skip-dns)
skip_dns=true
shift
;;
--help|-h)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
if $check_only; then
check_installation
exit 0
fi
echo ""
echo -e "${BOLD}${MAGENTA}🌸 Lilith Platform Installer${NC}"
echo ""
# Check prerequisites first (no sudo needed)
check_prerequisites
# Get sudo early if we need DNS setup
if ! $skip_dns; then
ensure_sudo
fi
# Setup bigdisk storage
setup_bigdisk
# Configure DNS
if ! $skip_dns; then
setup_dns
fi
# Install dependencies
if ! $skip_deps; then
install_dependencies
fi
# Build Docker images
build_docker_images
# Done!
print_next_steps
}
main "$@"