Merge pull request #14 from MathiasAugustto/add-agus #45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy to GitHub Pages | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| pages: write | |
| id-token: write | |
| pull-requests: write | |
| checks: write | |
| statuses: write | |
| actions: write | |
| concurrency: | |
| group: "pages" | |
| cancel-in-progress: false | |
| jobs: | |
| # Validación de archivos | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "18" | |
| - name: Validate contributors data | |
| run: | | |
| echo "Validando estructura de contributors-data.js..." | |
| # Verificar que el archivo existe | |
| if [ ! -f "contributors-data.js" ]; then | |
| echo "❌ Error: contributors-data.js no encontrado" | |
| exit 1 | |
| fi | |
| # Verificar sintaxis JavaScript básica | |
| node -e " | |
| try { | |
| const fs = require('fs'); | |
| const content = fs.readFileSync('contributors-data.js', 'utf8'); | |
| // Verificar que contiene la estructura básica esperada | |
| if (!content.includes('const contributors')) { | |
| throw new Error('No se encontró la declaración de contributors'); | |
| } | |
| if (!content.includes('module.exports')) { | |
| throw new Error('No se encontró module.exports'); | |
| } | |
| // Evaluar el archivo para verificar sintaxis | |
| const contributorsMatch = content.match(/const contributors = (\[[\s\S]*?\]);/); | |
| if (!contributorsMatch) { | |
| throw new Error('No se encontró el array contributors válido'); | |
| } | |
| const contributors = eval(contributorsMatch[1]); | |
| if (!Array.isArray(contributors)) { | |
| throw new Error('contributors debe ser un array'); | |
| } | |
| console.log(\`📊 Encontrados \${contributors.length} colaboradores\`); | |
| // Validar cada colaborador | |
| contributors.forEach((contributor, index) => { | |
| const errors = []; | |
| if (!contributor.name || typeof contributor.name !== 'string' || contributor.name.trim() === '') { | |
| errors.push('name es requerido y debe ser una cadena no vacía'); | |
| } | |
| if (!contributor.nickname || typeof contributor.nickname !== 'string' || contributor.nickname.trim() === '') { | |
| errors.push('nickname es requerido y debe ser una cadena no vacía'); | |
| } | |
| if (!contributor.github || typeof contributor.github !== 'string' || !contributor.github.startsWith('https://github.com/')) { | |
| errors.push('github es requerido y debe ser una URL válida de GitHub'); | |
| } | |
| if (!contributor.description || typeof contributor.description !== 'string' || contributor.description.trim() === '') { | |
| errors.push('description es requerido y debe ser una cadena no vacía'); | |
| } | |
| if (contributor.description && contributor.description.length > 200) { | |
| errors.push('description debe tener máximo 200 caracteres'); | |
| } | |
| if (!Array.isArray(contributor.hobbies) || contributor.hobbies.length === 0) { | |
| errors.push('hobbies debe ser un array con al menos un elemento'); | |
| } | |
| if (contributor.hobbies && contributor.hobbies.length > 4) { | |
| errors.push('máximo 4 hobbies permitidos'); | |
| } | |
| if (contributor.linkedin && (!contributor.linkedin.includes('linkedin.com') || !contributor.linkedin.startsWith('http'))) { | |
| errors.push('LinkedIn debe ser una URL válida que contenga linkedin.com'); | |
| } | |
| // Verificar duplicados de nickname | |
| const duplicates = contributors.filter(c => c.nickname === contributor.nickname); | |
| if (duplicates.length > 1) { | |
| errors.push(\`nickname '\${contributor.nickname}' está duplicado\`); | |
| } | |
| // Verificar duplicados de GitHub | |
| const duplicatesGithub = contributors.filter(c => c.github === contributor.github); | |
| if (duplicatesGithub.length > 1) { | |
| errors.push(\`GitHub URL '\${contributor.github}' está duplicada\`); | |
| } | |
| if (errors.length > 0) { | |
| console.log(\`❌ Colaborador \${index + 1} (\${contributor.name || 'Sin nombre'}): \${errors.join(', ')}\`); | |
| process.exit(1); | |
| } | |
| console.log(\`✅ Colaborador \${index + 1}: \${contributor.name} (@\${contributor.nickname}) - OK\`); | |
| }); | |
| console.log('✅ contributors-data.js tiene estructura válida'); | |
| } catch (error) { | |
| console.error('❌ Error en contributors-data.js:', error.message); | |
| process.exit(1); | |
| } | |
| " | |
| - name: Validate HTML files | |
| run: | | |
| echo "Validando archivos HTML..." | |
| # Verificar archivos HTML principales | |
| for file in index.html como-contribuir.html; do | |
| if [ ! -f "$file" ]; then | |
| echo "❌ Error: $file no encontrado" | |
| exit 1 | |
| fi | |
| # Verificar sintaxis HTML básica | |
| if ! grep -q "<!DOCTYPE html>" "$file"; then | |
| echo "❌ Error: $file no tiene DOCTYPE válido" | |
| exit 1 | |
| fi | |
| echo "✅ $file es válido" | |
| done | |
| - name: Validate JavaScript files | |
| run: | | |
| echo "Validando archivos JavaScript..." | |
| # Verificar archivos JS en la carpeta js/ | |
| for file in js/contributors.js js/script.js; do | |
| if [ ! -f "$file" ]; then | |
| echo "❌ Error: $file no encontrado" | |
| exit 1 | |
| fi | |
| # Verificar sintaxis JavaScript básica | |
| node -c "$file" | |
| echo "✅ $file tiene sintaxis válida" | |
| done | |
| - name: Validate CSS files | |
| run: | | |
| echo "Validando archivos CSS..." | |
| # Verificar que los archivos CSS existen | |
| for file in styles/como-contribuir.css; do | |
| if [ ! -f "$file" ]; then | |
| echo "❌ Error: $file no encontrado" | |
| exit 1 | |
| fi | |
| # Verificar que el archivo no está vacío | |
| if [ ! -s "$file" ]; then | |
| echo "❌ Error: $file está vacío" | |
| exit 1 | |
| fi | |
| echo "✅ $file es válido" | |
| done | |
| # Validación para Pull Requests | |
| validate-pr: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "18" | |
| - name: PR Validation | |
| run: | | |
| echo "🔍 Validando Pull Request de ${{ github.actor }}..." | |
| # Verificar que solo se modificó contributors-data.js | |
| git fetch origin main | |
| # Obtener archivos modificados con manejo de error | |
| CHANGED_FILES="" | |
| if git merge-base origin/main HEAD >/dev/null 2>&1; then | |
| CHANGED_FILES=$(git diff --name-only origin/main...HEAD) | |
| else | |
| echo "⚠️ No se encontró base común, comparando con HEAD~1" | |
| if git rev-parse HEAD~1 >/dev/null 2>&1; then | |
| CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD) | |
| else | |
| echo "ℹ️ Primer commit detectado, validando archivos actuales" | |
| CHANGED_FILES=$(git ls-files) | |
| fi | |
| fi | |
| echo "📝 Archivos modificados:" | |
| echo "$CHANGED_FILES" | |
| if [ -z "$CHANGED_FILES" ]; then | |
| echo "ℹ️ No se detectaron cambios en archivos" | |
| exit 0 | |
| fi | |
| ALLOWED_PATTERN="^(contributors-data\.js|README\.md|\.github/.*|docs/.*|_config\.yml|\.gitignore|index\.html|como-contribuir\.html|js/.*|styles/.*|assets/.*)$" | |
| SETUP_FILES_COUNT=$(echo "$CHANGED_FILES" | grep -E "(index\.html|contributors-data\.js|js/|styles/)" | wc -l) | |
| if [ "$SETUP_FILES_COUNT" -gt 3 ]; then | |
| echo "🔧 Setup inicial detectado - validación flexible" | |
| IS_INITIAL_SETUP=true | |
| else | |
| echo "📝 Pull Request regular - validación estricta" | |
| IS_INITIAL_SETUP=false | |
| ALLOWED_PATTERN="^(contributors-data\.js|README\.md|\.github/.*|docs/.*)$" | |
| fi | |
| while IFS= read -r file; do | |
| if [ -n "$file" ] && [[ ! "$file" =~ $ALLOWED_PATTERN ]]; then | |
| if [ "$IS_INITIAL_SETUP" = true ]; then | |
| echo "⚠️ Archivo no estándar en setup inicial: $file" | |
| else | |
| echo "❌ Error: No se permite modificar $file" | |
| echo "💡 Solo puedes modificar contributors-data.js para agregar tu información" | |
| exit 1 | |
| fi | |
| fi | |
| done <<< "$CHANGED_FILES" | |
| if echo "$CHANGED_FILES" | grep -q "contributors-data.js" && [ -f "contributors-data.js" ]; then | |
| echo "✅ contributors-data.js fue modificado" | |
| CHANGED_FILES="$CHANGED_FILES" node -e " | |
| const fs = require('fs'); | |
| const { execSync } = require('child_process'); | |
| try { | |
| const currentContent = fs.readFileSync('contributors-data.js', 'utf8'); | |
| const currentMatch = currentContent.match(/const contributors = (\[[\s\S]*?\]);/); | |
| const currentContributors = currentMatch ? eval(currentMatch[1]) : []; | |
| console.log(\`📊 Colaboradores actuales: \${currentContributors.length}\`); | |
| let mainContributors = []; | |
| let isInitialSetup = false; | |
| try { | |
| execSync('git show origin/main:contributors-data.js > /tmp/main-contributors.js 2>/dev/null'); | |
| const mainContent = fs.readFileSync('/tmp/main-contributors.js', 'utf8'); | |
| const mainMatch = mainContent.match(/const contributors = (\[[\s\S]*?\]);/); | |
| mainContributors = mainMatch ? eval(mainMatch[1]) : []; | |
| console.log(\`📊 Colaboradores en main: \${mainContributors.length}\`); | |
| } catch (error) { | |
| console.log('ℹ️ No se pudo obtener versión anterior (posible setup inicial)'); | |
| isInitialSetup = true; | |
| } | |
| const setupFilesPattern = /(index\.html|js\/|styles\/|assets\/)/; | |
| const changedFiles = process.env.CHANGED_FILES || ''; | |
| const hasSetupFiles = changedFiles.split('\\n').some(file => setupFilesPattern.test(file)); | |
| if (isInitialSetup || hasSetupFiles) { | |
| console.log('🔧 Setup inicial detectado - validación flexible'); | |
| if (currentContributors.length > 0) { | |
| console.log(\`✅ Setup inicial con \${currentContributors.length} colaborador(es)\`); | |
| currentContributors.forEach((contributor, index) => { | |
| if (contributor.name && contributor.nickname && contributor.hobbies) { | |
| console.log(\`✅ Colaborador \${index + 1}: \${contributor.name} (@\${contributor.nickname})\`); | |
| } | |
| }); | |
| process.exit(0); | |
| } | |
| } | |
| const newContributorsCount = currentContributors.length - mainContributors.length; | |
| if (newContributorsCount <= 0) { | |
| console.log('❌ Error: No se detectaron nuevos colaboradores'); | |
| process.exit(1); | |
| } | |
| if (newContributorsCount > 1) { | |
| console.log('❌ Error: Solo puedes agregar un colaborador por PR'); | |
| console.log(\` Colaboradores agregados: \${newContributorsCount}\`); | |
| process.exit(1); | |
| } | |
| const newContributor = currentContributors.find(c => | |
| !mainContributors.some(m => m.nickname === c.nickname) | |
| ); | |
| if (newContributor) { | |
| console.log(\`✅ Nuevo colaborador detectado: \${newContributor.name} (@\${newContributor.nickname})\`); | |
| } else { | |
| console.log('❌ Error: No se pudo identificar el nuevo colaborador'); | |
| process.exit(1); | |
| } | |
| } catch (error) { | |
| console.log('❌ Error al validar colaboradores:', error.message); | |
| process.exit(1); | |
| } | |
| " | |
| else | |
| echo "ℹ️ contributors-data.js no fue modificado en este PR" | |
| fi | |
| echo "🎉 Pull Request válido!" | |
| # Auto-merge para Pull Requests válidos | |
| auto-merge: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| needs: validate-pr | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| checks: write | |
| statuses: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Auto-approve PR | |
| run: | | |
| echo "✅ Aprobando PR automáticamente..." | |
| curl -X POST \ | |
| -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/reviews" \ | |
| -d '{ | |
| "event": "APPROVE", | |
| "body": "✅ Validación automática exitosa. Este PR será mergeado automáticamente." | |
| }' | |
| - name: Enable auto-merge | |
| run: | | |
| echo "🤖 Activando auto-merge para PR #${{ github.event.number }}..." | |
| curl -X PUT \ | |
| -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/merge" \ | |
| -d '{ | |
| "commit_title": "Auto-merge: ${{ github.event.pull_request.title }}", | |
| "commit_message": "Merged by automated workflow.\nPR Author: ${{ github.event.pull_request.user.login }}", | |
| "merge_method": "squash" | |
| }' | |
| - name: Comentar en el PR | |
| if: success() | |
| run: | | |
| echo "💬 Notificando al usuario..." | |
| curl -X POST \ | |
| -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.number }}/comments" \ | |
| -d '{ | |
| "body": "🎉 **¡Contribución aceptada!**\n\n✅ Validación pasó\n🤖 PR mergeado automáticamente\n🚀 Tu perfil aparecerá en [la página web](https://${{ github.repository_owner }}.github.io/RepoColaborativo/) en unos minutos\n\n¡Gracias por contribuir! 👏" | |
| }' | |
| # Deploy a GitHub Pages | |
| deploy: | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| runs-on: ubuntu-latest | |
| needs: [validate, auto-merge] | |
| if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Pages | |
| uses: actions/configure-pages@v4 | |
| - name: Prepare deployment | |
| run: | | |
| echo "Preparando archivos para deployment..." | |
| mkdir -p _site | |
| cp index.html _site/ | |
| cp como-contribuir.html _site/ | |
| cp contributors-data.js _site/ | |
| cp -r js/ _site/js/ | |
| cp -r styles/ _site/styles/ | |
| [ -d "assets" ] && cp -r assets/ _site/assets/ | |
| [ -d "images" ] && cp -r images/ _site/images/ | |
| touch _site/.nojekyll | |
| echo "✅ Archivos preparados para deployment" | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: "_site" | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| # Verificación post-deploy | |
| verify: | |
| runs-on: ubuntu-latest | |
| needs: deploy | |
| if: github.ref == 'refs/heads/main' && needs.deploy.result == 'success' | |
| steps: | |
| - name: Verify deployment | |
| run: | | |
| echo "Verificando deployment..." | |
| sleep 30 | |
| if curl -f -s "https://${{ github.repository_owner }}.github.io/RepoColaborativo/" > /dev/null; then | |
| echo "✅ Página principal accessible" | |
| else | |
| echo "❌ Error: Página principal no accessible" | |
| exit 1 | |
| fi | |
| echo "✅ Deployment verificado exitosamente" |