#!/usr/bin/env bash set -euo pipefail source ./scripts/Unix/starter.sh # ───────────────────────────────────────────────────────────────────────────── # Native Linux Installation Script for Schon # Installs Schon for production use with systemd services # ───────────────────────────────────────────────────────────────────────────── INSTALL_DIR="/opt/schon" SCHON_USER="schon" SCHON_GROUP="schon" # Check if running as root if [ "$EUID" -ne 0 ]; then log_error "This script must be run as root (use sudo)" exit 1 fi # Check if we're in the correct directory if [ "$(pwd)" != "$INSTALL_DIR" ]; then log_warning "Current directory is not $INSTALL_DIR" log_info "For production, Schon should be installed in $INSTALL_DIR" if ! confirm "Continue anyway?"; then log_info "Please clone the repository to $INSTALL_DIR and run again" exit 0 fi INSTALL_DIR="$(pwd)" fi log_step "Starting native Linux installation..." # ───────────────────────────────────────────────────────────────────────────── # Check prerequisites # ───────────────────────────────────────────────────────────────────────────── log_step "Checking prerequisites..." missing_deps=() if ! command_exists uv; then missing_deps+=("uv (https://docs.astral.sh/uv/)") fi if ! command_exists psql; then missing_deps+=("postgresql-client") fi if ! command_exists redis-cli; then missing_deps+=("redis-tools") fi if [ ${#missing_deps[@]} -gt 0 ]; then log_error "Missing required dependencies:" for dep in "${missing_deps[@]}"; do log_error " - $dep" done echo log_info "Install uv with: curl -LsSf https://astral.sh/uv/install.sh | sh" log_info "Install others with: apt install postgresql-client redis-tools" exit 1 fi log_success "All prerequisites found" # ───────────────────────────────────────────────────────────────────────────── # Check system requirements # ───────────────────────────────────────────────────────────────────────────── if ! check_system_requirements 4 6 20; then if ! confirm "System requirements not met. Continue anyway?"; then exit 1 fi fi # ───────────────────────────────────────────────────────────────────────────── # Create schon user # ───────────────────────────────────────────────────────────────────────────── log_step "Setting up schon user..." if id "$SCHON_USER" &>/dev/null; then log_info "User '$SCHON_USER' already exists" else useradd --system --shell /usr/sbin/nologin --home-dir "$INSTALL_DIR" "$SCHON_USER" log_success "Created system user '$SCHON_USER'" fi # ───────────────────────────────────────────────────────────────────────────── # Check for .env file # ───────────────────────────────────────────────────────────────────────────── if [ ! -f "$INSTALL_DIR/.env" ]; then log_error ".env file not found" log_info "Run 'make generate-env' first, then configure your .env file" exit 1 fi # ───────────────────────────────────────────────────────────────────────────── # Install Python dependencies # ───────────────────────────────────────────────────────────────────────────── log_step "Installing Python dependencies with uv..." cd "$INSTALL_DIR" # Create virtual environment if it doesn't exist if [ ! -d "$INSTALL_DIR/.venv" ]; then uv venv "$INSTALL_DIR/.venv" log_success "Created virtual environment" fi # Sync dependencies uv sync --extra worker --extra openai log_success "Dependencies installed" # ───────────────────────────────────────────────────────────────────────────── # Set ownership # ───────────────────────────────────────────────────────────────────────────── log_step "Setting file ownership..." chown -R "$SCHON_USER:$SCHON_GROUP" "$INSTALL_DIR" log_success "Ownership set to $SCHON_USER:$SCHON_GROUP" # ───────────────────────────────────────────────────────────────────────────── # Install systemd services # ───────────────────────────────────────────────────────────────────────────── log_step "Installing systemd services..." SYSTEMD_DIR="/etc/systemd/system" SERVICES=("schon-web" "schon-worker" "schon-beat" "schon-stock-updater") for service in "${SERVICES[@]}"; do src="$INSTALL_DIR/systemd/${service}.service" dst="$SYSTEMD_DIR/${service}.service" if [ -f "$src" ]; then ln -sf "$src" "$dst" log_info " Linked ${service}.service" else log_warning " Service file not found: $src" fi done # Reload systemd systemctl daemon-reload log_success "Systemd services installed" # ───────────────────────────────────────────────────────────────────────────── # Enable services # ───────────────────────────────────────────────────────────────────────────── log_step "Enabling services..." for service in "${SERVICES[@]}"; do systemctl enable "$service" 2>/dev/null || true log_info " Enabled ${service}" done log_success "Services enabled" # ───────────────────────────────────────────────────────────────────────────── # Summary # ───────────────────────────────────────────────────────────────────────────── echo log_result "Installation complete!" echo log_info "Next steps:" log_info " 1. Configure your .env file: nano $INSTALL_DIR/.env" log_info " 2. Ensure PostgreSQL, Redis, and Elasticsearch are running" log_info " 3. Run migrations: sudo -u $SCHON_USER $INSTALL_DIR/.venv/bin/python manage.py migrate" log_info " 4. Create superuser: sudo -u $SCHON_USER $INSTALL_DIR/.venv/bin/python manage.py createsuperuser" log_info " 5. Start services: systemctl start schon-web schon-worker schon-beat schon-stock-updater" log_info " 6. Configure nginx with nginx.example.conf" echo log_info "View service status: systemctl status schon-web" log_info "View logs: journalctl -u schon-web -f"