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.
This commit is contained in:
Mauricio Siu
2025-12-19 15:54:36 -06:00
parent 4757c59da3
commit dd8ef5b3c8

View File

@@ -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<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_COMPOSE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "toml_files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_TOML" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "directories<<EOF" >> $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<<EOF" >> $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