From 58799d9ae1fb3b5f63e0b99349c58a27a50c0729 Mon Sep 17 00:00:00 2001 From: Yury Kossakovsky Date: Mon, 29 Dec 2025 13:06:03 -0700 Subject: [PATCH] refactor(compose): centralize external compose file handling add build_compose_files_array() and getter functions for n8n-workers, supabase, dify compose files in utils.sh. simplifies restart.sh and apply_update.sh by using shared function. now checks both profile activation AND file existence before including external compose files. --- .claude/commands/add-new-service.md | 53 ++++++++++++++++++++++++----- CLAUDE.md | 2 ++ scripts/apply_update.sh | 25 +++----------- scripts/restart.sh | 21 ++++++------ scripts/utils.sh | 52 ++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 39 deletions(-) diff --git a/.claude/commands/add-new-service.md b/.claude/commands/add-new-service.md index e8aeef5..2fe69b7 100644 --- a/.claude/commands/add-new-service.md +++ b/.claude/commands/add-new-service.md @@ -633,20 +633,51 @@ fi --- -## STEP 11: scripts/apply_update.sh (for complex services) +## STEP 11: External Compose Files (for complex services like Supabase/Dify) -**File:** `scripts/apply_update.sh` +**Files:** `scripts/utils.sh`, `scripts/restart.sh`, `scripts/apply_update.sh`, `start_services.py` -For services with external docker-compose files: +For services with their own external docker-compose files (cloned from upstream repos): + +### 11.1 Add getter function to utils.sh ```bash -if is_profile_active "$ARGUMENTS"; then - log_info "Updating $ARGUMENTS..." - docker compose -f docker-compose.$ARGUMENTS.yml pull -fi +# Get $ARGUMENTS compose file path if profile is active and file exists +# Usage: path=$(get_${SERVICE_SLUG_UNDERSCORE}_compose) && COMPOSE_FILES+=("-f" "$path") +get_${SERVICE_SLUG_UNDERSCORE}_compose() { + local compose_file="$PROJECT_ROOT/$ARGUMENTS/docker/docker-compose.yml" + if [ -f "$compose_file" ] && is_profile_active "$ARGUMENTS"; then + echo "$compose_file" + return 0 + fi + return 1 +} ``` -Most services don't need this. +### 11.2 Add to build_compose_files_array() in utils.sh + +```bash +build_compose_files_array() { + COMPOSE_FILES=("-f" "$PROJECT_ROOT/docker-compose.yml") + # ... existing ... + if path=$(get_${SERVICE_SLUG_UNDERSCORE}_compose); then + COMPOSE_FILES+=("-f" "$path") + fi +} +``` + +### 11.3 Add to start_services.py + +Add functions similar to Supabase/Dify: +- `is_${SERVICE_SLUG_UNDERSCORE}_enabled()` - Check if profile is in COMPOSE_PROFILES +- `clone_${SERVICE_SLUG_UNDERSCORE}_repo()` - Clone upstream repo with sparse checkout +- `prepare_${SERVICE_SLUG_UNDERSCORE}_env()` - Copy/transform .env file +- `start_${SERVICE_SLUG_UNDERSCORE}()` - Start services if enabled +- Update `stop_all_services()` - Include compose file in down command (by file existence, not profile) + +**Important:** `stop_all_services()` checks file existence only (not profile) to ensure cleanup when profile is removed. + +Most services don't need this - only use for services that maintain their own docker-compose.yml upstream. --- @@ -713,3 +744,9 @@ bash -n scripts/07_final_report.sh ### If Hardware Variants (CPU/GPU) - [ ] Mutually exclusive profiles (cpu, gpu-nvidia, gpu-amd) - [ ] GPU resource reservations + +### If External Compose File (Supabase/Dify style) +- [ ] `scripts/utils.sh`: `get_*_compose()` function added +- [ ] `scripts/utils.sh`: `build_compose_files_array()` updated +- [ ] `start_services.py`: `is_*_enabled()`, `clone_*_repo()`, `prepare_*_env()`, `start_*()` functions +- [ ] `start_services.py`: `stop_all_services()` includes compose file (by existence, not profile) diff --git a/CLAUDE.md b/CLAUDE.md index 26b2aa1..f90ee6c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -155,6 +155,8 @@ Key functions: - `get_real_user` / `get_real_user_home` - Get actual user even under sudo - `backup_preserved_dirs` / `restore_preserved_dirs` - Directory preservation for git updates - `cleanup_legacy_n8n_workers` - Remove old n8n worker containers from previous naming convention +- `get_n8n_workers_compose` / `get_supabase_compose` / `get_dify_compose` - Get compose file path if profile active AND file exists +- `build_compose_files_array` - Build global `COMPOSE_FILES` array with all active compose files (main + external) ### Service Profiles diff --git a/scripts/apply_update.sh b/scripts/apply_update.sh index 87a6848..e78c627 100755 --- a/scripts/apply_update.sh +++ b/scripts/apply_update.sh @@ -77,31 +77,16 @@ cleanup_legacy_postgresus # Pull latest versions of selected containers based on updated .env set_telemetry_stage "update_docker_pull" log_info "Pulling latest versions of selected containers..." -COMPOSE_FILES_FOR_PULL=("-f" "$PROJECT_ROOT/docker-compose.yml") -# Check if n8n workers file exists (generated by 05_configure_services.sh) -N8N_WORKERS_COMPOSE_FILE="$PROJECT_ROOT/docker-compose.n8n-workers.yml" -if [ -f "$N8N_WORKERS_COMPOSE_FILE" ]; then - COMPOSE_FILES_FOR_PULL+=("-f" "$N8N_WORKERS_COMPOSE_FILE") -fi +# Load environment to check active profiles (wizard may have updated them) +load_env -# Check if Supabase directory and its docker-compose.yml exist -SUPABASE_DOCKER_DIR="$PROJECT_ROOT/supabase/docker" -SUPABASE_COMPOSE_FILE_PATH="$SUPABASE_DOCKER_DIR/docker-compose.yml" -if [ -d "$SUPABASE_DOCKER_DIR" ] && [ -f "$SUPABASE_COMPOSE_FILE_PATH" ]; then - COMPOSE_FILES_FOR_PULL+=("-f" "$SUPABASE_COMPOSE_FILE_PATH") -fi - -# Check if Dify directory and its docker-compose.yaml exist -DIFY_DOCKER_DIR="$PROJECT_ROOT/dify/docker" -DIFY_COMPOSE_FILE_PATH="$DIFY_DOCKER_DIR/docker-compose.yaml" -if [ -d "$DIFY_DOCKER_DIR" ] && [ -f "$DIFY_COMPOSE_FILE_PATH" ]; then - COMPOSE_FILES_FOR_PULL+=("-f" "$DIFY_COMPOSE_FILE_PATH") -fi +# Build compose files array using shared function (checks profile + file existence) +build_compose_files_array # Use the project name "localai" for consistency. # This command WILL respect COMPOSE_PROFILES from the .env file (updated by the wizard above). -$COMPOSE_CMD -p "localai" "${COMPOSE_FILES_FOR_PULL[@]}" pull --ignore-buildable || { +$COMPOSE_CMD -p "localai" "${COMPOSE_FILES[@]}" pull --ignore-buildable || { log_error "Failed to pull Docker images for selected services. Check network connection and Docker Hub status." exit 1 } diff --git a/scripts/restart.sh b/scripts/restart.sh index 33d0fff..babbb8e 100755 --- a/scripts/restart.sh +++ b/scripts/restart.sh @@ -3,11 +3,13 @@ # restart.sh - Restart all services # ============================================================================= # Restarts all Docker Compose services including dynamically generated -# worker/runner compose files. +# worker/runner compose files and external service stacks. # -# Handles compose files: +# Handles compose files via build_compose_files_array() from utils.sh: # - docker-compose.yml (main) -# - docker-compose.n8n-workers.yml (if exists) +# - docker-compose.n8n-workers.yml (if exists and n8n profile active) +# - supabase/docker/docker-compose.yml (if exists and supabase profile active) +# - dify/docker/docker-compose.yaml (if exists and dify profile active) # # Usage: bash scripts/restart.sh # ============================================================================= @@ -20,16 +22,13 @@ init_paths cd "$PROJECT_ROOT" +# Load environment to check active profiles +load_env + PROJECT_NAME="localai" -# Build compose files array -COMPOSE_FILES=("-f" "docker-compose.yml") - -# Add n8n workers compose file if exists -N8N_WORKERS_COMPOSE="docker-compose.n8n-workers.yml" -if [ -f "$N8N_WORKERS_COMPOSE" ]; then - COMPOSE_FILES+=("-f" "$N8N_WORKERS_COMPOSE") -fi +# Build compose files array (sets global COMPOSE_FILES) +build_compose_files_array log_info "Restarting services..." log_info "Using compose files: ${COMPOSE_FILES[*]}" diff --git a/scripts/utils.sh b/scripts/utils.sh index 47c8dc1..f42f752 100755 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -319,6 +319,58 @@ is_profile_active() { [[ -n "$COMPOSE_PROFILES" && ",$COMPOSE_PROFILES," == *",$profile,"* ]] } +# Get n8n workers compose file path if profile is active and file exists +# Usage: path=$(get_n8n_workers_compose) && COMPOSE_FILES+=("-f" "$path") +get_n8n_workers_compose() { + local compose_file="$PROJECT_ROOT/docker-compose.n8n-workers.yml" + if [ -f "$compose_file" ] && is_profile_active "n8n"; then + echo "$compose_file" + return 0 + fi + return 1 +} + +# Get Supabase compose file path if profile is active and file exists +# Usage: path=$(get_supabase_compose) && COMPOSE_FILES+=("-f" "$path") +get_supabase_compose() { + local compose_file="$PROJECT_ROOT/supabase/docker/docker-compose.yml" + if [ -f "$compose_file" ] && is_profile_active "supabase"; then + echo "$compose_file" + return 0 + fi + return 1 +} + +# Get Dify compose file path if profile is active and file exists +# Usage: path=$(get_dify_compose) && COMPOSE_FILES+=("-f" "$path") +get_dify_compose() { + local compose_file="$PROJECT_ROOT/dify/docker/docker-compose.yaml" + if [ -f "$compose_file" ] && is_profile_active "dify"; then + echo "$compose_file" + return 0 + fi + return 1 +} + +# Build array of all active compose files (main + external services) +# IMPORTANT: Requires COMPOSE_PROFILES to be set before calling (via load_env) +# Usage: build_compose_files_array; docker compose "${COMPOSE_FILES[@]}" up -d +# Result is stored in global COMPOSE_FILES array +build_compose_files_array() { + COMPOSE_FILES=("-f" "$PROJECT_ROOT/docker-compose.yml") + + local path + if path=$(get_n8n_workers_compose); then + COMPOSE_FILES+=("-f" "$path") + fi + if path=$(get_supabase_compose); then + COMPOSE_FILES+=("-f" "$path") + fi + if path=$(get_dify_compose); then + COMPOSE_FILES+=("-f" "$path") + fi +} + #============================================================================= # UTILITIES #=============================================================================