|
| 1 | +name: 🚀 Promote to Main |
| 2 | + |
| 3 | +on: |
| 4 | + pull_request: |
| 5 | + branches: |
| 6 | + - main |
| 7 | + types: [opened, synchronize, reopened] |
| 8 | + |
| 9 | + push: |
| 10 | + branches: |
| 11 | + - developer |
| 12 | + - develop |
| 13 | + |
| 14 | + workflow_dispatch: |
| 15 | + inputs: |
| 16 | + force_deploy: |
| 17 | + description: 'Force deploy to main' |
| 18 | + required: false |
| 19 | + default: 'false' |
| 20 | + type: boolean |
| 21 | + |
| 22 | +env: |
| 23 | + NODE_VERSION: '18' |
| 24 | + |
| 25 | +jobs: |
| 26 | + validate-for-production: |
| 27 | + name: 🔍 Production Validation |
| 28 | + runs-on: ubuntu-latest |
| 29 | + if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.head_ref == 'developer') |
| 30 | + |
| 31 | + steps: |
| 32 | + - name: 📥 Checkout Repository |
| 33 | + uses: actions/checkout@v4 |
| 34 | + with: |
| 35 | + fetch-depth: 0 |
| 36 | + |
| 37 | + - name: 🏷️ Extract Branch Info |
| 38 | + id: branch-info |
| 39 | + run: | |
| 40 | + echo "source_branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT |
| 41 | + echo "target_branch=${GITHUB_BASE_REF:-main}" >> $GITHUB_OUTPUT |
| 42 | + echo "is_developer_branch=$([[ ${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} == developer ]] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT |
| 43 | +
|
| 44 | + - name: ✅ Validate Source Branch |
| 45 | + run: | |
| 46 | + echo "🌿 Source: ${{ steps.branch-info.outputs.source_branch }}" |
| 47 | + echo "🎯 Target: ${{ steps.branch-info.outputs.target_branch }}" |
| 48 | + |
| 49 | + # Solo permitir developer -> main |
| 50 | + if [ "${{ steps.branch-info.outputs.target_branch }}" == "main" ] && [ "${{ steps.branch-info.outputs.source_branch }}" != "developer" ]; then |
| 51 | + echo "❌ ERROR: Solo la rama 'developer' puede hacer PR hacia 'main'" |
| 52 | + echo " Rama origen actual: ${{ steps.branch-info.outputs.source_branch }}" |
| 53 | + exit 1 |
| 54 | + fi |
| 55 | + |
| 56 | + echo "✅ Rama origen válida para producción" |
| 57 | +
|
| 58 | + - name: ⚡ Setup Node.js |
| 59 | + uses: actions/setup-node@v4 |
| 60 | + with: |
| 61 | + node-version: ${{ env.NODE_VERSION }} |
| 62 | + cache: 'npm' |
| 63 | + |
| 64 | + - name: 🔧 Install Dependencies |
| 65 | + run: | |
| 66 | + echo "📦 Installing dependencies for production validation..." |
| 67 | + npm ci --prefer-offline --no-audit |
| 68 | +
|
| 69 | + - name: 🔍 TypeScript Production Check |
| 70 | + id: typescript-check |
| 71 | + run: | |
| 72 | + echo "🔍 Validando tipos TypeScript para producción..." |
| 73 | + npx tsc --noEmit |
| 74 | + echo "✅ TypeScript validation pasó" |
| 75 | +
|
| 76 | + - name: 🧪 Run Full Test Suite |
| 77 | + id: full-tests |
| 78 | + run: | |
| 79 | + echo "🧪 Ejecutando suite completa de tests para producción..." |
| 80 | + npm run test:structure |
| 81 | + echo "✅ Tests de estructura pasaron" |
| 82 | +
|
| 83 | + - name: 🔍 Validate Module Content |
| 84 | + id: content-validation |
| 85 | + run: | |
| 86 | + echo "🔍 Validando contenido de módulos..." |
| 87 | + |
| 88 | + # Verificar que los módulos no estén vacíos |
| 89 | + MODULES_WITH_CONTENT=0 |
| 90 | + TOTAL_MODULES=0 |
| 91 | + |
| 92 | + for module_dir in src/core/*/; do |
| 93 | + if [ -d "$module_dir" ] && [ "$(basename "$module_dir")" != "shared" ]; then |
| 94 | + TOTAL_MODULES=$((TOTAL_MODULES + 1)) |
| 95 | + module_name=$(basename "$module_dir") |
| 96 | + |
| 97 | + echo "📂 Verificando módulo: $module_name" |
| 98 | + |
| 99 | + # Contar archivos en el módulo (excluyendo directorios vacíos) |
| 100 | + FILE_COUNT=$(find "$module_dir" -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | wc -l) |
| 101 | + |
| 102 | + if [ "$FILE_COUNT" -gt 0 ]; then |
| 103 | + MODULES_WITH_CONTENT=$((MODULES_WITH_CONTENT + 1)) |
| 104 | + echo " ✅ $module_name tiene $FILE_COUNT archivos" |
| 105 | + else |
| 106 | + echo " ⚠️ $module_name está vacío" |
| 107 | + fi |
| 108 | + fi |
| 109 | + done |
| 110 | + |
| 111 | + echo "📊 Módulos con contenido: $MODULES_WITH_CONTENT/$TOTAL_MODULES" |
| 112 | + |
| 113 | + if [ "$MODULES_WITH_CONTENT" -eq 0 ]; then |
| 114 | + echo "❌ ERROR: Ningún módulo tiene contenido. No se puede promover a main." |
| 115 | + exit 1 |
| 116 | + fi |
| 117 | + |
| 118 | + # Requerir al menos 1 módulo con contenido para pasar a main |
| 119 | + if [ "$MODULES_WITH_CONTENT" -lt 1 ]; then |
| 120 | + echo "❌ ERROR: Se requiere al menos 1 módulo con contenido para promover a main" |
| 121 | + exit 1 |
| 122 | + fi |
| 123 | + |
| 124 | + echo "✅ Validación de contenido pasó: $MODULES_WITH_CONTENT módulos tienen contenido" |
| 125 | + echo "modules_with_content=$MODULES_WITH_CONTENT" >> $GITHUB_OUTPUT |
| 126 | + echo "total_modules=$TOTAL_MODULES" >> $GITHUB_OUTPUT |
| 127 | +
|
| 128 | + - name: 🏗️ Build for Production |
| 129 | + run: | |
| 130 | + echo "🏗️ Building for production..." |
| 131 | + npm run build |
| 132 | + echo "✅ Build completado exitosamente" |
| 133 | +
|
| 134 | + - name: 📊 Generate Production Report |
| 135 | + run: | |
| 136 | + echo "📊 Generando reporte de producción..." |
| 137 | + |
| 138 | + echo "## 🚀 Production Readiness Report" > production-report.md |
| 139 | + echo "" >> production-report.md |
| 140 | + echo "- ✅ Structure validation: **PASSED**" >> production-report.md |
| 141 | + echo "- ✅ Build process: **PASSED**" >> production-report.md |
| 142 | + echo "- ✅ Content validation: **PASSED**" >> production-report.md |
| 143 | + echo "- 📊 Modules with content: **${{ steps.content-validation.outputs.modules_with_content }}/${{ steps.content-validation.outputs.total_modules }}**" >> production-report.md |
| 144 | + echo "" >> production-report.md |
| 145 | + echo "### Module Status:" >> production-report.md |
| 146 | + |
| 147 | + for module_dir in src/core/*/; do |
| 148 | + if [ -d "$module_dir" ] && [ "$(basename "$module_dir")" != "shared" ]; then |
| 149 | + module_name=$(basename "$module_dir") |
| 150 | + FILE_COUNT=$(find "$module_dir" -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | wc -l) |
| 151 | + |
| 152 | + if [ "$FILE_COUNT" -gt 0 ]; then |
| 153 | + echo "- ✅ **$module_name**: $FILE_COUNT files" >> production-report.md |
| 154 | + else |
| 155 | + echo "- ⚠️ **$module_name**: Empty module" >> production-report.md |
| 156 | + fi |
| 157 | + fi |
| 158 | + done |
| 159 | +
|
| 160 | + - name: 📤 Upload Production Report |
| 161 | + uses: actions/upload-artifact@v4 |
| 162 | + with: |
| 163 | + name: production-readiness-report |
| 164 | + path: production-report.md |
| 165 | + |
| 166 | + - name: 💬 Comment Production Report on PR |
| 167 | + if: github.event_name == 'pull_request' |
| 168 | + uses: actions/github-script@v7 |
| 169 | + with: |
| 170 | + script: | |
| 171 | + const fs = require('fs'); |
| 172 | + |
| 173 | + let comment = `## 🚀 Production Readiness Report\\n\\n`; |
| 174 | + comment += `✅ **Ready for Main Branch!**\\n\\n`; |
| 175 | + comment += `📊 **Validation Results:**\\n`; |
| 176 | + comment += `- Structure Tests: ✅ PASSED\\n`; |
| 177 | + comment += `- Content Validation: ✅ PASSED\\n`; |
| 178 | + comment += `- Production Build: ✅ PASSED\\n`; |
| 179 | + comment += `- Modules with Content: **${{ steps.content-validation.outputs.modules_with_content }}/${{ steps.content-validation.outputs.total_modules }}**\\n\\n`; |
| 180 | + |
| 181 | + comment += `🎯 **This PR is approved for production deployment!**\\n\\n`; |
| 182 | + comment += `🔗 [View detailed report in Actions](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`; |
| 183 | + |
| 184 | + github.rest.issues.createComment({ |
| 185 | + issue_number: context.issue.number, |
| 186 | + owner: context.repo.owner, |
| 187 | + repo: context.repo.repo, |
| 188 | + body: comment |
| 189 | + }); |
| 190 | +
|
| 191 | + auto-promote-to-main: |
| 192 | + name: 🔄 Auto-promote to Main |
| 193 | + runs-on: ubuntu-latest |
| 194 | + needs: [validate-for-production] |
| 195 | + if: github.event_name == 'push' && github.ref == 'refs/heads/developer' |
| 196 | + |
| 197 | + steps: |
| 198 | + - name: 📥 Checkout Repository |
| 199 | + uses: actions/checkout@v4 |
| 200 | + with: |
| 201 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 202 | + fetch-depth: 0 |
| 203 | + |
| 204 | + - name: 🚀 Create PR to Main |
| 205 | + uses: actions/github-script@v7 |
| 206 | + with: |
| 207 | + script: | |
| 208 | + const { data: existingPRs } = await github.rest.pulls.list({ |
| 209 | + owner: context.repo.owner, |
| 210 | + repo: context.repo.repo, |
| 211 | + head: 'developer', |
| 212 | + base: 'main', |
| 213 | + state: 'open' |
| 214 | + }); |
| 215 | +
|
| 216 | + if (existingPRs.length === 0) { |
| 217 | + const { data: pr } = await github.rest.pulls.create({ |
| 218 | + owner: context.repo.owner, |
| 219 | + repo: context.repo.repo, |
| 220 | + title: '🚀 Deploy to Production', |
| 221 | + head: 'developer', |
| 222 | + base: 'main', |
| 223 | + body: `## 🚀 Automated Production Deployment |
| 224 | +
|
| 225 | + This PR was automatically created after successful validation on the \`developer\` branch. |
| 226 | +
|
| 227 | + ### ✅ Pre-deployment Validations Completed: |
| 228 | + - Structure tests passed |
| 229 | + - All modules validated |
| 230 | + - Content validation passed |
| 231 | + - Production build successful |
| 232 | +
|
| 233 | + ### 📋 Review Checklist: |
| 234 | + - [ ] Code review completed |
| 235 | + - [ ] All tests passing |
| 236 | + - [ ] Documentation updated |
| 237 | + - [ ] Ready for production |
| 238 | +
|
| 239 | + **Auto-generated from developer branch** |
| 240 | + ` |
| 241 | + }); |
| 242 | +
|
| 243 | + console.log(\`Created PR #\${pr.number}: \${pr.html_url}\`); |
| 244 | + } else { |
| 245 | + console.log('PR from developer to main already exists'); |
| 246 | + } |
0 commit comments