Desarrollo Web 5-8 minutos

CI/CD para Laravel con GitHub Actions: tests, Pint y despliegue automático

Diego Cortés
Diego Cortés
Full Stack Developer & SEO Specialist
Compartir:
CI/CD para Laravel con GitHub Actions: tests, Pint y despliegue automático

Cada git push puede convertirse en una mini-entrega: tests, análisis de calidad y un despliegue a producción sin que nadie abra un terminal. GitHub Actions para Laravel es el estándar de facto del CI/CD en proyectos PHP en 2026, y este tutorial monta el pipeline completo paso a paso.

Qué es un pipeline de CI/CD y por qué tu proyecto Laravel lo necesita

Un pipeline de integración y despliegue continuos encadena tareas automáticas que se ejecutan en una máquina limpia cada vez que el repositorio recibe código nuevo: instalar dependencias, ejecutar tests, revisar el estilo, comprobar tipos y compilar assets. Si todo es verde, se despliega. El beneficio no es solo ahorrar tiempo: es que el código que llega a producción ya ha pasado por las mismas comprobaciones en cada commit, lo que elimina el clásico "en mi máquina funciona".

GitHub Actions tiene una ventaja adicional frente a servicios de CI externos: los workflows viven dentro del propio repositorio, se versionan con el código y el Marketplace ofrece acciones listas para cada etapa del pipeline. Para un proyecto Laravel 13 típico, el flujo completo se puede montar en un solo archivo YAML.

El esqueleto del workflow: on, jobs y steps

Un workflow se define en .github/workflows/ci.yml y tiene tres niveles: on declara los eventos que lo disparan, jobs agrupa trabajos independientes que corren en paralelo, y cada job contiene una lista de steps que se ejecutan en orden dentro de un runner. Los runners de GitHub son máquinas efímeras con Linux, macOS o Windows; para Laravel, ubuntu-latest cubre el 99% de los casos.

Disparadores: push y pull_request

El disparador más común combina push y pull_request: así el pipeline corre tanto cuando se sube código a la rama principal como cuando alguien abre una pull request, que es justo donde más valor tiene detectar un test roto antes de fusionar. Se puede afinar con ramas concretas para no gastar minutos en ramas de trabajo:

name: CI
on:
  push:
    branches: [main]
  pull_request:

Instalar PHP con shivammathur/setup-php

La acción shivammathur/setup-php es el estándar para preparar PHP en GitHub Actions. Soporta múltiples plataformas, cualquier versión de PHP publicada y configuración fina del entorno: extensiones, opciones de php.ini, cobertura de código y herramientas como Composer o Xdebug. Para Laravel, las extensiones que casi siempre hacen falta son mbstring, intl, pdo_mysql y, si usas colas con Redis, redis.

Extensiones, php.ini y cobertura de código

Todo se declara en el bloque with de la acción. Por ejemplo, para activar Xdebug solo cuando el job necesita cobertura se puede usar coverage: xdebug, y opciones como ini-values permiten ajustar memory_limit sin tocar nada local. El resultado es un runner con un PHP prácticamente idéntico al de tu entorno de producción.

Composer con caché: de minutos a segundos

El paso que más tarda en cualquier pipeline PHP es composer install, porque descarga cientos de paquetes de Packagist en cada ejecución. La solución es cachear la carpeta vendor entre ejecuciones: GitHub Actions guarda los archivos en su caché de 10 GB por repositorio y los restaura si la clave coincide.

Cachear vendor contra composer.lock con actions/cache

La clave se construye con el hash de composer.lock:

- uses: actions/cache@v4
  with:
    path: vendor
    key: composer-${{ hashFiles('composer.lock') }}

Mientras el lockfile no cambie, composer install restaura vendor desde la caché y no descarga nada de la red. En mediciones reales de 2026, cachear dependencias redujo un workflow de PHP de 30 a 20 segundos en un proyecto pequeño, y en pipelines más pesados el ahorro medio rondó los 4,5 minutos por ejecución. Merece la pena también cachear la caché interna de Composer con la misma estrategia.

Calidad en el pipeline: Pint, Larastan y tests

