diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 937cbd0..2b51de1 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -5,7 +5,29 @@ on: branches: - main paths: - - 'workloads/ecommerce/**' + # Código y configuración real del frontend. + # Los cambios exclusivos de GitOps (frontend.yaml, ingress, patches, etc.) + # no vuelven a construir la imagen. + - 'workloads/ecommerce/Dockerfile' + - 'workloads/ecommerce/.dockerignore' + - 'workloads/ecommerce/.npmrc' + - 'workloads/ecommerce/package.json' + - 'workloads/ecommerce/package-lock.json' + - 'workloads/ecommerce/next.config.*' + - 'workloads/ecommerce/tsconfig.json' + - 'workloads/ecommerce/tailwind.config.*' + - 'workloads/ecommerce/postcss.config.*' + - 'workloads/ecommerce/eslint.config.*' + - 'workloads/ecommerce/*.js' + - 'workloads/ecommerce/*.mjs' + - 'workloads/ecommerce/*.ts' + - 'workloads/ecommerce/*.tsx' + - 'workloads/ecommerce/app/**' + - 'workloads/ecommerce/src/**' + - 'workloads/ecommerce/components/**' + - 'workloads/ecommerce/lib/**' + - 'workloads/ecommerce/public/**' + - 'workloads/ecommerce/styles/**' - '.gitea/workflows/build.yaml' permissions: @@ -14,20 +36,29 @@ permissions: jobs: build: + name: Construir y Subir Imagen runs-on: ubuntu-latest - timeout-minutes: 20 # Aumentamos a 20 min para evitar que se corte el clone + timeout-minutes: 20 + + env: + APP_DIR: workloads/ecommerce + MANIFEST_FILE: workloads/ecommerce/frontend.yaml + IMAGE_NAME: gitea.cruzcloud.net/devops/ecommerce-frontend + steps: - name: Checkout del código uses: actions/checkout@v3 with: - fetch-depth: 1 # IMPORTANTE: Solo descarga el último commit, no todo el historial + fetch-depth: 1 + persist-credentials: true - # 1. NUEVO: Generamos la versión semántica dinámica + # Conservamos el esquema estable v1.0.X basado en el número del pipeline. - name: Definir Versión Semántica id: vars + shell: bash run: | - # Formato v1.0.X (X = número de ejecución del pipeline) - echo "VERSION=v1.0.${{ github.run_number }}" >> $GITHUB_OUTPUT + set -euo pipefail + echo "VERSION=v1.0.${{ github.run_number }}" >> "$GITHUB_OUTPUT" - name: Validar secretos del Registry shell: bash @@ -49,6 +80,76 @@ jobs: echo "OK: secretos del Registry disponibles." + # Evita que archivos de ejemplo entren en la compilación de Next.js. + # Se eliminan únicamente del workspace temporal del runner. + - name: Preparar contexto de producción + shell: bash + run: | + set -euo pipefail + + test -f "${APP_DIR}/Dockerfile" + test -f "${APP_DIR}/package.json" + test -f "${APP_DIR}/package-lock.json" + test -f "${MANIFEST_FILE}" + + rm -rf "${APP_DIR}/integration" + + find "${APP_DIR}" -type f \( -name '*.example.ts' -o -name '*.example.tsx' \) -delete + + echo "Contexto listo:" + du -sh "${APP_DIR}" + + # Diagnóstico temprano para el hotfix de navegación. + # No modifica el frontend, pero evita publicar silenciosamente un menú + # que existe y no está conectado al Header/Layout real. + - name: Validar integración del menú de categorías + shell: bash + run: | + set -euo pipefail + + MENU_FILE="$( + find "${APP_DIR}" -type f -name 'CategoryMenu.tsx' -print -quit + )" + + if [ -z "${MENU_FILE}" ]; then + echo "AVISO: no se encontró CategoryMenu.tsx." + echo "El build continuará usando el menú actualmente integrado." + exit 0 + fi + + echo "CategoryMenu encontrado: ${MENU_FILE}" + + if ! grep -Eq 'useSearchParams|URLSearchParams|window\.location\.search' "${MENU_FILE}"; then + echo "ERROR: CategoryMenu.tsx no deriva el estado activo desde la URL." + exit 1 + fi + + if ! grep -q 'collection=Marvel' "${MENU_FILE}"; then + echo "ERROR: falta collection=Marvel para Acción y aventura." + exit 1 + fi + + if ! grep -q 'category=Mochilas' "${MENU_FILE}"; then + echo "ERROR: falta category=Mochilas." + exit 1 + fi + + MENU_USAGE="$( + grep -Rsl --include='*.tsx' --include='*.ts' 'CategoryMenu' "${APP_DIR}/app" "${APP_DIR}/src" "${APP_DIR}/components" 2>/dev/null | + grep -Fvx "${MENU_FILE}" | + head -n 1 || + true + )" + + if [ -z "${MENU_USAGE}" ]; then + echo "ERROR: CategoryMenu.tsx existe, pero ningún Header/Layout lo importa." + echo "Debe renderizarse en el encabezado que ve el usuario." + exit 1 + fi + + echo "CategoryMenu integrado desde: ${MENU_USAGE}" + echo "Validación del menú: OK" + - name: Login en Gitea Registry uses: docker/login-action@v2 with: @@ -57,7 +158,6 @@ jobs: password: ${{ secrets.REGISTRY_PASSWORD }} logout: true - # 2. ACTUALIZADO: Usamos la nueva variable para los tags de Docker - name: Construir y Subir Imagen uses: docker/build-push-action@v4 with: @@ -67,25 +167,69 @@ jobs: gitea.cruzcloud.net/devops/ecommerce-frontend:${{ steps.vars.outputs.VERSION }} gitea.cruzcloud.net/devops/ecommerce-frontend:latest - # 3. ACTUALIZADO: Inyectamos la versión limpia en el manifiesto YAML - - name: Actualizar Manifiesto GitOps (CD) + # Evita que una ejecución antigua actualice frontend.yaml después de que + # ya exista un commit más reciente en main. + - name: Verificar promoción segura + id: promotion + shell: bash run: | + set -euo pipefail + + git fetch origin main + + CURRENT_SHA="${{ github.sha }}" + REMOTE_SHA="$(git rev-parse origin/main)" + + echo "Commit del pipeline: ${CURRENT_SHA}" + echo "Último commit remoto: ${REMOTE_SHA}" + + if [ "${CURRENT_SHA}" = "${REMOTE_SHA}" ]; then + echo "promote=true" >> "$GITHUB_OUTPUT" + echo "La imagen puede promocionarse." + else + echo "promote=false" >> "$GITHUB_OUTPUT" + echo "La imagen fue publicada, pero no se actualizará el manifiesto." + echo "Existe un commit más reciente y su pipeline debe realizar la promoción." + fi + + - name: Actualizar Manifiesto GitOps (CD) + if: steps.promotion.outputs.promote == 'true' + shell: bash + run: | + set -euo pipefail + + VERSION="${{ steps.vars.outputs.VERSION }}" + git config user.name "Cristian Felipe" git config user.email "cristiancruz0529@gmail.com" - - # 1. Forzamos a Git a ignorar la historia local y sincronizarse 100% con el servidor + + # Sincronizamos el workspace temporal con main. + # No se fuerza ni se sobrescribe la rama remota. git fetch origin main - git reset --hard origin/main - - # 2. Aplicamos el cambio de versión - sed -i -E "s|(image: gitea.cruzcloud.net/devops/ecommerce-frontend:).*|\1${{ steps.vars.outputs.VERSION }}|g" workloads/ecommerce/frontend.yaml - - # 3. Solo hacemos commit si realmente hubo un cambio - git add workloads/ecommerce/frontend.yaml + git checkout -B main origin/main + + sed -i -E "s|(image: ${IMAGE_NAME}:).*|\1${VERSION}|g" "${MANIFEST_FILE}" + + git add "${MANIFEST_FILE}" + if git diff --cached --quiet; then echo "No hay cambios en el manifiesto." - else - git commit -m "chore: release ${{ steps.vars.outputs.VERSION }} [skip ci]" - # 4. Forzamos el push usando --force para limpiar cualquier discrepancia - git push origin HEAD:main --force - fi \ No newline at end of file + exit 0 + fi + + git commit -m "chore: release ${VERSION} [skip ci]" + + # Push normal: si main cambió durante el job, se detiene para evitar + # sobrescribir commits ajenos o promocionar una versión antigua. + git push origin HEAD:main + + - name: Resumen del pipeline + if: always() + shell: bash + run: | + echo "========================================" + echo "ARI Shopping Frontend" + echo "Versión: ${{ steps.vars.outputs.VERSION }}" + echo "Commit: ${{ github.sha }}" + echo "Promoción GitOps: ${{ steps.promotion.outputs.promote }}" + echo "========================================" \ No newline at end of file