🎨 Sysprovider Code
Sysprovider LogoWiki
🇪🇸Hosting español para ecommerce

Automatización de CI/CD para WordPress con GitHub Actions y Deployments

Actualizado el 4 de octubre de 2025

Imagina que lanzas una actualización crítica de seguridad en tu sitio WordPress y, en lugar de hacerlo manualmente a las 3 a.m., un flujo automatizado lo despliega en segundos con solo hacer git push. Eso es el CI/CD WordPress. La integración continua (CI) y el despliegue continuo (CD) son la columna vertebral de cualquier flujo de trabajo moderno de WordPress DevOps.

Tradicionalmente, actualizar un WordPress implicaba FTP, plugins de terceros o scripts caseros frágiles. Hoy, con GitHub Actions, puedes construir pipelines robustos que comprueben la calidad del código, ejecuten tests, construyan assets y desplieguen tu sitio en producción o staging sin intervención manual. Este artículo te guiará paso a paso para implementar un sistema de deployment automatizado que transformará tu forma de trabajar.

¿Por qué Automatizar el CI/CD en WordPress?

WordPress no es solo un CMS; es un ecosistema complejo con temas, plugins, archivos de configuración (wp-config.php) y una base de datos. Automatizar su ciclo de vida aporta beneficios concretos:

  • Elimina el error humano: Olvídate de subir archivos incorrectos o perder cambios.
  • Velocidad de entrega: Cada commit puede convertirse en un deploy en minutos.
  • Calidad asegurada: Ejecuta linters (PHPCS, ESLint) y tests unitarios antes de tocar producción.
  • Rollback sencillo: Si algo falla, revertir es tan simple como restaurar un commit en Git.
  • Entornos consistentes: Desarrollo, staging y producción se mantienen sincronizados.

[INFO] El CI/CD no reemplaza los backups de base de datos. El pipeline se enfoca en archivos de código (temas, plugins, mu-plugins). La base de datos debe gestionarse con herramientas como WP-CLI o migraciones de plugins especializados.

Conceptos Clave Antes de Empezar

Antes de escribir el primer workflow, necesitas tener claros estos conceptos:

GitHub Actions

Es la plataforma de CI/CD integrada en GitHub. Se basa en workflows (archivos YAML dentro de .github/workflows/) que se activan por eventos (push, pull request, schedule).

Artefactos y Secrets

  • Artefactos: Archivos generados durante el pipeline (por ejemplo, un tema compilado con Webpack).
  • Secrets: Variables de entorno cifradas (claves SSH, tokens de API, credenciales de servidor). Nunca las pongas en el código.

Estrategias de Deploy para WordPress

No todas las estrategias sirven para WordPress. Las más comunes son:

  1. Deploy por Git (Git push): Ideal si el servidor tiene Git instalado. Usas un hook post-receive.
  2. Deploy via SFTP/SSH: El pipeline se conecta al servidor y sube los archivos mediante rsync o scp.
  3. Deploy a través de APIs: Si usas plataformas como Kinsta, WP Engine o Pantheon, tienen APIs específicas.
  4. Deploy con Docker: Más avanzado, para entornos containerizados.

Para este artículo, nos centraremos en la opción más universal y segura: deploy mediante SSH + rsync.

Configurando el Entorno de WordPress DevOps

Tu repositorio debe estar bien estructurado. Un ejemplo de estructura para un tema personalizado:

/
├── .github/
│   └── workflows/
│       └── deploy.yml
├── wp-content/
│   ├── themes/
│   │   └── mi-tema/
│   │       ├── assets/
│   │       ├── src/
│   │       ├── package.json
│   │       └── style.css
│   └── plugins/
│       └── mi-plugin/
└── .gitignore

[TIP] Mantén tu WordPress core fuera del repositorio. Solo versiona tu wp-content (temas, plugins, uploads si es necesario) y archivos de configuración como wp-config.php (sin credenciales reales).

