From 7200f3c65f44b9799eef1e0da8094981c5415180 Mon Sep 17 00:00:00 2001 From: Igor Loskutov Date: Tue, 10 Feb 2026 19:04:42 -0500 Subject: [PATCH] =?UTF-8?q?fix:=20standalone=20setup=20=E2=80=94=20garage?= =?UTF-8?q?=20config,=20symlink=20handling,=20healthcheck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - garage.toml: fix rpc_secret field name (was secret_transmitter), move to top-level per Garage v1.1.0 spec, remove unused [s3_web] - setup-standalone.sh: resolve symlinked .env files before writing, always ensure all standalone-critical vars via env_set, fix garage key create/info syntax (positional arg, not --name), avoid overwriting key secret with "(redacted)" on re-run, use compose_cmd in health check - docker-compose.standalone.yml: fix garage healthcheck (no curl in image, use /garage stats instead) --- docker-compose.standalone.yml | 2 +- scripts/garage.toml | 10 +--- scripts/setup-standalone.sh | 110 +++++++++++++++++----------------- 3 files changed, 58 insertions(+), 64 deletions(-) diff --git a/docker-compose.standalone.yml b/docker-compose.standalone.yml index 9f67acc0..996392d0 100644 --- a/docker-compose.standalone.yml +++ b/docker-compose.standalone.yml @@ -17,7 +17,7 @@ services: - ./data/garage.toml:/etc/garage.toml:ro restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-sf", "http://localhost:3903/health"] + test: ["CMD", "/garage", "stats"] interval: 10s timeout: 5s retries: 5 diff --git a/scripts/garage.toml b/scripts/garage.toml index 5694f11c..84778c9f 100644 --- a/scripts/garage.toml +++ b/scripts/garage.toml @@ -2,17 +2,13 @@ metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" replication_factor = 1 +rpc_secret = "__GARAGE_RPC_SECRET__" +rpc_bind_addr = "[::]:3901" + [s3_api] api_bind_addr = "[::]:3900" s3_region = "garage" root_domain = ".s3.garage.localhost" -[s3_web] -bind_addr = "[::]:3902" - [admin] api_bind_addr = "[::]:3903" - -[rpc] -bind_addr = "[::]:3901" -secret_transmitter = "__GARAGE_RPC_SECRET__" diff --git a/scripts/setup-standalone.sh b/scripts/setup-standalone.sh index 4f0cdc7b..7f04b628 100755 --- a/scripts/setup-standalone.sh +++ b/scripts/setup-standalone.sh @@ -68,6 +68,16 @@ env_set() { fi } +resolve_symlink() { + local file="$1" + if [[ -L "$file" ]]; then + warn "$(basename "$file") is a symlink — creating standalone copy" + cp -L "$file" "$file.tmp" + rm "$file" + mv "$file.tmp" "$file" + fi +} + compose_cmd() { local compose_files="-f $ROOT_DIR/docker-compose.yml -f $ROOT_DIR/docker-compose.standalone.yml" if [[ "$OS" == "Linux" ]] && [[ -n "${OLLAMA_PROFILE:-}" ]]; then @@ -156,45 +166,32 @@ step_llm() { step_server_env() { info "Step 2: Generating server/.env" + resolve_symlink "$SERVER_ENV" + if [[ -f "$SERVER_ENV" ]]; then - ok "server/.env already exists — checking key vars" + ok "server/.env already exists — ensuring standalone vars" else cat > "$SERVER_ENV" << 'ENVEOF' # Generated by setup-standalone.sh — standalone local development # Source of truth for settings: server/reflector/settings.py - -# --- Database (Docker internal hostnames) --- -DATABASE_URL=postgresql+asyncpg://reflector:reflector@postgres:5432/reflector -REDIS_HOST=redis -CELERY_BROKER_URL=redis://redis:6379/1 -CELERY_RESULT_BACKEND=redis://redis:6379/1 - -# --- Auth (disabled for standalone) --- -AUTH_BACKEND=none - -# --- Transcription (local whisper) --- -TRANSCRIPT_BACKEND=whisper - -# --- Storage (set by step_storage, Garage S3-compatible) --- - -# --- Diarization (disabled, no backend available) --- -DIARIZATION_ENABLED=false - -# --- Translation (passthrough, no Modal) --- -TRANSLATION_BACKEND=passthrough - -# --- LLM (set below by setup script) --- -LLM_API_KEY=not-needed ENVEOF ok "Created server/.env" fi - # Ensure LLM vars are set (may differ per OS/re-run) + # Ensure all standalone-critical vars (appends if missing, replaces if present) + env_set "$SERVER_ENV" "DATABASE_URL" "postgresql+asyncpg://reflector:reflector@postgres:5432/reflector" + env_set "$SERVER_ENV" "REDIS_HOST" "redis" + env_set "$SERVER_ENV" "CELERY_BROKER_URL" "redis://redis:6379/1" + env_set "$SERVER_ENV" "CELERY_RESULT_BACKEND" "redis://redis:6379/1" + env_set "$SERVER_ENV" "AUTH_BACKEND" "none" + env_set "$SERVER_ENV" "TRANSCRIPT_BACKEND" "whisper" + env_set "$SERVER_ENV" "DIARIZATION_ENABLED" "false" + env_set "$SERVER_ENV" "TRANSLATION_BACKEND" "passthrough" env_set "$SERVER_ENV" "LLM_URL" "$LLM_URL_VALUE" env_set "$SERVER_ENV" "LLM_MODEL" "$MODEL" env_set "$SERVER_ENV" "LLM_API_KEY" "not-needed" - ok "LLM vars set (LLM_URL=$LLM_URL_VALUE)" + ok "Standalone vars set (LLM_URL=$LLM_URL_VALUE)" } # ========================================================= @@ -231,25 +228,28 @@ step_storage() { fi # Create key (idempotent — skip if exists) - KEY_OUTPUT=$(compose_cmd exec -T garage /garage key info --name reflector 2>&1 || true) - if echo "$KEY_OUTPUT" | grep -q "not found"; then - KEY_OUTPUT=$(compose_cmd exec -T garage /garage key create --name reflector) + CREATED_KEY=false + if compose_cmd exec -T garage /garage key info reflector &>/dev/null; then + ok "Key 'reflector' already exists" + else + KEY_OUTPUT=$(compose_cmd exec -T garage /garage key create reflector) + CREATED_KEY=true fi - # Parse key ID and secret from output - KEY_ID=$(echo "$KEY_OUTPUT" | grep -i "key id" | awk '{print $NF}') - KEY_SECRET=$(echo "$KEY_OUTPUT" | grep -i "secret" | awk '{print $NF}') - # Grant bucket permissions (idempotent) compose_cmd exec -T garage /garage bucket allow reflector-media --read --write --key reflector - # Set env vars + # Set env vars (only parse key on first create — key info redacts the secret) env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_BACKEND" "aws" env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL" "http://garage:3900" env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_BUCKET_NAME" "reflector-media" env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_REGION" "garage" - env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID" "$KEY_ID" - env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY" "$KEY_SECRET" + if [[ "$CREATED_KEY" == "true" ]]; then + KEY_ID=$(echo "$KEY_OUTPUT" | grep -i "key id" | awk '{print $NF}') + KEY_SECRET=$(echo "$KEY_OUTPUT" | grep -i "secret key" | awk '{print $NF}') + env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID" "$KEY_ID" + env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY" "$KEY_SECRET" + fi ok "Object storage ready (Garage)" } @@ -260,29 +260,26 @@ step_storage() { step_www_env() { info "Step 4: Generating www/.env.local" + resolve_symlink "$WWW_ENV" + if [[ -f "$WWW_ENV" ]]; then - ok "www/.env.local already exists — skipping" - return + ok "www/.env.local already exists — ensuring standalone vars" + else + cat > "$WWW_ENV" << 'ENVEOF' +# Generated by setup-standalone.sh — standalone local development +ENVEOF + ok "Created www/.env.local" fi - cat > "$WWW_ENV" << 'ENVEOF' -# Generated by setup-standalone.sh — standalone local development + env_set "$WWW_ENV" "SITE_URL" "http://localhost:3000" + env_set "$WWW_ENV" "NEXTAUTH_URL" "http://localhost:3000" + env_set "$WWW_ENV" "NEXTAUTH_SECRET" "standalone-dev-secret-not-for-production" + env_set "$WWW_ENV" "API_URL" "http://localhost:1250" + env_set "$WWW_ENV" "WEBSOCKET_URL" "ws://localhost:1250" + env_set "$WWW_ENV" "SERVER_API_URL" "http://server:1250" + env_set "$WWW_ENV" "FEATURE_REQUIRE_LOGIN" "false" -SITE_URL=http://localhost:3000 -NEXTAUTH_URL=http://localhost:3000 -NEXTAUTH_SECRET=standalone-dev-secret-not-for-production - -# Browser-side URLs (localhost, outside Docker) -API_URL=http://localhost:1250 -WEBSOCKET_URL=ws://localhost:1250 - -# Server-side (SSR) URL (Docker internal) -SERVER_API_URL=http://server:1250 - -# Auth disabled for standalone -FEATURE_REQUIRE_LOGIN=false -ENVEOF - ok "Created www/.env.local" + ok "Standalone www vars set" } # ========================================================= @@ -312,7 +309,7 @@ step_health() { ok "Frontend responding" # Check LLM reachability from inside a container - if docker compose -f "$ROOT_DIR/docker-compose.yml" exec -T server \ + if compose_cmd exec -T server \ curl -sf "$LLM_URL_VALUE/models" > /dev/null 2>&1; then ok "LLM reachable from containers" else @@ -338,6 +335,7 @@ main() { exit 1 fi + # LLM_URL_VALUE is set by step_llm, used by later steps LLM_URL_VALUE="" OLLAMA_PROFILE=""