🔄 GitHub Actions: Reusable Workflows
Los Reusable Workflows (Workflows Reutilizables) de GitHub Actions permiten evitar la duplicación de código YAML en proyectos con múltiples repositorios o microservicios. Con esta funcionalidad, puedes definir una plantilla de pipeline (por ejemplo, compilación de Docker, despliegue en Kubernetes o análisis de calidad de código) en un solo lugar y llamarla desde decenas de repositorios diferentes.
1. ¿Qué es un Reusable Workflow y por qué usarlo?
En organizaciones medianas o grandes, mantener copias idénticas de pipelines en 50 repositorios distintos conduce a la pesadilla de mantenimiento conocida como DRY violation (Don't Repeat Yourself). Si necesitas actualizar una versión de Node.js o corregir un parámetro de seguridad en Helm, tendrías que enviar 50 Pull Requests.
Concepto Clave: Un Called Workflow (workflow reutilizable/llamado) expone el disparador on: workflow_call. Un Caller Workflow (workflow invocador) llama a ese pipeline dentro de uno de sus jobs mediante la directiva uses:.
2. Reusable Workflows vs. Custom Actions
Es común confundir los Reusable Workflows con las Composite/Custom Actions. Aunque ambas promueven la reutilización de código, resuelven distintos niveles de abstracción:
| Característica | Reusable Workflows | Composite / Custom Actions |
|---|---|---|
| Nivel de Jerarquía | Nivel de Job completo. Puede incluir múltiples jobs en paralelo/secuencia. | Nivel de Step (paso) individual dentro de un job existente. |
| Runners / Ambiente | Maneja sus propios runners (runs-on), permisos (permissions) y matriz. | Se ejecuta dentro del runner ya asignado por el job que la invoca. |
| Matriz y Paralelismo | Soporta matrices complejas y múltiples empleos coordinados. | No maneja empleos independientes ni matrices de ejecución directa. |
| Secretos y Entornos | Soporta integración nativa con environment y aprobación previa. | Accede a las variables y secretos transmitidos al paso. |
3. Sintaxis del Workflow Reutilizable (Called Workflow)
El archivo se guarda habitualmente dentro de .github/workflows/ (por ejemplo, _build_and_deploy.yml). La clave fundamental radica en definir el evento de activación on: workflow_call.
name: Standard Build & Deploy Pipeline
on:
workflow_call:
# 1. Definición de Parámetros de Entrada
inputs:
environment:
description: 'Entorno de despliegue (staging, production)'
required: true
type: string
node_version:
description: 'Versión de Node.js'
required: false
default: '20'
type: string
run_tests:
description: 'Si se deben ejecutar pruebas unitarias'
required: false
default: true
type: boolean
# 2. Definición de Secretos Requeridos
secrets:
AWS_ROLE_ARN:
description: 'ARN del rol de AWS IAM mediante OIDC'
required: true
SLACK_WEBHOOK:
description: 'URL de webhook para notificaciones'
required: false
# 3. Salidas generadas para el caller
outputs:
image_tag:
description: 'Tag generado de la imagen Docker'
value: ${{ jobs.build.outputs.docker_tag }}
jobs:
build:
name: Build & Test
runs-on: ubuntu-latest
outputs:
docker_tag: ${{ steps.prep.outputs.tag }}
steps:
- name: Checkout del repositorio caller
uses: actions/checkout@v4
- name: Configurar Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Instalar Dependencias
run: npm ci
- name: Ejecutar Tests
if: ${{ inputs.run_tests }}
run: npm test
- name: Generar Docker Tag
id: prep
run: echo "tag=${{ inputs.environment }}-${{ github.sha }}" >> $GITHUB_OUTPUT
deploy:
name: Deploy a K8s
needs: build
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Notificar inicio
run: echo "Desplegando imagen ${{ needs.build.outputs.docker_tag }} en ${{ inputs.environment }}"4. Invocando el Workflow (Caller Workflow)
El repositorio que desea consumir este pipeline estandarizado crea un workflow normal (disparado por push, pull_request, etc.) y llama al reusable workflow definiendo uses: a nivel de job.
name: Production Release
on:
push:
branches:
- main
jobs:
# Llama al Reusable Workflow dentro del mismo repositorio (vía ruta relativa)
call-pipeline:
uses: ./.github/workflows/_build_and_deploy.yml
with:
environment: production
node_version: '20'
run_tests: true
secrets:
AWS_ROLE_ARN: ${{ secrets.PROD_AWS_ROLE_ARN }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
# Job posterior que utiliza las salidas (outputs) del reusable workflow
notify:
needs: call-pipeline
runs-on: ubuntu-latest
steps:
- name: Ver Tag Desplegado
run: |
echo "Despliegue finalizado exitosamente."
echo "Tag publicado: ${{ needs.call-pipeline.outputs.image_tag }}"5. Manejo Avanzado de Secretos: secrets: inherit
Pasar manualmente 10 o 15 secretos a través de la cláusula secrets: puede ser tedioso. GitHub Actions provee la directiva secrets: inherit para que el workflow reutilizable herede automáticamente todos los secretos del repositorio y de la organización.
call-central-pipeline:
# El llamado hereda implícitamente todos los secretos accesibles en el caller
uses: mi-organizacion/ci-templates/.github/workflows/deploy.yml@v2
with:
environment: staging
secrets: inherit6. Centralización entre Múltiples Repositorios (Cross-Repository)
La mayor ventaja de los Reusable Workflows se desbloquea al hospedar tus plantillas en un repositorio centralizado dentro de tu organización (por ejemplo, mi-org/devops-templates).
Referencia por Referencia Git (Branch, Tag o Commit SHA):
- Por Tag Semántico (Recomendado para producción):
uses: mi-org/devops-templates/.github/workflows/docker-build.yml@v1.2.0
- Por Rama (Desarrollo/Testing):
uses: mi-org/devops-templates/.github/workflows/docker-build.yml@main
- Por Commit SHA (Máxima seguridad e inmutabilidad):
uses: mi-org/devops-templates/.github/workflows/docker-build.yml@a1b2c3d4e5f6...
Configuración de Permisos en GitHub Org: Para que otros repositorios privados de tu organización puedan invocar el workflow reutilizable hospedado en un repo privado central, debes ir a:
Repository Settings → Actions → General → Access y seleccionar "Accessible from repositories in the 'NAME' organization".
7. Buenas Prácticas y Reglas de Oro
- Prefijo con guion bajo: Nombra tus workflows reutilizables internos comenzando con guion bajo (ej:
_terraform-pipeline.yml) para diferenciarlos visualmente de los workflows de entrada estándar.
- Límite de Anidación: Puedes anidar Reusable Workflows (un reusable workflow llamando a otro), pero existe un límite máximo de 4 niveles de profundidad.
- Estrategia de Versionado: Utiliza releases semánticos (tags
v1,v1.1.0) en tu repositorio centralizado de pipelines para no romper los proyectos consumidores cuando hagas cambios retroincompatibles.
- Evitar dependencias ocultas: Declara explícitamente todos los
inputsysecretsnecesarios en elon: workflow_callpara contar con documentación autocontenida.