Preparando el Servidor

  1. Crea un usuario SSH dedicado (ej: github-actions) con permisos solo sobre la carpeta wp-content.
  2. Genera un par de llaves SSH (sin passphrase) en tu máquina local: ssh-keygen -t ed25519 -C "github-actions-wordpress".
  3. Agrega la llave pública al ~/.ssh/authorized_keys del usuario en el servidor.
  4. Guarda la llave privada como un secret en GitHub: Settings > Secrets and variables > Actions > New repository secret. Nómbrala SSH_PRIVATE_KEY.

También necesitarás otros secrets:

  • SSH_HOST (IP o dominio del servidor)
  • SSH_USER (el usuario creado)
  • SSH_PORT (normalmente 22)
  • DEPLOY_PATH (ruta absoluta a wp-content en el servidor, ej: /var/www/mi-sitio/wp-content)

Creando el Workflow de Integración Continua (CI)

El primer paso es la integración continua. Se ejecutará en cada push a la rama develop o en cada Pull Request.

Crea el archivo .github/workflows/ci.yml:

name: CI WordPress

on:
  push:
    branches: [ develop ]
  pull_request:
    branches: [ develop, main ]

jobs:
  quality-check:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: wp-content/themes/mi-tema

    steps:
      - name: Checkout del código
        uses: actions/checkout@v4

      - name: Configurar PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          tools: composer, wp-cli

      - name: Configurar Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: wp-content/themes/mi-tema/package-lock.json

      - name: Instalar dependencias
        run: |
          npm ci
          composer install --no-dev --optimize-autoloader

      - name: Lint PHP (PHPCS)
        run: |
          vendor/bin/phpcs --standard=WordPress .

      - name: Lint JavaScript (ESLint)
        run: |
          npx eslint assets/js/

      - name: Compilar assets
        run: |
          npm run build

      - name: Tests unitarios (si aplica)
        run: |
          # Aquí irían tests con PHPUnit o Jest
          echo "Tests ejecutados correctamente"

Este workflow asegura que el código cumple con los estándares de WordPress antes de ser desplegado. Si falla, el proceso se detiene.

Construyendo el Pipeline de Deploy (CD)

Ahora, el despliegue continuo. Cuando se fusione un PR a main (producción) o a develop (staging), se ejecutará el deploy.

Crea el archivo .github/workflows/deploy.yml:

name: Deploy WordPress a Producción

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout del código
        uses: actions/checkout@v4

      - name: Configurar Node.js y compilar assets
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: wp-content/themes/mi-tema/package-lock.json

      - name: Instalar y compilar tema
        working-directory: wp-content/themes/mi-tema
        run: |
          npm ci
          npm run build
          # Eliminar node_modules para no subirlos
          rm -rf node_modules

      - name: Configurar SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts

      - name: Sincronizar archivos con rsync (modo seco primero)
        run: |
          rsync -avz --dry-run \
            -e "ssh -p ${{ secrets.SSH_PORT }}" \
            --exclude 'node_modules' \
            --exclude '.git' \
            --exclude '.github' \
            --delete \
            ./wp-content/ \
            ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DEPLOY_PATH }}/

      - name: Deploy real
        run: |
          rsync -avz \
            -e "ssh -p ${{ secrets.SSH_PORT }}" \
            --exclude 'node_modules' \
            --exclude '.git' \
            --exclude '.github' \
            --delete \
            ./wp-content/ \
            ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DEPLOY_PATH }}/

      - name: Limpiar cachés en servidor (post-deploy)
        run: |
          ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "cd ${{ secrets.DEPLOY_PATH }} && wp cache flush --allow-root || echo 'WP-CLI no disponible'"

[WARNING] El flag --delete de rsync eliminará archivos en el servidor que no existan en el repositorio. Úsalo con cuidado. Siempre ejecuta primero un --dry-run para verificar qué se va a borrar.

Estrategias Avanzadas de Deployment Automatizado

Despliegues por Etapas (Staging + Producción)

Puedes tener dos workflows diferentes o uno solo con condicionales. Por ejemplo:

