Back to Entradas
Despliegues seguros en WordPress con GitHub Actions
Posted by: rt Category: Blog Comments: 0

Cuando tu sitio en WordPress/WooCommerce es crítico para ventas y captación, actualizar “a mano” es arriesgado. Con CI/CD en GitHub Actions puedes probar, construir y desplegar cambios de forma atómica y sin downtime, con rollback en un clic.

¿Qué resuelve un pipeline CI/CD?

  • Zero downtime: el nuevo release se prepara en segundo plano y se activa cambiando un symlink (current → releases/2025…).

  • Calidad: linters, tests y checks antes de tocar producción.

  • Seguridad: claves en GitHub Secrets; acceso al servidor por SSH key de solo despliegue.

  • Trazabilidad: cada deploy queda versionado y con logs.

Arquitectura de despliegue (recomendada)

/var/www/mi-sitio/
├─ shared/ # persistente (uploads, cache, claves)
│ └─ wp-content/uploads → se comparte entre releases
├─ releases/
│ ├─ 2025-09-01_12-00-03/ # release generado por el pipeline
│ └─ 2025-08-28_19-44-10/
└─ current → releases/2025-09-01_12-00-03/ # symlink activo

 

En cada release, enlaza wp-content/uploads a ../shared/uploads y cualquier otro recurso persistente (cache, claves locales, etc.).

Ejemplo de workflow (GitHub Actions)

Archivo: .github/workflows/deploy.yml
Deploy por push a main o manual (workflow_dispatch). Ajusta rutas/usuarios y añade tus pasos de build de tema/plug-ins.

name: Deploy WordPress (Zero-Downtime)

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

jobs:
  build-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.2"
          tools: composer, phpstan, phpcs

      - name: Install Composer deps (no-dev in prod)
        run: composer install --no-interaction --prefer-dist

      - name: Lint PHP
        run: find . -type f -name "*.php" -not -path "./vendor/*" -print0 | xargs -0 -n 1 -P 4 php -l

      - name: WordPress Coding Standards (PHPCS)
        run: phpcs --standard=WordPress --ignore=vendor,node_modules .

      - name: Build assets (theme/plugins)
        run: |
          if [ -f package.json ]; then npm ci && npm run build; fi

      - name: Archive artifact
        run: |
          mkdir -p artifact
          rsync -a --delete --exclude '.git' --exclude 'node_modules' --exclude '.github' ./ artifact/app/
          tar -czf artifact.tar.gz -C artifact app
        shell: bash

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: site-artifact
          path: artifact.tar.gz

  deploy:
    needs: build-test
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://tusitio.com
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: site-artifact
          path: .

      - name: Prepare release name
        id: rel
        run: echo "REL=releases/$(date +'%Y-%m-%d_%H-%M-%S')" >> $GITHUB_OUTPUT

      - name: Upload & Extract via SSH
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            set -e
            BASE=/var/www/tusitio
            REL=${{ steps.rel.outputs.REL }}
            mkdir -p "$BASE/$REL" "$BASE/shared/uploads"
            tar -xzf site-artifact.tar.gz -C "$BASE/$REL"
            ln -sfn "$BASE/shared/uploads" "$BASE/$REL/app/wp-content/uploads"

      - name: Health check (pre-switch)
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            curl -sSf https://tusitio.com | head -n 5 > /dev/null

      - name: Atomic switch (no downtime)
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            set -e
            BASE=/var/www/tusitio
            REL=${{ steps.rel.outputs.REL }}
            ln -sfn "$BASE/$REL/app" "$BASE/current"

      - name: Post-deploy (cache & checks)
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            set -e
            cd /var/www/tusitio/current
            if command -v wp >/dev/null 2>&1; then
              wp cache flush --allow-root || true
              wp rewrite flush --hard --allow-root || true
            fi
            # Purga LiteSpeed/Cloudflare si aplica (curl a endpoint o WP-CLI plugin)
            # Mantén 5 releases
            cd /var/www/tusitio/releases && ls -1t | tail -n +6 | xargs -r rm -rf

Rollback en 10 segundos

  • Lista releases (ls -1t /var/www/tusitio/releases) y apunta el anterior.

  • Cambia symlink: ln -sfn /var/www/tusitio/releases/FECHA_HORA/app /var/www/tusitio/current.

  • (Opcional) Purga caché y verifica health check.

Seguridad y buenas prácticas

  • Secrets en GitHub (host, user, key, port).

  • Usuario SSH solo para deploy y sin shell interactivo si es posible.

  • WAF/CDN activado (Cloudflare/QUIC.cloud).

  • Backups automáticos previos al switch.

  • Presupuestos de rendimiento: falla el build si LCP/INP excede tu umbral.

Checklist express

  1. Repositorio limpio (sin wp-content/uploads).

  2. Workflows: build-test y deploy con approvals.

  3. shared/uploads enlazado por symlink.

  4. Health checks antes y después del switch.

  5. Rollback documentado.

  6. Purga de caché/OPcache.

  7. Mantener 5 releases.

  8. Logs centralizados del deploy.

Back to Entradas

¡Ponte en contacto!

No dudes en escribirnos
WhastApp