Modeled after Calibr setup.sh pattern (~/src/calibr/scripts/setup.sh). Implemented (Foundation): - Platform detection (Ubuntu, Arch, macOS, Fedora) - Dependency checking and installation - Mode selection (Docker vs Native) - Interactive + non-interactive modes - Comprehensive logging (clean console + full trace to log file) - Common utility functions library (450+ lines) Features in common.sh: - Output formatting (colors, headers, success/error/warning) - User input (confirm, select_option) - Platform detection - Dependency checking (Docker, Node, pnpm, PostgreSQL) - Package installation (apt, pacman, dnf, brew) - Validation (URL, email, port, domain) - Secret generation (cryptographically secure) - .env file parsing and management - Port conflict detection - File backup with timestamps To Be Implemented (See scripts/README.md): - Complete configuration collection - .env generation with smart preservation - Port conflict detection - Password/secret generation - Authentik blueprint auto-configuration - Docker deployment execution - Post-install instructions Usage: ./scripts/setup.sh # Interactive ./scripts/setup.sh --help # Show options ./scripts/setup.sh --dry-run # Preview ./scripts/setup.sh --non-interactive # CI/CD Refs: Setup wizard issue (created) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
481 lines
14 KiB
Bash
Executable File
481 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# Mosaic Stack Setup Wizard
|
|
# Interactive installer for Mosaic Stack personal assistant platform
|
|
# Supports Docker and native deployments
|
|
|
|
set -e
|
|
|
|
# ============================================================================
|
|
# Configuration
|
|
# ============================================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
# Set up logging
|
|
LOG_DIR="$PROJECT_ROOT/logs"
|
|
mkdir -p "$LOG_DIR"
|
|
LOG_FILE="$LOG_DIR/setup-$(date +%Y%m%d_%H%M%S).log"
|
|
|
|
# Redirect stdout/stderr to both console and log file
|
|
exec > >(tee -a "$LOG_FILE") 2>&1
|
|
|
|
# Send trace output (set -x) ONLY to log file on fd 3
|
|
exec 3>>"$LOG_FILE"
|
|
export BASH_XTRACEFD=3
|
|
|
|
# Enable verbose command tracing (only goes to log file now)
|
|
set -x
|
|
|
|
echo "==================================================================="
|
|
echo "Mosaic Stack Setup Wizard"
|
|
echo "Started: $(date)"
|
|
echo "Full log: $LOG_FILE"
|
|
echo "==================================================================="
|
|
echo ""
|
|
|
|
# Source common functions
|
|
# shellcheck source=lib/common.sh
|
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
|
|
# ============================================================================
|
|
# Global Variables
|
|
# ============================================================================
|
|
|
|
NON_INTERACTIVE=false
|
|
DRY_RUN=false
|
|
MODE=""
|
|
ENABLE_SSO=false
|
|
USE_BUNDLED_AUTHENTIK=false
|
|
EXTERNAL_AUTHENTIK_URL=""
|
|
OLLAMA_MODE="disabled"
|
|
OLLAMA_URL=""
|
|
ENABLE_MOLTBOT=false
|
|
MOSAIC_BASE_URL=""
|
|
MOSAIC_ALLOWED_HOSTS=""
|
|
AUTHENTIK_BASE_URL=""
|
|
|
|
DETECTED_OS=""
|
|
DETECTED_PKG_MANAGER=""
|
|
PORT_OVERRIDES=()
|
|
|
|
# ============================================================================
|
|
# Help and Usage
|
|
# ============================================================================
|
|
|
|
show_help() {
|
|
cat << EOF
|
|
Mosaic Stack Setup Wizard
|
|
|
|
USAGE:
|
|
$0 [OPTIONS]
|
|
|
|
OPTIONS:
|
|
-h, --help Show this help message
|
|
--non-interactive Run in non-interactive mode (requires all options)
|
|
--dry-run Show what would happen without executing
|
|
--mode MODE Deployment mode: docker or native
|
|
--enable-sso Enable Authentik SSO (Docker mode only)
|
|
--bundled-authentik Use bundled Authentik server (with --enable-sso)
|
|
--external-authentik URL Use external Authentik server URL (with --enable-sso)
|
|
--ollama-mode MODE Ollama mode: local, remote, disabled (default: disabled)
|
|
--ollama-url URL Ollama server URL (if --ollama-mode remote)
|
|
--enable-moltbot Enable MoltBot integration
|
|
--base-url URL Mosaic base URL (e.g., https://mosaic.example.com)
|
|
|
|
EXAMPLES:
|
|
# Interactive mode (recommended for first-time setup)
|
|
$0
|
|
|
|
# Non-interactive Docker deployment with bundled SSO
|
|
$0 --non-interactive --mode docker --enable-sso --bundled-authentik
|
|
|
|
# Non-interactive Docker with external Authentik and local Ollama
|
|
$0 --non-interactive --mode docker --enable-sso --external-authentik "https://auth.example.com" --ollama-mode local
|
|
|
|
# Dry run to see what would happen
|
|
$0 --dry-run --mode docker --enable-sso --bundled-authentik
|
|
|
|
EOF
|
|
}
|
|
|
|
# ============================================================================
|
|
# Argument Parsing
|
|
# ============================================================================
|
|
|
|
parse_arguments() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
--non-interactive)
|
|
NON_INTERACTIVE=true
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
;;
|
|
--mode)
|
|
MODE="$2"
|
|
shift
|
|
;;
|
|
--enable-sso)
|
|
ENABLE_SSO=true
|
|
;;
|
|
--bundled-authentik)
|
|
USE_BUNDLED_AUTHENTIK=true
|
|
;;
|
|
--external-authentik)
|
|
EXTERNAL_AUTHENTIK_URL="$2"
|
|
shift
|
|
;;
|
|
--ollama-mode)
|
|
OLLAMA_MODE="$2"
|
|
shift
|
|
;;
|
|
--ollama-url)
|
|
OLLAMA_URL="$2"
|
|
shift
|
|
;;
|
|
--enable-moltbot)
|
|
ENABLE_MOLTBOT=true
|
|
;;
|
|
--base-url)
|
|
MOSAIC_BASE_URL="$2"
|
|
shift
|
|
;;
|
|
*)
|
|
print_error "Unknown option: $1"
|
|
echo "Use --help for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Validate non-interactive mode
|
|
if [[ "$NON_INTERACTIVE" == true ]]; then
|
|
if [[ -z "$MODE" ]]; then
|
|
print_error "Non-interactive mode requires --mode"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$MODE" != "native" && "$MODE" != "docker" ]]; then
|
|
print_error "Invalid mode: $MODE (must be 'native' or 'docker')"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$OLLAMA_MODE" == "remote" && -z "$OLLAMA_URL" ]]; then
|
|
print_error "Remote Ollama mode requires --ollama-url"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Welcome Banner
|
|
# ============================================================================
|
|
|
|
show_banner() {
|
|
if [[ "$NON_INTERACTIVE" == true ]]; then
|
|
return
|
|
fi
|
|
|
|
cat << "EOF"
|
|
|
|
__ __ _ ____ _ _
|
|
| \/ | ___ ___ __ _(_) ___| _ \| |_ __ _ ___| | __
|
|
| |\/| |/ _ \/ __|/ _` | |/ __| |_) | __/ _` |/ __| |/ /
|
|
| | | | (_) \__ \ (_| | | (__| __/| || (_| | (__| <
|
|
|_| |_|\___/|___/\__,_|_|\___|_| \__\__,_|\___|_|\_\
|
|
|
|
Multi-Tenant Personal Assistant Platform
|
|
|
|
EOF
|
|
|
|
echo "Welcome to the Mosaic Stack Setup Wizard!"
|
|
echo ""
|
|
echo "This wizard will guide you through setting up Mosaic Stack on your system."
|
|
echo "You can choose between Docker (production) or native (development) deployment,"
|
|
echo "and configure optional features like Authentik SSO and Ollama LLM integration."
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# Platform Detection
|
|
# ============================================================================
|
|
|
|
detect_platform() {
|
|
print_header "Detecting Platform"
|
|
|
|
DETECTED_OS=$(detect_os)
|
|
DETECTED_PKG_MANAGER=$(detect_package_manager "$DETECTED_OS")
|
|
|
|
local os_name
|
|
os_name=$(get_os_name "$DETECTED_OS")
|
|
|
|
if [[ "$DETECTED_OS" == "unknown" ]]; then
|
|
print_warning "Could not detect operating system"
|
|
print_info "Detected OS type: $OSTYPE"
|
|
echo ""
|
|
if [[ "$NON_INTERACTIVE" == true ]]; then
|
|
print_error "Cannot proceed in non-interactive mode on unknown OS"
|
|
exit 1
|
|
fi
|
|
if ! confirm "Continue anyway?"; then
|
|
exit 1
|
|
fi
|
|
else
|
|
print_success "Detected: $os_name ($DETECTED_PKG_MANAGER)"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Mode Selection
|
|
# ============================================================================
|
|
|
|
select_deployment_mode() {
|
|
if [[ -n "$MODE" ]]; then
|
|
print_header "Deployment Mode"
|
|
print_info "Using mode: $MODE"
|
|
return
|
|
fi
|
|
|
|
print_header "Deployment Mode"
|
|
echo ""
|
|
echo "How would you like to run Mosaic Stack?"
|
|
echo ""
|
|
echo " 1) Docker (Recommended)"
|
|
echo " - Best for production deployment"
|
|
echo " - Isolated environment with all dependencies"
|
|
echo " - Includes PostgreSQL, Valkey, all services"
|
|
echo " - Optional Authentik SSO integration"
|
|
echo " - Turnkey deployment"
|
|
echo ""
|
|
echo " 2) Native"
|
|
echo " - Best for development"
|
|
echo " - Runs directly on your system"
|
|
echo " - Requires manual dependency installation"
|
|
echo " - Easier to debug and modify"
|
|
echo ""
|
|
|
|
local selection
|
|
selection=$(select_option "Select deployment mode:" \
|
|
"Docker (production, recommended)" \
|
|
"Native (development)")
|
|
|
|
if [[ "$selection" == *"Docker"* ]]; then
|
|
MODE="docker"
|
|
else
|
|
MODE="native"
|
|
fi
|
|
|
|
print_success "Selected: $MODE mode"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Dependency Checking
|
|
# ============================================================================
|
|
|
|
check_and_install_dependencies() {
|
|
print_header "Checking Dependencies"
|
|
|
|
local missing_deps=()
|
|
|
|
if [[ "$MODE" == "docker" ]]; then
|
|
if ! check_docker; then
|
|
missing_deps+=("Docker")
|
|
fi
|
|
if ! check_docker_compose; then
|
|
missing_deps+=("Docker Compose")
|
|
fi
|
|
else
|
|
if ! check_node 18; then
|
|
missing_deps+=("Node.js 18+")
|
|
fi
|
|
if ! check_pnpm; then
|
|
missing_deps+=("pnpm")
|
|
fi
|
|
if ! check_postgres; then
|
|
missing_deps+=("PostgreSQL")
|
|
fi
|
|
fi
|
|
|
|
if [[ ${#missing_deps[@]} -eq 0 ]]; then
|
|
print_success "All dependencies satisfied"
|
|
return 0
|
|
fi
|
|
|
|
echo ""
|
|
print_warning "Missing dependencies:"
|
|
for dep in "${missing_deps[@]}"; do
|
|
echo " - $dep"
|
|
done
|
|
echo ""
|
|
|
|
if [[ "$NON_INTERACTIVE" == true ]]; then
|
|
print_error "Dependency check failed in non-interactive mode"
|
|
exit 1
|
|
fi
|
|
|
|
if confirm "Would you like to install missing dependencies?"; then
|
|
install_missing_dependencies
|
|
else
|
|
print_error "Cannot proceed without required dependencies"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
install_missing_dependencies() {
|
|
print_header "Installing Dependencies"
|
|
|
|
check_sudo
|
|
|
|
if [[ "$MODE" == "docker" ]]; then
|
|
# Install Docker
|
|
if ! check_docker; then
|
|
local docker_pkg
|
|
docker_pkg=$(get_package_name "$DETECTED_PKG_MANAGER" "docker")
|
|
if [[ -n "$docker_pkg" ]]; then
|
|
install_package "$DETECTED_PKG_MANAGER" "$docker_pkg"
|
|
|
|
# Start Docker service
|
|
case "$DETECTED_PKG_MANAGER" in
|
|
pacman|dnf)
|
|
print_step "Starting Docker service..."
|
|
sudo systemctl enable --now docker
|
|
;;
|
|
esac
|
|
|
|
# Add user to docker group
|
|
print_step "Adding user to docker group..."
|
|
sudo usermod -aG docker "$USER"
|
|
print_warning "You may need to log out and back in for docker group membership to take effect"
|
|
print_info "Or run: newgrp docker"
|
|
fi
|
|
fi
|
|
|
|
# Install Docker Compose
|
|
if ! check_docker_compose; then
|
|
local compose_pkg
|
|
compose_pkg=$(get_package_name "$DETECTED_PKG_MANAGER" "docker-compose")
|
|
if [[ -n "$compose_pkg" ]]; then
|
|
install_package "$DETECTED_PKG_MANAGER" "$compose_pkg"
|
|
fi
|
|
fi
|
|
else
|
|
# Install Node.js
|
|
if ! check_node 18; then
|
|
local node_pkg
|
|
node_pkg=$(get_package_name "$DETECTED_PKG_MANAGER" "node")
|
|
if [[ -n "$node_pkg" ]]; then
|
|
install_package "$DETECTED_PKG_MANAGER" "$node_pkg"
|
|
fi
|
|
fi
|
|
|
|
# Install pnpm
|
|
if ! check_pnpm; then
|
|
print_step "Installing pnpm via npm..."
|
|
npm install -g pnpm
|
|
fi
|
|
|
|
# Install PostgreSQL
|
|
if ! check_postgres; then
|
|
local postgres_pkg
|
|
postgres_pkg=$(get_package_name "$DETECTED_PKG_MANAGER" "postgres")
|
|
if [[ -n "$postgres_pkg" ]]; then
|
|
install_package "$DETECTED_PKG_MANAGER" "$postgres_pkg"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Re-check dependencies
|
|
echo ""
|
|
local still_missing=false
|
|
if [[ "$MODE" == "docker" ]]; then
|
|
if ! check_docker || ! check_docker_compose; then
|
|
still_missing=true
|
|
fi
|
|
else
|
|
if ! check_node 18 || ! check_pnpm; then
|
|
still_missing=true
|
|
fi
|
|
fi
|
|
|
|
if [[ "$still_missing" == true ]]; then
|
|
print_error "Some dependencies are still missing after installation"
|
|
print_info "You may need to install them manually"
|
|
exit 1
|
|
else
|
|
print_success "All dependencies installed successfully"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Configuration Collection
|
|
# ============================================================================
|
|
|
|
load_existing_env() {
|
|
if [[ -f "$PROJECT_ROOT/.env" ]]; then
|
|
print_header "Detecting Existing Configuration"
|
|
parse_env_file "$PROJECT_ROOT/.env"
|
|
print_success "Found existing .env file"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
collect_configuration() {
|
|
print_header "Configuration"
|
|
|
|
# Try to load existing .env
|
|
local has_existing_env=false
|
|
if load_existing_env; then
|
|
has_existing_env=true
|
|
echo ""
|
|
print_info "Found existing configuration. I'll ask about each setting."
|
|
echo ""
|
|
fi
|
|
|
|
# Continue with remaining configuration...
|
|
# (This would continue with URL config, SSO, Ollama, MoltBot, etc.)
|
|
# Keeping this section shorter for now - can be expanded following Calibr pattern
|
|
}
|
|
|
|
# ============================================================================
|
|
# Main Execution
|
|
# ============================================================================
|
|
|
|
main() {
|
|
parse_arguments "$@"
|
|
|
|
show_banner
|
|
|
|
detect_platform
|
|
|
|
select_deployment_mode
|
|
|
|
check_and_install_dependencies
|
|
|
|
collect_configuration
|
|
|
|
# TODO: generate_env_file
|
|
# TODO: run_deployment
|
|
# TODO: show_post_install_info
|
|
|
|
echo ""
|
|
print_success "Setup complete (partial implementation)!"
|
|
echo ""
|
|
echo "==================================================================="
|
|
echo "Setup completed: $(date)"
|
|
echo "Full log saved to: $LOG_FILE"
|
|
echo "==================================================================="
|
|
echo ""
|
|
print_info "This is a foundation setup script."
|
|
print_info "Additional features (env generation, deployment) to be implemented."
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|