Skip to content

Merge pull request #14 from MathiasAugustto/add-agus #45

Merge pull request #14 from MathiasAugustto/add-agus

Merge pull request #14 from MathiasAugustto/add-agus #45

Workflow file for this run

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"