CI/CD en Drupal: Automatizar Pruebas y Despliegues
Cómo automatizar pruebas y despliegues en Drupal con CI/CD para equipos de desarrollo
Todos lo hemos hecho alguna vez. SSH al servidor, git pull, drush cr, y rezar para que funcione. El clásico deploy artesanal que funciona... hasta que un viernes a las siete de la tarde deja de funcionar y te arruina el fin de semana.
He perdido la cuenta de las veces que he visto equipos Drupal perfectamente competentes romper producción porque alguien olvidó ejecutar drush updb después de un pull, o importó la configuración en un orden que dejó el sitio en un estado zombi: técnicamente levantado, funcionalmente muerto.
La integración continua y el despliegue continuo (CI/CD) no son un lujo reservado a equipos de veinte personas con un DevOps dedicado. Son la mínima higiene que necesitas para que desplegar un proyecto Drupal no sea un acto de fe. Y con las herramientas que existen hoy, montar un pipeline funcional es cuestión de una tarde, no de un sprint entero.
Por qué Drupal necesita CI/CD más que otros CMS
Mira, WordPress lo subes por FTP y más o menos tira. Drupal no perdona. Tiene particularidades que hacen la automatización de despliegues prácticamente obligatoria:
Configuración como código. Desde Drupal 8, la configuración del sitio (tipos de contenido, vistas, permisos, formularios) se exporta como archivos YAML. Esto es una ventaja brutal... siempre y cuando tu flujo de despliegue incluya la importación automática. Cuando los desarrolladores importan configuración a mano, pasan cosas: se olvidan cambios, importan en orden incorrecto, o —mi favorito— pisan la configuración que otro compañero acaba de subir.
Actualizaciones de base de datos. Cada actualización de módulo puede incluir update hooks que modifican el esquema de la base de datos. Ejecutar drush updb de forma manual introduce el riesgo de olvidarlo o ejecutarlo en el momento incorrecto. Y créeme, un update hook ejecutado a destiempo puede dejarte una tarde muy larga por delante.
Cache system complejo. Drupal tiene múltiples capas de cache que necesitan reconstruirse después de cada despliegue. Olvidar un drush cr después de importar configuración es la fuente número uno de bugs fantasma que hacen que cuestiones tu carrera profesional.
Dependencias PHP. Composer gestiona las dependencias del proyecto, incluyendo el core de Drupal, módulos contribuidos y librerías. Un despliegue que no ejecuta composer install puede dejar el sitio con dependencias desactualizadas o faltantes. Y luego llega el WSOD y nadie sabe por qué.
Un pipeline CI/CD bien montado ejecuta todos estos pasos en el orden correcto, cada vez, sin excepción. Sin depender de la memoria de nadie.
Anatomía de un pipeline CI/CD para Drupal
Un pipeline típico para un proyecto Drupal tiene estas fases, y cada una existe por una razón concreta:
Fase 1: Construcción (Build)
build:
- composer install --no-dev --optimize-autoloader
- npm install && npm run build # Si hay tema custom
Aquí se instalan las dependencias PHP con Composer y se construyen los assets del tema si usas un theme custom con Sass/PostCSS y JavaScript compilado. La flag --no-dev excluye las dependencias de desarrollo (PHPUnit, Behat, etc.) del artefacto de producción. No quieres PHPUnit en tu servidor de producción. Nunca.
Fase 2: Análisis estático (Lint & Standards)
lint:
- phpcs --standard=Drupal,DrupalPractice web/modules/custom
- phpstan analyse web/modules/custom --level=6
- eslint web/themes/custom/js/
PHPCS con los estándares Drupal y DrupalPractice verifica que el código sigue las convenciones de codificación de Drupal. PHPStan detecta errores de tipado y lógica sin ejecutar el código —te ahorra vergüenzas en producción—. ESLint hace lo equivalente para el JavaScript del tema.
Fase 3: Tests automatizados
test:
- phpunit --testsuite=unit
- phpunit --testsuite=kernel
- phpunit --testsuite=functional
Drupal soporta tres niveles de testing:
- Unit tests: prueban clases PHP aisladas sin arrancar Drupal. Son rápidos (milisegundos) y prueban lógica de negocio.
- Kernel tests: arrancan el kernel de Drupal con módulos específicos. Permiten probar servicios, plugins y lógica que depende de la base de datos.
- Functional tests: arrancan una instancia completa de Drupal con un navegador simulado. Prueban flujos completos como "un usuario anónimo puede registrarse y publicar un comentario".
Y no, no necesitas un 100 % de cobertura. Eso es un mito que paraliza equipos. Empieza por tests funcionales de los flujos críticos del negocio y añade unit tests para la lógica compleja. El 80/20 funciona perfectamente aquí.
Fase 4: Despliegue
deploy:
- drush deploy # Equivale a: updatedb + config:import + cache:rebuild
# O paso a paso:
- drush state:set system.maintenance_mode TRUE
- drush updatedb --yes
- drush config:import --yes
- drush cache:rebuild
- drush state:set system.maintenance_mode FALSE
El comando drush deploy (disponible desde Drush 10.3) encapsula la secuencia estándar de despliegue: ejecuta update hooks, importa configuración y reconstruye la cache. El modo mantenimiento evita que los usuarios vean errores durante el proceso. Yo prefiero la versión paso a paso porque te da más control sobre qué pasa si algo falla a mitad de camino.
Herramientas CI/CD para proyectos Drupal
Aquí va el repaso rápido de las opciones que he usado en proyectos reales:
GitHub Actions. La opción más accesible si tu código está en GitHub. Configuración como YAML en el repositorio, runners gratuitos para proyectos open source y runners privados para proyectos comerciales. La comunidad Drupal mantiene actions reutilizables para setup de PHP, Composer y Drupal. Para la mayoría de equipos, es la mejor relación esfuerzo/resultado.
GitLab CI. Si usas GitLab, su CI/CD integrado es potente y bien documentado. Los runners se pueden ejecutar en tu propia infraestructura, lo que es útil para tests que necesitan acceso a servicios internos. Personalmente, me gusta cómo gestiona los entornos de review.
Jenkins. El veterano. Más complejo de configurar pero flexible. Si ya lo tienes para otros proyectos, úsalo. Si no, hay opciones menos dolorosas.
Platform.sh y Acquia Cloud. Hosting especializado en Drupal con CI/CD integrado. Cada push crea un entorno de preview. La opción más rápida si no quieres gestionar infraestructura de CI/CD.
Ejemplo completo: pipeline con GitHub Actions
Un archivo .github/workflows/ci.yml funcional para un proyecto Drupal. Lo uso como base en prácticamente todos mis proyectos:
name: Drupal CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_DATABASE: drupal
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: gd, pdo_mysql, mbstring
- run: composer install
- run: phpcs --standard=Drupal web/modules/custom
- run: phpunit --testsuite=unit
- run: phpunit --testsuite=kernel
deploy:
needs: build-and-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
ssh deploy@server "cd /var/www/drupal && git pull && composer install --no-dev && drush deploy"
Este pipeline ejecuta análisis de código y tests en cada push y pull request, y despliega a producción automáticamente cuando se mergea a main. En un proyecto real, añadirías secrets para las credenciales SSH, notificaciones a Slack cuando algo pete y probablemente un paso de deploy a staging antes de producción.
Testing en Drupal: qué probar y cómo empezar
Si tu proyecto Drupal no tiene ni un solo test —y he visto proyectos con seis años en producción sin un test—, empezar de cero puede parecer una montaña. Pero no tienes que escalar todo de golpe. Estos son los puntos de entrada que más valor aportan con menos esfuerzo:
Tests funcionales de formularios críticos. Si tu sitio tiene un formulario de contacto, registro o checkout, escribe un test que envíe datos válidos y verifique que se procesan. Estos tests protegen los flujos que generan ingresos.
Tests kernel de módulos custom. Si has desarrollado módulos custom con lógica de negocio, prueba esa lógica con kernel tests. Un servicio que calcula precios, un plugin que transforma datos o un event subscriber que envía notificaciones son candidatos perfectos. Si escribiste código custom, merece un test.
Tests de regresión para bugs. Cada bug que corriges es una oportunidad de oro para escribir un test que lo prevenga en el futuro. El flujo es sencillo: el test reproduce el bug (falla), aplicas la corrección (pasa), y el test queda como guardia permanente. Nunca más el mismo bug dos veces.
Para escribir tests en Drupal, familiarízate con BrowserTestBase para tests funcionales y KernelTestBase para kernel tests. Los tests del core son la mejor referencia.
Gestión de entornos: desarrollo, staging y producción
Un pipeline CI/CD que funcione de verdad necesita al menos tres entornos. Menos de tres y estás jugando con fuego:
Desarrollo (local). Donde los desarrolladores trabajan en sus máquinas. DDEV es la herramienta estándar de facto para entornos locales de Drupal: un solo comando (ddev start) levanta PHP, MySQL, mailpit y un servidor web correctamente configurado para Drupal. Si tu equipo aún no usa DDEV, migrar es de las mejores inversiones de tiempo que podéis hacer.
Staging. Réplica de producción donde se validan los cambios. El pipeline despliega automáticamente a staging al mergear a develop. QA o el cliente validan antes de aprobar el paso a producción.
Producción. El entorno público. El despliegue a producción debe requerir aprobación manual (un botón en la interfaz de CI/CD) después de la validación en staging. Nunca despliegues automáticamente a producción en un viernes por la tarde. Nunca. En serio, no lo hagas.
Rollback: qué hacer cuando todo se va al traste
Incluso con el mejor pipeline del mundo, los despliegues pueden fallar. Murphy es así. Un plan de rollback claro y probado es tan necesario como el propio pipeline:
Rollback de código. Git te permite volver a cualquier commit anterior. Un git revert del merge commit deshace los cambios del código. Limpio y trazable.
Rollback de base de datos. Las migraciones de base de datos no siempre son reversibles. Si una actualización elimina una columna o modifica datos, necesitas un backup previo al despliegue para revertir. Tu pipeline debería crear un snapshot automático de la base de datos antes de cada despliegue a producción. Esto no es opcional.
Rollback de configuración. Si la importación de configuración causa problemas, puedes revertir exportando la configuración de producción anterior. Pero es más complejo que un rollback de código porque la configuración puede depender del esquema de base de datos. Aquí es donde las cosas se ponen interesantes —y no en el buen sentido—.
La mejor estrategia de rollback es no necesitarla: despliegues pequeños y frecuentes son más fáciles de debuggear y revertir que despliegues mastodónticos cada dos meses. Si tu deploy toca 47 archivos en 12 módulos, buena suerte encontrando qué rompió qué.
Deja de rezar, automatiza
Si tu equipo de desarrollo Drupal todavía despliega a mano y cada release es una sesión de estrés colectivo, montar CI/CD no es un "nice to have" —es lo que separa a los equipos que entregan software con confianza de los que cruzan los dedos cada jueves—. En Tangram Consulting llevamos años diseñando e implementando pipelines adaptados a la complejidad real de cada proyecto Drupal. Cuéntanos qué necesitas y te ayudamos a que desplegar deje de ser sinónimo de sufrir.