From dd8ef5b3c8cc6f7c6360fa5536a692f10fd9f274 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 19 Dec 2025 15:54:36 -0600 Subject: [PATCH] refactor: enhance Docker Compose validation workflow Updated the GitHub Actions workflow for validating Docker Compose files by streamlining the detection of changed blueprints and improving the validation process. Removed redundant steps and consolidated the validation of docker-compose.yml and template.toml files into a more efficient structure. Added clearer output messages for validation results and ensured that best practices are checked for each blueprint. This refactor aims to improve maintainability and clarity in the CI/CD process. --- .github/workflows/validate-docker-compose.yml | 296 +++--------------- 1 file changed, 50 insertions(+), 246 deletions(-) diff --git a/.github/workflows/validate-docker-compose.yml b/.github/workflows/validate-docker-compose.yml index ddd16aab..25d4eaf3 100644 --- a/.github/workflows/validate-docker-compose.yml +++ b/.github/workflows/validate-docker-compose.yml @@ -17,13 +17,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 # Necesitamos el historial completo para comparar con base + fetch-depth: 0 - name: Set up Docker Compose - run: | - echo "🐳 Setting up Docker Compose..." - # Docker Compose V2 viene preinstalado en ubuntu-latest - docker compose version + run: docker compose version - name: Set up Node.js uses: actions/setup-node@v4 @@ -36,282 +33,89 @@ jobs: version: 8 - name: Install dependencies - run: | - echo "πŸ“¦ Installing Node.js dependencies..." - cd build-scripts && pnpm install + run: cd build-scripts && pnpm install - - name: Get changed files - id: changed-files + - name: Detect changed blueprints + id: changed run: | - echo "πŸ” Detecting changed files..." - - # Obtener la rama base BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }}) - # Encontrar todos los archivos docker-compose.yml y template.toml modificados/agregados - CHANGED_COMPOSE=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/docker-compose\.yml$' || true) - CHANGED_TOML=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/template\.toml$' || true) + # Obtener todos los blueprints que tienen cambios (en docker-compose.yml o template.toml) + CHANGED_BLUEPRINTS=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | \ + grep -E 'blueprints/[^/]+/(docker-compose\.yml|template\.toml)$' | \ + sed 's|blueprints/\([^/]*\)/.*|\1|' | \ + sort -u) - # Crear lista de directorios ΓΊnicos que tienen cambios - CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u) - - echo "Changed compose files:" - echo "$CHANGED_COMPOSE" | while read file; do [ -n "$file" ] && echo " - $file"; done - - echo "Changed TOML files:" - echo "$CHANGED_TOML" | while read file; do [ -n "$file" ] && echo " - $file"; done - - echo "Changed directories:" - echo "$CHANGED_DIRS" | while read dir; do [ -n "$dir" ] && echo " - $dir"; done - - # Guardar para usar en siguientes pasos - echo "compose_files<> $GITHUB_OUTPUT - echo "$CHANGED_COMPOSE" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - echo "toml_files<> $GITHUB_OUTPUT - echo "$CHANGED_TOML" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - echo "directories<> $GITHUB_OUTPUT - echo "$CHANGED_DIRS" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - - name: Validate Docker Compose files syntax - id: validate-compose-syntax - run: | - echo "πŸ” Validating Docker Compose files syntax..." - - ERROR=0 - COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}" - - if [ -z "$COMPOSE_FILES" ]; then - echo "ℹ️ No docker-compose.yml files changed, skipping validation" - exit 0 - fi - - echo "$COMPOSE_FILES" | while read -r compose_file; do - if [ -z "$compose_file" ]; then - continue - fi - - TEMPLATE_DIR=$(dirname "$compose_file") - TEMPLATE_NAME=$(basename "$TEMPLATE_DIR") - - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "πŸ“¦ Validating syntax: $TEMPLATE_NAME" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Validar sintaxis de docker-compose.yml usando docker compose - echo "πŸ” Validating docker-compose.yml syntax..." - if ! docker compose -f "$compose_file" config > /dev/null 2>&1; then - echo "❌ ERROR: docker-compose.yml syntax is invalid in $TEMPLATE_NAME" - echo "Running docker compose config to show errors:" - docker compose -f "$compose_file" config 2>&1 || true - ERROR=1 - else - echo "βœ… docker-compose.yml syntax is valid" - fi - - # Obtener lista de servicios del compose - SERVICES=$(docker compose -f "$compose_file" config --services 2>/dev/null || echo "") - echo "πŸ“‹ Services found in docker-compose.yml:" - echo "$SERVICES" | while read service; do - [ -n "$service" ] && echo " - $service" - done - - # Guardar servicios para validaciΓ³n posterior - echo "$SERVICES" > "/tmp/${TEMPLATE_NAME}_services.txt" + echo "Changed blueprints:" + echo "$CHANGED_BLUEPRINTS" | while read blueprint; do + [ -n "$blueprint" ] && echo " - $blueprint" done - if [ $ERROR -eq 1 ]; then - echo "" - echo "❌ Docker Compose syntax validation failed" - exit 1 - else - echo "" - echo "βœ… All Docker Compose files have valid syntax" - fi + # Guardar lista de blueprints (una por lΓ­nea) + echo "blueprints<> $GITHUB_OUTPUT + echo "$CHANGED_BLUEPRINTS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - - name: Validate Docker Compose best practices - id: validate-compose-practices + - name: Validate blueprints run: | - echo "πŸ” Validating Docker Compose best practices..." + BLUEPRINTS="${{ steps.changed.outputs.blueprints }}" - ERROR=0 - COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}" - - if [ -z "$COMPOSE_FILES" ]; then - echo "ℹ️ No docker-compose.yml files changed, skipping validation" + if [ -z "$BLUEPRINTS" ] || [ "$BLUEPRINTS" = "" ]; then + echo "ℹ️ No blueprints changed, skipping validation" exit 0 fi - # Convert to array to avoid subshell issues with pipe - # This ensures ERROR=1 inside the loop propagates to the parent shell - mapfile -t COMPOSE_ARRAY <<< "$COMPOSE_FILES" - - for compose_file in "${COMPOSE_ARRAY[@]}"; do - if [ -z "$compose_file" ]; then - continue - fi - - TEMPLATE_DIR=$(dirname "$compose_file") - TEMPLATE_NAME=$(basename "$TEMPLATE_DIR") - - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "πŸ“¦ Validating best practices: $TEMPLATE_NAME" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Validar usando el script de TypeScript - if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$compose_file" --verbose); then - ERROR=1 - fi - done - - if [ $ERROR -eq 1 ]; then - echo "" - echo "❌ Docker Compose best practices validation failed" - exit 1 - else - echo "" - echo "βœ… All Docker Compose files follow best practices" - fi - - - name: Validate template.toml files - id: validate-toml - run: | - echo "πŸ” Validating template.toml files..." - ERROR=0 - DIRECTORIES="${{ steps.changed-files.outputs.directories }}" - if [ -z "$DIRECTORIES" ]; then - echo "ℹ️ No template directories changed, skipping TOML validation" - exit 0 - fi + # Convertir a array para evitar problemas con subshells + mapfile -t BLUEPRINT_ARRAY <<< "$BLUEPRINTS" - # Convert to array to avoid subshell issues with pipe - # This ensures ERROR=1 inside the loop propagates to the parent shell - mapfile -t DIRS_ARRAY <<< "$DIRECTORIES" - - for template_dir in "${DIRS_ARRAY[@]}"; do - if [ -z "$template_dir" ]; then - continue - fi + # Iterar sobre cada blueprint + for blueprint in "${BLUEPRINT_ARRAY[@]}"; do + [ -z "$blueprint" ] && continue - TEMPLATE_PATH="blueprints/$template_dir" - TOML_FILE="$TEMPLATE_PATH/template.toml" - - if [ ! -f "$TOML_FILE" ]; then - echo "⚠️ WARNING: template.toml not found in $template_dir (might be deleted)" - continue - fi + BLUEPRINT_PATH="blueprints/$blueprint" + COMPOSE_FILE="$BLUEPRINT_PATH/docker-compose.yml" + TOML_FILE="$BLUEPRINT_PATH/template.toml" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "πŸ“ Validating: $template_dir/template.toml" + echo "πŸ“¦ Validating: $blueprint" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - # Validar usando el script de TypeScript con tsx - # Ejecutar desde build-scripts para tener acceso a node_modules - if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$TEMPLATE_PATH" --verbose); then - ERROR=1 - fi - done - - if [ $ERROR -eq 1 ]; then - echo "" - echo "❌ template.toml validation failed" - exit 1 - else - echo "" - echo "βœ… All template.toml files are valid" - fi - - - name: Test Docker Compose (dry-run) - id: test-compose - run: | - echo "πŸ§ͺ Testing Docker Compose files (dry-run)..." - - ERROR=0 - DIRECTORIES="${{ steps.changed-files.outputs.directories }}" - - if [ -z "$DIRECTORIES" ]; then - echo "ℹ️ No template directories changed, skipping dry-run test" - exit 0 - fi - - echo "$DIRECTORIES" | while read -r template_dir; do - if [ -z "$template_dir" ]; then - continue - fi - - COMPOSE_FILE="blueprints/$template_dir/docker-compose.yml" - + # 1. Validar best practices de docker-compose.yml if [ ! -f "$COMPOSE_FILE" ]; then + echo "⚠️ WARNING: docker-compose.yml not found" + ERROR=1 + continue + fi + echo "πŸ” Validating docker-compose.yml best practices..." + if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$COMPOSE_FILE" --verbose); then + ERROR=1 continue fi - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "πŸ§ͺ Testing: $template_dir" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Cambiar al directorio del template para resolver rutas relativas - cd "blueprints/$template_dir" - - # Validar que docker-compose puede parsear el archivo completamente - echo "πŸ” Running docker compose config (full validation)..." - if docker compose config > /dev/null 2>&1; then - echo "βœ… Docker Compose file is fully valid and can be processed" - - # Mostrar informaciΓ³n ΓΊtil - echo "πŸ“‹ Configuration summary:" - docker compose config --services | while read service; do - [ -n "$service" ] && echo " Service: $service" - done + # 3. Validar template.toml + if [ -f "$TOML_FILE" ]; then + echo "πŸ” Validating template.toml..." + if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$BLUEPRINT_PATH" --verbose); then + ERROR=1 + continue + fi else - echo "❌ ERROR: Docker Compose file failed full validation" - docker compose config 2>&1 || true + echo "⚠️ WARNING: template.toml not found" ERROR=1 + continue fi - cd - > /dev/null + echo "βœ… All validations passed for $blueprint" done if [ $ERROR -eq 1 ]; then echo "" - echo "❌ Docker Compose dry-run test failed" + echo "❌ Validation failed for one or more blueprints" exit 1 else echo "" - echo "βœ… All Docker Compose files passed dry-run test" + echo "βœ… All blueprints validated successfully" fi - - - name: Summary - if: always() - run: | - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "πŸ“Š Validation Summary" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - if [ "${{ steps.validate-compose-syntax.outcome }}" == "success" ] && \ - [ "${{ steps.validate-compose-practices.outcome }}" == "success" ] && \ - [ "${{ steps.validate-toml.outcome }}" == "success" ] && \ - [ "${{ steps.test-compose.outcome }}" == "success" ]; then - echo "βœ… All validations passed!" - echo "" - echo "Your Docker Compose and template.toml files are valid and ready to merge." - else - echo "❌ Some validations failed. Please review the errors above." - echo "" - echo "Common issues to check:" - echo " - docker-compose.yml syntax errors" - echo " - template.toml syntax errors" - echo " - serviceName in template.toml must match service names in docker-compose.yml" - echo " - Avoid using container_name, explicit networks, or port mappings" - fi -