on:
  push:
    branches:
      - develop
      - main

jobs:
  deploy:
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    steps:
      - name: Determinar entorno
        id: env
        run: |
          if [ "${{ github.ref }}" == "refs/heads/main" ]; then
            echo "ENV=production" >> $GITHUB_OUTPUT
          else
            echo "ENV=staging" >> $GITHUB_OUTPUT
          fi
      # ... usar secrets diferentes según el entorno

Deploy con Artefactos y Liberación (Release)

Para proyectos más grandes, puedes generar un artefacto comprimido (zip del tema o plugin) y subirlo a GitHub Releases. Luego, otro workflow se encarga de descargar la última release y desplegarla.

# En el workflow de CI
- name: Subir artefacto
  uses: actions/upload-artifact@v4
  with:
    name: mi-tema-build
    path: wp-content/themes/mi-tema/dist/

Notificaciones Post-Deploy

Integra Slack, Discord o correo para saber si el deploy fue exitoso.

- name: Notificar éxito
  if: success()
  uses: slackapi/slack-github-action@v1.24.0
  with:
    payload: |
      {
        "text": "✅ Deploy exitoso de mi-tema a producción"
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Buenas Prácticas y Seguridad en WordPress DevOps

Gestión de Secrets

  • Nunca expongas wp-config.php con credenciales reales en el repositorio. Usa variables de entorno en el servidor o secrets de GitHub.
  • Rota las llaves SSH periódicamente.

Pruebas en Entorno Aislado

Antes de hacer deploy a producción, ejecuta el mismo workflow contra un entorno de staging. GitHub Actions permite usar matrices para probar contra múltiples versiones de PHP o WordPress.

strategy:
  matrix:
    php: [7.4, 8.0, 8.2]
    wordpress: [6.0, 6.4]

Monitoreo Post-Deploy

Agrega un paso que verifique que el sitio responde con HTTP 200 después del deploy:

- name: Health check
  run: |
    curl -sSf -o /dev/null -w "%{http_code}" https://tusitio.com/wp-content/themes/mi-tema/style.css

Solución de Problemas Comunes

Error: Permission denied (publickey)

  • Verifica que la llave privada en GitHub Secrets esté completa (incluye -----BEGIN OPENSSH PRIVATE KEY-----).
  • Asegúrate de que la llave pública esté en authorized_keys del usuario correcto en el servidor.

rsync no encuentra archivos

  • Revisa las rutas relativas. En el workflow, ./wp-content/ es relativo al directorio raíz del repositorio.
  • Usa ls -la en un paso de depuración para verificar la estructura.

El deploy es lento

  • Excluye más carpetas: --exclude 'uploads' (si las imágenes se manejan aparte).
  • Usa --compress en rsync.
  • Considera usar un mirror o CDN para assets estáticos.

Conclusión: El Futuro de WordPress es DevOps

Implementar CI/CD WordPress con GitHub Actions no es un lujo, es una necesidad para cualquier proyecto que busque escalar con calidad. La integración continua te protege de errores tontos, y el deployment automatizado te libera de tareas repetitivas.

Empieza con un pipeline simple para un tema y ve añadiendo complejidad: tests de regresión visual, migraciones de base de datos con WP-CLI, o incluso despliegues blue-green. La inversión inicial en configurar estos workflows se amortiza la primera vez que un deploy nocturno falla y te despiertas con el sitio intacto.

[TIP] No intentes abarcar todo de golpe. Automatiza primero lo que más dolor te cause: el deploy manual. Luego añade los linters. Y finalmente, los tests. El WordPress DevOps es un viaje, no un destino.

Tu flujo de trabajo ha cambiado para siempre. Ahora, cada git push es una oportunidad para mejorar, no un riesgo.

¿Necesitas ayuda?Son dos de nuestros técnicos, Agustín y Mikel, y están disponibles para resolver cualquier problema.

Hablar con ellos ahora
Agustín y Mikel