#!/usr/bin/env bash # Automated developer environment setup for Stella Ops (Linux/macOS). # # Usage: # ./scripts/setup.sh [--skip-build] [--infra-only] [--images-only] [--skip-images] set -euo pipefail # ─── Parse flags ──────────────────────────────────────────────────────────── SKIP_BUILD=false INFRA_ONLY=false IMAGES_ONLY=false SKIP_IMAGES=false for arg in "$@"; do case "$arg" in --skip-build) SKIP_BUILD=true ;; --infra-only) INFRA_ONLY=true ;; --images-only) IMAGES_ONLY=true ;; --skip-images) SKIP_IMAGES=true ;; -h|--help) echo "Usage: $0 [--skip-build] [--infra-only] [--images-only] [--skip-images]" exit 0 ;; *) echo "Unknown flag: $arg" >&2; exit 1 ;; esac done ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true) if [[ -z "$ROOT" ]]; then echo "ERROR: Not inside a git repository." >&2 exit 1 fi COMPOSE_DIR="${ROOT}/devops/compose" # ─── Helpers ──────────────────────────────────────────────────────────────── step() { printf '\n\033[1;36m>> %s\033[0m\n' "$1"; } ok() { printf ' \033[0;32m[OK]\033[0m %s\n' "$1"; } warn() { printf ' \033[0;33m[WARN]\033[0m %s\n' "$1"; } fail() { printf ' \033[0;31m[FAIL]\033[0m %s\n' "$1"; } has_cmd() { command -v "$1" &>/dev/null; } # ─── 1. Check prerequisites ──────────────────────────────────────────────── check_prerequisites() { step 'Checking prerequisites' local all_good=true # dotnet if has_cmd dotnet; then local v; v=$(dotnet --version 2>/dev/null) if [[ "$v" =~ ^10\. ]]; then ok "dotnet $v" else fail "dotnet $v found, but 10.x is required" all_good=false fi else fail 'dotnet SDK not found. Install .NET 10 SDK.' all_good=false fi # node if has_cmd node; then local v; v=$(node --version 2>/dev/null | sed 's/^v//') local major; major=$(echo "$v" | cut -d. -f1) if (( major >= 20 )); then ok "node $v" else fail "node $v found, but 20+ is required" all_good=false fi else fail 'node not found. Install Node.js 20+.' all_good=false fi # npm if has_cmd npm; then local v; v=$(npm --version 2>/dev/null) local major; major=$(echo "$v" | cut -d. -f1) if (( major >= 10 )); then ok "npm $v" else fail "npm $v found, but 10+ is required" all_good=false fi else fail 'npm not found.' all_good=false fi # docker if has_cmd docker; then ok "docker: $(docker --version 2>/dev/null)" else fail 'docker not found. Install Docker.' all_good=false fi # docker compose if docker compose version &>/dev/null; then ok 'docker compose available' else fail 'docker compose not available. Install Compose V2.' all_good=false fi # git if has_cmd git; then ok "$(git --version 2>/dev/null)" else fail 'git not found.' all_good=false fi if [[ "$all_good" != "true" ]]; then echo 'ERROR: Prerequisites not met. Install missing tools and re-run.' >&2 exit 1 fi } # ─── 2. Check hosts file ─────────────────────────────────────────────────── check_hosts() { step 'Checking hosts file for stella-ops.local entries' if grep -q 'stella-ops\.local' /etc/hosts 2>/dev/null; then ok 'stella-ops.local entries found in /etc/hosts' else warn 'stella-ops.local entries NOT found in /etc/hosts.' echo ' Add the hosts block from docs/dev/DEV_ENVIRONMENT_SETUP.md section 2' echo ' to /etc/hosts (use sudo).' fi } # ─── 3. Ensure .env ──────────────────────────────────────────────────────── ensure_env() { step 'Ensuring .env file exists' local env_file="${COMPOSE_DIR}/.env" local env_example="${COMPOSE_DIR}/env/stellaops.env.example" if [[ -f "$env_file" ]]; then ok ".env already exists at $env_file" elif [[ -f "$env_example" ]]; then cp "$env_example" "$env_file" ok "Copied $env_example -> $env_file" warn 'Review .env and change POSTGRES_PASSWORD at minimum.' else fail "Neither .env nor env/stellaops.env.example found in $COMPOSE_DIR" exit 1 fi } # ─── 4. Start infrastructure ─────────────────────────────────────────────── start_infra() { step 'Starting infrastructure containers (docker-compose.dev.yml)' cd "$COMPOSE_DIR" docker compose -f docker-compose.dev.yml up -d echo ' Waiting for containers to become healthy...' local max_wait=120 local elapsed=0 while (( elapsed < max_wait )); do local all_healthy=true while IFS= read -r line; do [[ -z "$line" ]] && continue local health; health=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin).get('Health',''))" 2>/dev/null || true) if [[ -n "$health" && "$health" != "healthy" ]]; then all_healthy=false fi done < <(docker compose -f docker-compose.dev.yml ps --format json 2>/dev/null) if [[ "$all_healthy" == "true" && $elapsed -gt 5 ]]; then ok 'All infrastructure containers healthy' cd "$ROOT" return fi sleep 5 elapsed=$((elapsed + 5)) done warn "Timed out waiting for healthy status after ${max_wait}s." cd "$ROOT" } # ─── 5. Build .NET solutions ─────────────────────────────────────────────── build_solutions() { step 'Building all .NET solutions' local script="${ROOT}/scripts/build-all-solutions.sh" if [[ -x "$script" ]]; then "$script" ok '.NET solutions built successfully' elif [[ -f "$script" ]]; then bash "$script" ok '.NET solutions built successfully' else warn "Build script not found at $script. Skipping .NET build." fi } # ─── 6. Build Docker images ──────────────────────────────────────────────── build_images() { step 'Building Docker images' local script="${ROOT}/devops/docker/build-all.sh" if [[ -x "$script" ]]; then "$script" ok 'Docker images built successfully' elif [[ -f "$script" ]]; then bash "$script" ok 'Docker images built successfully' else warn "Build script not found at $script. Skipping image build." fi } # ─── 7. Start full platform ──────────────────────────────────────────────── start_platform() { step 'Starting full Stella Ops platform' cd "$COMPOSE_DIR" docker compose -f docker-compose.stella-ops.yml up -d ok 'Platform services started' cd "$ROOT" } # ─── 8. Smoke test ───────────────────────────────────────────────────────── smoke_test() { step 'Running smoke tests' if docker exec stellaops-dev-postgres pg_isready -U stellaops &>/dev/null; then ok 'PostgreSQL' else warn 'PostgreSQL not responding' fi local pong; pong=$(docker exec stellaops-dev-valkey valkey-cli ping 2>/dev/null || true) if [[ "$pong" == "PONG" ]]; then ok 'Valkey' else warn 'Valkey not responding' fi } # ─── Main ─────────────────────────────────────────────────────────────────── echo '=============================================' echo ' Stella Ops Developer Environment Setup' echo '=============================================' check_prerequisites check_hosts if [[ "$IMAGES_ONLY" == "true" ]]; then build_images echo '' echo 'Done (images only).' exit 0 fi ensure_env start_infra if [[ "$INFRA_ONLY" == "true" ]]; then smoke_test echo '' echo 'Done (infra only). Infrastructure is running.' exit 0 fi if [[ "$SKIP_BUILD" != "true" ]]; then build_solutions fi if [[ "$SKIP_IMAGES" != "true" ]]; then build_images fi start_platform smoke_test echo '' echo '=============================================' echo ' Setup complete!' echo ' Platform: https://stella-ops.local' echo ' Docs: docs/dev/DEV_ENVIRONMENT_SETUP.md' echo '============================================='