#!/usr/bin/env bash # TestAjanı başlatıcı (macOS + Linux). # Kullanım: ./scripts/start.sh [--rebuild] [--model MODEL] [--no-browser] set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" MODEL="${OLLAMA_MODEL:-qwen2.5-coder:7b}" REBUILD=0 OPEN_BROWSER=1 APP_URL="http://localhost:8000" while [ $# -gt 0 ]; do case "$1" in --rebuild) REBUILD=1 ;; --model) MODEL="$2"; shift ;; --no-browser) OPEN_BROWSER=0 ;; -h|--help) sed -n '2,3p' "$0"; exit 0 ;; *) echo "Bilinmeyen seçenek: $1"; exit 2 ;; esac shift done OS="$(uname -s)" step() { printf '\n\033[1;34m==>\033[0m %s\n' "$1"; } ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; } fail() { printf '\n\033[31mHATA:\033[0m %s\n' "$1"; exit 1; } http_ok() { curl -fsS -m 3 -o /dev/null "$1" 2>/dev/null; } # ---------------------------------------------------------------- 1. Docker kurulu mu? step "Docker kontrol ediliyor" if ! command -v docker >/dev/null 2>&1; then if [ "$OS" = "Darwin" ]; then echo " Docker Desktop kurulu değil." if command -v brew >/dev/null 2>&1; then read -r -p " Homebrew ile şimdi kurulsun mu? [E/h] " ans if [[ ! "$ans" =~ ^[Hh] ]]; then brew install --cask docker else fail "Docker Desktop'ı https://www.docker.com/products/docker-desktop/ adresinden kurup tekrar deneyin." fi else fail "Docker Desktop'ı https://www.docker.com/products/docker-desktop/ adresinden kurup tekrar deneyin." fi else fail "Docker kurulu değil. Kurulum: https://docs.docker.com/engine/install/ (hızlı yol: curl -fsSL https://get.docker.com | sh)" fi fi ok "docker komutu bulundu" # ---------------------------------------------------------------- 2. Docker çalışıyor mu? if ! docker info >/dev/null 2>&1; then if [ "$OS" = "Darwin" ]; then echo " Docker Desktop başlatılıyor…" open -a Docker || fail "Docker Desktop açılamadı. Uygulamalar klasöründen elle açın." elif docker info 2>&1 | grep -qi "permission denied"; then fail "Docker'a erişim izni yok. Şunu çalıştırıp oturumu kapatıp açın: sudo usermod -aG docker \$USER" else echo " Docker servisi başlatılıyor (sudo parolası istenebilir)…" sudo systemctl start docker 2>/dev/null || true fi for _ in $(seq 1 60); do docker info >/dev/null 2>&1 && break sleep 3 done docker info >/dev/null 2>&1 || fail "Docker 3 dakikada başlamadı. Docker'ı elle açıp tekrar deneyin." fi docker compose version >/dev/null 2>&1 || fail "'docker compose' bulunamadı. Docker'ı güncelleyin (Compose v2 gerekli)." ok "Docker çalışıyor" # ---------------------------------------------------------------- 3. Sandbox imajları step "Kod çalıştırma ortamları (sandbox imajları)" for lang in python node java ui; do img="lta-sandbox-$lang" # Dockerfile değiştiyse (ör. yeni kütüphane) imaj yeniden derlenir: özet imaj etiketinde tutulur hash="$( (sha256sum "sandbox/docker/$lang.Dockerfile" 2>/dev/null || shasum -a 256 "sandbox/docker/$lang.Dockerfile") | cut -c1-16)" have="$(docker image inspect "$img" --format '{{ index .Config.Labels "lta.hash" }}' 2>/dev/null || true)" if [ "$REBUILD" = 1 ] || [ "$have" != "$hash" ]; then echo " $img derleniyor (ilk seferde birkaç dakika sürer)…" docker build -q --label "lta.hash=$hash" -f "sandbox/docker/$lang.Dockerfile" -t "$img" sandbox >/dev/null fi ok "$img" done # ---------------------------------------------------------------- 4. Ollama: yerel mi, konteyner mi? step "Dil modeli sunucusu (Ollama)" PROFILE_ARGS=() if http_ok "http://localhost:11434/api/tags" && ! docker compose ps --services --status running 2>/dev/null | grep -qx ollama; then export OLLAMA_URL="http://host.docker.internal:11434" OLLAMA_MODE=host ok "Bilgisayardaki Ollama kullanılacak" else export OLLAMA_URL="http://ollama:11434" OLLAMA_MODE=docker PROFILE_ARGS=(--profile ollama) ok "Ollama konteynerde çalışacak" if command -v nvidia-smi >/dev/null || [ "$(uname)" = "Darwin" ]; then echo " [UYARI] Konteynerdeki Ollama ekran kartını (Mac'te Metal) genellikle kullanmaz; çok yavaş olur." echo " Hız için Ollama'yı bilgisayara kurun: https://ollama.com/download — sonra yeniden başlatın." fi fi export OLLAMA_MODEL="$MODEL" # ---------------------------------------------------------------- 5. Uygulamayı başlat step "Uygulama başlatılıyor" EXPECTED="$(head -n1 VERSION 2>/dev/null | tr -d '[:space:]')" # Başka bir klasörden (eski sürümden) başlatılmış ve 8000 portunu tutan uygulama konteynerini kaldır docker ps -a --filter "publish=8000" --format '{{.ID}}|{{.Names}}|{{.Labels}}' 2>/dev/null | while IFS='|' read -r cid cname clabels; do proj="$(printf '%s' "$clabels" | tr ',' '\n' | sed -n 's/^com\.docker\.compose\.project=//p')" if [ -n "$cid" ] && [ "$proj" != "llm-test-agent" ]; then echo " Eski uygulama konteyneri kaldırılıyor: $cname (proje: ${proj:-yok})" docker rm -f "$cid" >/dev/null 2>&1 || true fi done # Her başlatmada imaj güncellenir; değişiklik yoksa Docker önbelleği sayesinde birkaç saniye sürer BUILD_ARGS=(--build) if [ -z "$(docker compose images -q app 2>/dev/null)" ]; then echo " Uygulama imajı derleniyor (ilk seferde birkaç dakika sürer)…" fi # Not: macOS'taki bash 3.2 boş dizileri "set -u" altında sorun sayar; bu yüzden ${a[@]+...} kalıbı # NVIDIA kartı varsa uygulama konteynerine GPU izleme erişimi verilir (gerçek zamanlı GPU göstergesi için) GPU_FILES=() command -v nvidia-smi >/dev/null && GPU_FILES=(-f docker-compose.yml -f docker-compose.gpu.yml) if ! docker compose ${GPU_FILES[@]+"${GPU_FILES[@]}"} ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}; then [ ${#GPU_FILES[@]} -gt 0 ] || fail "docker compose up başarısız oldu. Ayrıntı: docker compose logs" echo " [UYARI] Konteynere GPU erişimi verilemedi; GPU göstergesi yalnızca Ollama bilgisini gösterecek." docker compose ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"} || fail "docker compose up başarısız oldu." fi ok "Konteynerler ayakta" # ---------------------------------------------------------------- 6. Model step "Model: $MODEL" for _ in $(seq 1 40); do http_ok "http://localhost:11434/api/tags" && break; sleep 2; done if curl -fsS -m 5 "http://localhost:11434/api/tags" 2>/dev/null | grep -q "\"name\":\"$MODEL"; then ok "Model zaten indirilmiş" else echo " Model indiriliyor (yalnızca ilk seferde; ~5 GB)…" if [ "$OLLAMA_MODE" = host ]; then ollama pull "$MODEL" else docker compose --profile ollama exec -T ollama ollama pull "$MODEL" fi ok "Model hazır" fi # ---------------------------------------------------------------- 7. Hazır olmasını bekle, tarayıcıyı aç step "Arayüz hazırlanıyor" # Hafif /api/ping kullanılır (Ollama'ya dokunmaz); eski sürümde ping yoksa /api/health'e uzun zaman aşımıyla bakılır. app_version() { local v v="$(curl -fsS -m 5 "$1/api/ping" 2>/dev/null | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')" [ -n "$v" ] || v="$(curl -fsS -m 30 "$1/api/health" 2>/dev/null | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')" printf '%s' "$v" } wait_app() { local v u for _ in $(seq 1 60); do for u in "http://localhost:8000" "http://127.0.0.1:8000"; do v="$(app_version "$u")" if [ -n "$v" ]; then APP_URL="$u"; RUNNING="$v"; return 0; fi done sleep 2 done RUNNING=""; return 1 } show_diag() { printf '\n\033[33m---- docker compose ps ----\033[0m\n'; docker compose ps -a || true printf '\033[33m---- docker compose logs app (son 60 satır) ----\033[0m\n'; docker compose logs app --tail 60 || true } wait_app || { show_diag; fail "Uygulama yanıt vermiyor. Yukarıdaki satırların ekran görüntüsünü paylaşın."; } if [ -n "$EXPECTED" ] && [ "$RUNNING" != "$EXPECTED" ]; then echo " Çalışan sürüm '${RUNNING}', beklenen '${EXPECTED}'. Uygulama önbelleksiz yeniden derleniyor…" docker compose ${GPU_FILES[@]+"${GPU_FILES[@]}"} build --no-cache app || docker compose build --no-cache app docker compose ${GPU_FILES[@]+"${GPU_FILES[@]}"} ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d --force-recreate app \ || docker compose ${PROFILE_ARGS[@]+"${PROFILE_ARGS[@]}"} up -d --force-recreate app wait_app || true [ "$RUNNING" = "$EXPECTED" ] || { show_diag; fail "Uygulama hâlâ eski sürümde ('${RUNNING}'). 'docker ps' ile 8000 portunu kullanan konteyneri kontrol edin."; } fi if ! curl -fsS -m 30 "$APP_URL/api/health" 2>/dev/null | grep -q '"ollama":{"ok":true'; then printf ' \033[33m!\033[0m Uygulama Ollama’ya ulaşamıyor (%s).\n' "$OLLAMA_URL" echo " Ollama'yı yeniden başlatın; sürerse OLLAMA_HOST=0.0.0.0 ile başlatın (Linux: systemctl edit ollama)." echo " Arayüz yine de açılıyor; Ollama düzelince üst çubuktaki durum yeşile döner." fi ok "Sürüm: $RUNNING" ok "Hazır: $APP_URL" if [ "$OPEN_BROWSER" = 1 ]; then if [ "$OS" = "Darwin" ]; then open "$APP_URL/?v=$RUNNING" elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$APP_URL/?v=$RUNNING" >/dev/null 2>&1 & fi fi echo echo "Durdurmak için: ./scripts/stop.sh (veya Durdur kısayolu)"