CI/CD: pipeline de despliegue con GitHub Actions
Convertir un git push en un despliegue reproducible con GitHub Actions: las cuatro etapas del pipeline install, test, build y deploy, cómo se declara un workflow en YAML, el cacheado de dependencias para acelerar cada corrida y las previews por pull request que dan una URL a cada cambio antes de fusionarlo.
Desplegar a mano funciona una vez y falla la segunda: olvidas un paso, construyes desde una rama sucia, subes con tu versión de Node y no la del equipo. Un pipeline de integración y despliegue continuos elimina esa fragilidad convirtiendo el despliegue en un proceso automático, reproducible y auditado —cada git push dispara la misma secuencia de pasos en un entorno limpio, y solo llega a producción lo que pasa las comprobaciones—. GitHub Actions es la forma más directa de montarlo sin salir de tu repositorio: un fichero YAML describe las cuatro etapas canónicas —instalar, probar, construir, desplegar— y la plataforma las ejecuta por ti en cada cambio. Bien hecho, el pipeline no solo automatiza: es la barrera que impide que un error humano llegue a la web.
- Comprender las cuatro etapas del pipeline: install, test, build y deploy.
- Declarar un workflow de GitHub Actions en YAML para un proyecto Astro.
- Cachear las dependencias para acortar el tiempo de cada corrida.
- Generar previews por pull request que den una URL a cada cambio.
Las cuatro etapas del pipeline
Todo despliegue automatizado, por complejo que parezca, se descompone en la misma secuencia de cuatro etapas. Entenderlas como pasos con un orden fijo —cada uno depende del anterior— es la clave para leer y escribir cualquier pipeline.
Install
Instala dependencias de forma reproducible con npm ci, partiendo del lockfile, en un entorno recién creado y vacío.
Test
Ejecuta linters, comprobación de tipos y pruebas. Es la barrera: si algo falla aquí, el pipeline se detiene y no despliega.
Build
Corre astro build y produce dist/. Solo se construye lo que ya pasó las pruebas, sobre dependencias limpias.
Deploy
Publica el artefacto en la plataforma de destino. Ocurre únicamente si las tres etapas anteriores terminaron en verde.
El orden no es decorativo: es una cadena de confianza. Se instala antes de probar porque las pruebas necesitan el árbol de dependencias; se prueba antes de construir porque no tiene sentido gastar tiempo compilando código roto; se construye antes de desplegar porque solo se publica lo que existe y funciona. Cada etapa es una compuerta, y la última —deploy— solo se abre si todas las anteriores dieron luz verde. Ese encadenamiento es lo que convierte un montón de comandos en una garantía.
Vale la pena notar que estas cuatro etapas son las mismas que ya ejecutas a mano en tu máquina —instalas, pruebas, construyes, subes—. El pipeline no inventa pasos nuevos: toma el ritual que ya conoces y lo mueve a un entorno limpio y automático donde nadie puede saltarse ninguno ni ejecutarlos en el orden equivocado. Por eso migrar a CI casi nunca exige reescribir nada; exige, más bien, escribir explícitamente lo que antes vivía en tu memoria y en tu costumbre.
Puesto en negativo, cada compuerta existe para atrapar un tipo concreto de error antes de que llegue a producción:
- Install atrapa el árbol de dependencias roto: un paquete que falta o una versión que no resuelve.
- Test atrapa la regresión: el cambio que rompe algo que antes funcionaba, sin que nadie lo note al escribirlo.
- Build atrapa el fallo de compilación: un import muerto, un tipo inválido, un schema de contenido que no valida.
- Deploy no atrapa, veda: si cualquier etapa anterior falla, la publicación sencillamente no ocurre.
Un workflow de GitHub Actions
En GitHub Actions, el pipeline vive en un fichero YAML dentro de .github/workflows. Su estructura es una jerarquía sencilla: un disparador que dice cuándo corre, y una lista de pasos que dicen qué hace.
name: deploy
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run test --if-present
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist
Léelo de arriba abajo y verás las cuatro etapas. El bloque on define los disparadores: cada push a main y cada pull request. Dentro del job, checkout trae el código y setup-node prepara el entorno; luego vienen las etapas en orden —npm ci instala, npm run test prueba, npm run build construye— y el último paso guarda dist/ como artefacto. El detalle de --if-present es cortesía profesional: no rompe el pipeline si aún no has escrito pruebas, pero deja el hueco listo para cuando las tengas. Fíjate en que cada uses apunta a una versión fija —@v4—: como con las dependencias, fijar la versión de las acciones es lo que hace el pipeline reproducible en el tiempo.
La anatomía del fichero se reduce a un puñado de piezas que se repiten en cualquier workflow:
on: los eventos que disparan la corrida —push a una rama, pull request, o una programación horaria—.jobs: los trabajos, que corren en paralelo salvo que declares dependencias entre ellos.runs-on: la máquina virtual efímera donde todo ocurre, creada de cero en cada corrida.steps: los pasos en orden, dondeusesinvoca una acción reutilizable yrunejecuta un comando de shell.
La virtud central de CI —y lo que lo diferencia de desplegar desde tu portátil— es que cada corrida arranca en una máquina recién creada, sin rastro de la anterior: sin caché sucio, sin variables residuales, sin ese paquete que instalaste una vez a mano y olvidaste. Esa pureza es incómoda al principio, porque destapa dependencias implícitas de tu entorno local, pero es exactamente lo que quieres: si el build pasa en ese vacío, pasará en cualquier sitio. El entorno limpio no es una molestia, es la prueba de que tu despliegue no depende de secretos guardados en tu máquina.
Cache de dependencias y previews por PR
Un pipeline correcto puede ser lento, y la lentitud tiene un coste: cada minuto de espera es fricción que desincentiva desplegar a menudo. Dos técnicas lo transforman de peaje a fluido.
La primera es el cacheado de dependencias. Instalar desde cero en cada corrida descarga los mismos paquetes una y otra vez; con cache: npm en setup-node, GitHub guarda el almacén de descargas entre corridas y lo restaura, de modo que npm ci reutiliza lo que ya existía en lugar de bajarlo de internet. En un proyecto Astro con muchas dependencias, esto recorta una fracción notable del tiempo total sin ningún riesgo, porque el lockfile sigue siendo la fuente de verdad.
Lo elegante del cacheado por lockfile es que la invalidación es automática y correcta. La caché se identifica por un hash de tu package-lock.json: mientras el lockfile no cambie, se reutiliza; en cuanto añades o actualizas una dependencia, el hash cambia y la caché se reconstruye sola. No hay que acordarse de vaciarla ni riesgo de arrastrar paquetes viejos, porque la fuente de verdad de qué instalar y la fuente de verdad de qué cachear son el mismo fichero. Es la clase de mecanismo que funciona sin que tengas que pensar en él.
La segunda son las previews por pull request. En lugar de fusionar a ciegas y ver el resultado en producción, el pipeline despliega cada PR a una URL temporal y aislada. Revisas el cambio funcionando, no solo el diff; el equipo lo prueba en vivo; y solo cuando convence se fusiona. Las plataformas Git-driven ofrecen estas previews casi de fábrica, y son la diferencia entre revisar código y revisar la experiencia real.
La preview cambia la naturaleza de la revisión. Un diff te dice qué líneas cambiaron; una preview te dice qué le pasa al usuario. Un cambio de estilos, una animación, un ajuste de maquetación son casi imposibles de juzgar leyendo código, pero obvios de aprobar o rechazar en una URL viva. Cuando cada PR trae su propio sitio desplegado, la conversación deja de ser sobre la implementación y pasa a ser sobre el resultado, que es donde de verdad se decide si un cambio entra.
# El disparo por pull_request es lo que habilita las previews:
on:
pull_request:
# cada PR corre el pipeline y la plataforma publica una URL temporal
Reunidas, cache y previews cambian la economía del despliegue. La cache recorta el tiempo de cada corrida, así que publicar deja de ser una espera; las previews eliminan el riesgo de revisar a ciegas, así que fusionar deja de dar miedo. Un pipeline lento y opaco desincentiva desplegar a menudo, y un equipo que despliega poco acumula cambios grandes y arriesgados. Uno rápido y transparente invita a lo contrario: cambios pequeños, frecuentes y fáciles de revertir, que es la forma más segura de hacer evolucionar un sitio en producción.
Es tentador tratar el fichero de workflow como un script desechable, pero es infraestructura crítica: define cómo llega tu código a producción. Merece revisión en pull request, control de versiones y cambios pequeños y probados, igual que el resto del proyecto. Un pipeline que nadie revisa y que solo una persona entiende es un punto único de fallo tan grave como un servidor sin respaldo. Trátalo como lo que es —el guardián de tu despliegue— y tendrás un equipo que despliega con calma en lugar de con miedo.
flowchart LR PUSH[push o pull request] --> INS[install con cache de npm] INS --> TEST[test lint y tipos] TEST --> BLD[astro build] BLD --> DEP[deploy] DEP --> PROD[produccion en main] DEP --> PREV[preview por pull request] style TEST fill:#89b4fa,color:#11111b style PROD fill:#a6e3a1,color:#11111b
La razón profunda de automatizar el despliegue no es la comodidad de no teclear comandos: es sacar la fiabilidad de las manos de las personas y meterla en la estructura del sistema. Un equipo que despliega a mano confía en que cada persona recuerde cada paso, use la rama correcta, corra las pruebas y no suba desde un entorno contaminado —confía, en suma, en la vigilancia humana constante, que es la cosa menos fiable del mundo—. Un pipeline invierte esa carga: la disciplina deja de ser una virtud que hay que ejercer cada vez y se convierte en una propiedad que el sistema garantiza siempre. Las pruebas no se corren cuando alguien se acuerda, se corren en cada push porque la máquina no sabe olvidar; el entorno no está limpio porque alguien lo limpió, sino porque nace nuevo en cada corrida; solo llega a producción lo que pasó las compuertas, no porque nadie se saltó una, sino porque saltárselas es imposible. Esa es la transformación silenciosa que hace CI/CD: mueve la calidad del terreno de la intención al terreno de la estructura. Y una vez ahí, escala sin desgaste —da igual que despliegues tres veces al año o treinta al día, que el equipo sea de uno o de cincuenta— porque la garantía no depende de la memoria de nadie. El código que describe tu pipeline es, en el fondo, la constitución de tu proyecto: las reglas que nadie puede saltarse, escritas una vez y aplicadas por igual a todos. Automatizar el despliegue es escribir esas reglas y, con ellas, dejar de rezar para empezar a confiar.
- Crea un fichero en
.github/workflowscon las etapas install, test y build sobreubuntu-latesty Node 22. - Activa
cache: npmensetup-nodey compara el tiempo de dos corridas: la primera y la segunda. - Añade el disparador
pull_requesty abre un PR para ver el pipeline correr sobre el cambio antes de fusionarlo. - Rompe una prueba a propósito y confirma que la etapa de deploy no llega a ejecutarse.