El corazón del pipeline son las comprobaciones de calidad. Laravel Pint corrige el estilo de código siguiendo el preset oficial del framework, y Larastan (PHPStan sobre Laravel) detecta errores de tipos y usos incorrectos del contenedor que los tests no ven. Ambos se ejecutan en modo estricto: vendor/bin/pint --test falla si hay algo sin formatear, y vendor/bin/phpstan analyse falla con cualquier nivel de error.

Pest y PHPUnit: php artisan test

Los tests se lanzan con php artisan test, que funciona igual con Pest que con PHPUnit. Lo importante es preparar la base de datos antes: migrar y sembrar en cada ejecución garantiza que los tests corren siempre sobre un estado conocido. Si usas parallel testing de Pest, el pipeline puede dividir los tests entre varios procesos para recortar el tiempo total.

MySQL y Redis como servicios de contenedor

No hace falta una base de datos externa: GitHub Actions permite levantar servicios como contenedores dentro del propio job con la clave services. Un MySQL 8.0 con health check, expuesto en el puerto 3306, y un Redis en el 6379 bastan para que los tests usen las mismas tecnologías que producción:

services:
  mysql:
    image: mysql:8.0
    env:
      MYSQL_DATABASE: testing
      MYSQL_ALLOW_EMPTY_PASSWORD: yes
    ports: ['3306:3306']
    options: >-
      --health-cmd="mysqladmin ping"
      --health-interval=10s
      --health-timeout=5s
      --health-retries=5

Compilar assets: Node y Vite en el workflow

Laravel 13 usa Vite para compilar JavaScript y CSS, así que el pipeline necesita un job con Node. El patrón típico es actions/setup-node con caché de npm, seguido de npm ci y npm run build. Es buena práctica subir los assets compilados como artefacto (actions/upload-artifact) para que el job de despliegue los descargue y no haya que recompilar en producción.

Desplegar: Forge, Deployer o SSH

Cuando tests y build pasan, llega el despliegue. Hay tres rutas habituales. Laravel Forge expone un endpoint de despliegue que se puede invocar con su CLI usando un token; Deployer define los servidores en un archivo deploy.php y ejecuta tareas vía SSH; y para setups simples, un git pull + php artisan migrate --force por SSH directo funciona igual de bien. En los tres casos, el job de deploy solo debe correr en la rama principal y tras un push, no en pull requests:

deploy:
  needs: [tests, build]
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: forge deploy
      env:
        FORGE_API_TOKEN: ${{ secrets.FORGE_API_TOKEN }}

Secrets: claves de producción sin exponerlas

Las credenciales se guardan en Settings > Secrets and variables del repositorio y se inyectan con ${{ secrets.NOMBRE }}. Nunca se escriben en el YAML ni en el historial: GitHub las oculta en los logs y las hace inaccesibles desde pull requests de forks, que es el vector de ataque más común en CI.

Matrix de PHP: probar varias versiones a la vez

Con strategy.matrix el mismo job se ejecuta con varias configuraciones en paralelo. Declarar una matriz con PHP 8.3, 8.4 y 8.6 te avisa al instante si una actualización de dependencias rompe la compatibilidad con alguna versión, algo que en local casi nadie comprueba. El coste es más minutos de CI, así que muchos proyectos limitan la matriz a los tests y dejan Pint y Larastan en una sola versión.

Límites del plan gratuito y optimización

GitHub Actions es gratuito para repositorios públicos, con minutos ilimitados. En repositorios privados, el plan gratuito incluye una cuota mensual de minutos y almacenamiento de artefactos y caché, ampliable de pago. Para no gastarla: cachea Composer y npm, evita correr el pipeline en cada rama de trabajo, usa paths para saltarte jobs cuando solo cambia documentación y sube la imagen base del runner cuando los tiempos empiecen a notarse.

Conclusión

Con un solo archivo YAML, tu proyecto Laravel pasa de depender de la memoria de nadie a tener un flujo reproducible: PHP con extensiones, Composer con caché, Pint, Larastan, tests sobre MySQL real y despliegue automático a producción. El primer pipeline cuesta una mañana, y desde entonces cada push entrega con la misma garantía. Si quieres seguir profundizando en el ecosistema, el blog tiene guías de Pest 3, de las novedades de Laravel 13 y de desarrollo local con Laravel Sail que encajan con este flujo.

Categorías