update-projects #71
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 ] | |
| workflow_dispatch: | |
| repository_dispatch: | |
| types: [ update-projects ] | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| concurrency: | |
| group: "pages" | |
| cancel-in-progress: false | |
| jobs: | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout main repository | |
| uses: actions/checkout@v4 | |
| - name: Install Puppeteer system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2t64 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Install dependencies & Build ctgcode.com | |
| run: | | |
| bun install --frozen-lockfile | |
| bun run build | |
| # Las tres plantillas se construyen con PUBLIC_DEMO="true": son muestras | |
| # que simulan negocios inexistentes, así que salen con `noindex, follow`, | |
| # sin el JSON-LD del negocio y sin sitemap propio. Quien posiciona es la | |
| # página dedicada de cada plantilla en /plantillas/. | |
| - name: Checkout plantilla-negocio-local | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ctgcode-com/plantilla-negocio-local | |
| path: templates/plantilla-negocio-local | |
| token: ${{ secrets.PORTFOLIO_TRIGGER_TOKEN || github.token }} | |
| - name: Build plantilla-negocio-local (ES & EN) | |
| run: | | |
| mkdir -p dist/en | |
| cd templates/plantilla-negocio-local | |
| bun install | |
| BASE_PATH="/plantilla-negocio-local" PUBLIC_LOCALE="es" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/plantilla-negocio-local | |
| BASE_PATH="/en/template-local-business" PUBLIC_LOCALE="en" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/en/template-local-business | |
| cd ../.. | |
| - name: Checkout plantilla-servicios-profesionales | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ctgcode-com/plantilla-servicios-profesionales | |
| path: templates/plantilla-servicios-profesionales | |
| token: ${{ secrets.PORTFOLIO_TRIGGER_TOKEN || github.token }} | |
| - name: Build plantilla-servicios-profesionales (ES & EN) | |
| run: | | |
| mkdir -p dist/en | |
| cd templates/plantilla-servicios-profesionales | |
| bun install | |
| BASE_PATH="/plantilla-servicios-profesionales" PUBLIC_LOCALE="es" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/plantilla-servicios-profesionales | |
| BASE_PATH="/en/template-professional-services" PUBLIC_LOCALE="en" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/en/template-professional-services | |
| cd ../.. | |
| - name: Checkout plantilla-producto-startup | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ctgcode-com/plantilla-producto-startup | |
| path: templates/plantilla-producto-startup | |
| token: ${{ secrets.PORTFOLIO_TRIGGER_TOKEN || github.token }} | |
| - name: Build plantilla-producto-startup (ES & EN) | |
| run: | | |
| mkdir -p dist/en | |
| cd templates/plantilla-producto-startup | |
| bun install | |
| BASE_PATH="/plantilla-producto-startup" PUBLIC_LOCALE="es" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/plantilla-producto-startup | |
| BASE_PATH="/en/template-startup-product" PUBLIC_LOCALE="en" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/en/template-startup-product | |
| cd ../.. | |
| # Las tres demos de automatización se construyen igual que las plantillas | |
| # y por la misma razón: simulan negocios que no existen, así que salen | |
| # con `noindex, follow` y sin sitemap propio. La diferencia es que aquí | |
| # no hay página dedicada que posicione — quien lo hace es la sección de | |
| # automatizaciones de /proyectos, que es la que enlaza a cada demo. | |
| - name: Checkout control-acceso-smart | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ctgcode-com/control-acceso-smart | |
| path: automations/control-acceso-smart | |
| token: ${{ secrets.PORTFOLIO_TRIGGER_TOKEN || github.token }} | |
| - name: Build demo control de acceso (ES & EN) | |
| run: | | |
| mkdir -p dist/en | |
| cd automations/control-acceso-smart | |
| bun install | |
| BASE_PATH="/automatizacion-control-acceso" PUBLIC_LOCALE="es" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/automatizacion-control-acceso | |
| BASE_PATH="/en/automation-access-control" PUBLIC_LOCALE="en" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/en/automation-access-control | |
| cd ../.. | |
| - name: Checkout gestion-reservas-clases | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ctgcode-com/gestion-reservas-clases | |
| path: automations/gestion-reservas-clases | |
| token: ${{ secrets.PORTFOLIO_TRIGGER_TOKEN || github.token }} | |
| - name: Build demo motor de reservas (ES & EN) | |
| run: | | |
| mkdir -p dist/en | |
| cd automations/gestion-reservas-clases | |
| bun install | |
| BASE_PATH="/automatizacion-reservas" PUBLIC_LOCALE="es" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/automatizacion-reservas | |
| BASE_PATH="/en/automation-bookings" PUBLIC_LOCALE="en" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/en/automation-bookings | |
| cd ../.. | |
| - name: Checkout alertas-facturacion-recurrente | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ctgcode-com/alertas-facturacion-recurrente | |
| path: automations/alertas-facturacion-recurrente | |
| token: ${{ secrets.PORTFOLIO_TRIGGER_TOKEN || github.token }} | |
| - name: Build demo alertas de facturación (ES & EN) | |
| run: | | |
| mkdir -p dist/en | |
| cd automations/alertas-facturacion-recurrente | |
| bun install | |
| BASE_PATH="/automatizacion-alertas-facturacion" PUBLIC_LOCALE="es" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/automatizacion-alertas-facturacion | |
| BASE_PATH="/en/automation-billing-alerts" PUBLIC_LOCALE="en" PUBLIC_DEMO="true" bun run build | |
| cp -r dist ../../dist/en/automation-billing-alerts | |
| cd ../.. | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: ./dist | |
| deploy: | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| # Avisa a los motores de búsqueda (Bing, Yandex y demás vía IndexNow) de que | |
| # el sitio se ha actualizado, para que recrawleen sin esperar. Complementa a | |
| # los sitemaps registrados en Bing Webmaster Tools. No bloquea el despliegue: | |
| # si el sitemap aún no propaga o IndexNow falla, solo deja un aviso. | |
| indexnow: | |
| name: Notify IndexNow | |
| needs: deploy | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Submit sitemap URLs to IndexNow | |
| # Sin `set -e` a propósito: los fallos se manejan explícitamente. Con | |
| # `set -e`, `[ -n "$urls" ] && break` abortaba el job cuando el sitemap | |
| # aún no había propagado por el CDN (en vez de reintentar). | |
| run: | | |
| set -uo pipefail | |
| HOST="ctgcode.com" | |
| # La clave es el nombre del archivo servido en public/ (sin .txt). | |
| # Si rotas la clave, cambia este valor y el archivo en public/. | |
| KEY="234f3a26e66c418f8923f549ce9fd3a9" | |
| KEY_LOCATION="https://${HOST}/${KEY}.txt" | |
| SITEMAP_INDEX="https://${HOST}/sitemap-index.xml" | |
| # Endpoint de Bing: cualquier endpoint IndexNow comparte el aviso con | |
| # el resto de motores, pero enviamos al de Bing porque es donde se | |
| # verifica (Bing Webmaster Tools). | |
| ENDPOINT="https://www.bing.com/indexnow" | |
| UA="ctgcode-indexnow-bot/1.0 (+https://${HOST})" | |
| # El despliegue puede tardar unos segundos en propagar por el CDN: | |
| # reintenta la lectura del sitemap antes de rendirse. | |
| urls="" | |
| for attempt in $(seq 1 6); do | |
| submaps=$(curl -fsSL -A "$UA" "$SITEMAP_INDEX" 2>/dev/null \ | |
| | grep -oE '<loc>[^<]+</loc>' | sed -E 's#</?loc>##g') | |
| collected="" | |
| for sm in $submaps; do | |
| locs=$(curl -fsSL -A "$UA" "$sm" 2>/dev/null \ | |
| | grep -oE '<loc>[^<]+</loc>' | sed -E 's#</?loc>##g') | |
| collected="${collected}"$'\n'"${locs}" | |
| done | |
| urls=$(printf '%s\n' "$collected" | sed '/^$/d' | sort -u) | |
| if [ -n "$urls" ]; then | |
| break | |
| fi | |
| echo "Sitemap aún no disponible; reintento en 15 s (intento ${attempt}/6)…" | |
| sleep 15 | |
| done | |
| if [ -z "$urls" ]; then | |
| echo "::error::No se pudieron obtener URLs del sitemap tras varios intentos." | |
| exit 1 | |
| fi | |
| count=$(printf '%s\n' "$urls" | wc -l | tr -d ' ') | |
| echo "Enviando ${count} URLs a IndexNow (${ENDPOINT}):" | |
| printf ' %s\n' $urls | |
| # Construye el payload con jq (evita problemas de escapado en el JSON). | |
| payload=$(printf '%s\n' "$urls" | jq -R . | jq -s \ | |
| --arg host "$HOST" --arg key "$KEY" --arg kl "$KEY_LOCATION" \ | |
| '{host: $host, key: $key, keyLocation: $kl, urlList: .}') | |
| echo "Payload:" | |
| echo "$payload" | |
| http_code=$(curl -sS -o indexnow_resp.txt -w '%{http_code}' \ | |
| -X POST "$ENDPOINT" \ | |
| -H "Content-Type: application/json; charset=utf-8" \ | |
| --data "$payload") | |
| echo "Respuesta IndexNow: HTTP ${http_code}" | |
| echo "Cuerpo: $(cat indexnow_resp.txt 2>/dev/null)" | |
| # IndexNow responde 200 (OK) o 202 (recibido, validación en curso). | |
| # Cualquier otro código se trata como fallo VISIBLE (el deploy ya se | |
| # publicó en un job aparte, así que esto no revierte nada). | |
| case "$http_code" in | |
| 200|202) echo "✓ IndexNow aceptó la notificación de ${count} URLs." ;; | |
| *) echo "::error::IndexNow respondió HTTP ${http_code}."; exit 1 ;; | |
| esac |