GitHub Actions: del commit al deploy sin intervención manual
GitHub Actions parece intimidante por el YAML y las variables de contexto, pero el 90% de los errores son de sintaxis o de contexto mal entendido. Esta guía construye un pipeline Docker real, production-grade, desde cero.
GitHub Actions tiene fama de ser difícil. No lo es — pero tiene una curva de entrada que asusta: YAML con indentación estricta, variables de contexto con sintaxis propia, y una documentación que asume que ya sabés lo que estás haciendo.
El 90% de los errores que vas a cometer en GitHub Actions son de dos tipos: indentación incorrecta o variables de contexto mal entendidas. Esta guía existe para que no pierdas horas en ninguno de los dos.
El modelo mental correcto
Antes de escribir una sola línea de YAML, necesitás tener claro el vocabulario. GitHub Actions tiene cinco conceptos centrales:
Workflow — El archivo YAML que define toda la automatización. Vive en .github/workflows/ y puede haber varios por repositorio.
Trigger — El evento que dispara el workflow. Puede ser un push a main, un pull request, un schedule, o incluso un evento manual.
Job — Un grupo de steps que corre en la misma máquina virtual. Los jobs corren en paralelo por defecto; podés hacerlos dependientes con needs:.
Runner — La máquina virtual que ejecuta el job. ubuntu-latest es el más común. GitHub los provee gratis hasta cierto límite mensual.
Step — La unidad mínima de trabajo. Cada step es o un comando de shell (run:) o una acción reutilizable de terceros (uses:).
La confusión entre run: y uses: es uno de los errores más frecuentes. run: ejecuta comandos directamente en el runner. uses: delega en una Action — un paquete de lógica que alguien ya escribió y publicó.
Los errores que vas a cometer (y cómo evitarlos)
Indentación incorrecta. YAML es sensible a los espacios. Un step mal indentado dentro de un job va a producir un error críptico. Usá un linter de YAML en tu editor — VSCode tiene uno nativo.
Hardcodear secrets en archivos. Nunca pongas credenciales en el YAML. Si lo hacés y commitás, ya es tarde — el secreto está en el historial de git aunque lo borres después. Los secrets van en la configuración del repositorio en GitHub, no en el código.
Confundir run: con uses:. Son mutuamente excluyentes en un step. Un step que arranca con uses: no puede tener run:.
Olvidar el bloque permissions:. Muchas Actions necesitan permisos explícitos para leer el repo, escribir packages, o comentar en PRs. Sin el bloque, van a fallar con un error de autorización poco descriptivo.
Ignorar fetch-depth. Por defecto, actions/checkout@v4 hace un shallow clone (solo el último commit). Si tu pipeline necesita el historial completo — para generar changelogs, o para herramientas que analizan diffs — tenés que agregar fetch-depth: 0.
Desperdiciar minutos gratuitos. Los runners de GitHub tienen un límite mensual. Jobs largos que podrían paralelizarse o cachear dependencias queman ese límite innecesariamente.
Secrets y variables de entorno
Los secrets son valores encriptados que GitHub almacena y que nunca aparecen en los logs. Se configuran en Settings → Secrets and variables → Actions y se referencian con la sintaxis ${{ secrets.NOMBRE }}.
Las variables de entorno son para configuración que no es sensible — URLs de staging, nombres de entornos, flags de feature.
El GITHUB_TOKEN es un caso especial: GitHub lo inyecta automáticamente en cada workflow sin que tengas que configurar nada. Tiene permisos sobre el repositorio actual y es lo que vas a usar para autenticarte contra el GitHub Container Registry.
env:
NODE_ENV: production
API_URL: https://api.staging.example.com
steps:
- name: Deploy
run: ./deploy.sh
env:
API_KEY: ${{ secrets.API_KEY }}
El pipeline real: build y push de Docker a GHCR
Suficiente teoría. Acá está un workflow completo, production-grade, que construye una imagen Docker desde el Dockerfile del repo y la pushea al GitHub Container Registry (GHCR).
name: Build & Push Docker Image
on:
push:
branches: [ "main" ]
jobs:
build-and-push:
name: Build & Push to GHCR
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Push image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
ghcr.io/${{ github.repository }}:latest
Varias cosas que vale la pena marcar de este workflow:
permissions: es explícito. contents: read para clonar el repo, packages: write para pushear a GHCR. Sin este bloque, el step de push va a fallar con un 403.
${{ github.sha }} tagea la imagen con el hash del commit exacto. Esto hace que cada imagen sea trazable — si un deploy rompe producción, sabés exactamente qué commit corrés.
No hay secrets manuales. GITHUB_TOKEN es auto-inyectado. El pipeline funciona sin que el equipo configure nada en Settings.
docker/build-push-action@v6 abstrae el buildkit y el push en un solo step. Sin esta Action, serían cinco comandos manuales y gestión de credenciales a mano.
Debugging cuando algo falla
Los logs de GitHub Actions son detallados, pero no siempre obvios. Tres estrategias que funcionan:
Activá el debug logging. Agregá un secret llamado ACTIONS_STEP_DEBUG con valor true. El workflow va a logear cada comando interno de las Actions. Ruidoso, pero útil cuando no entendés por qué falla un step.
Inspeccioná el contexto con echo. Las variables de contexto (github, env, secrets) a veces tienen valores distintos a lo que esperás. Un step de debug que imprime los valores te ahorra media hora de confusión:
- name: Debug context
run: |
echo "Actor: ${{ github.actor }}"
echo "Repo: ${{ github.repository }}"
echo "SHA: ${{ github.sha }}"
echo "Ref: ${{ github.ref }}"
Usá if: always() en steps de diagnóstico para que corran aunque pasos anteriores fallen:
- name: Dump logs
if: always()
run: cat /tmp/build.log
Probá localmente con act. La herramienta nektos/act ejecuta workflows de GitHub Actions en tu máquina usando Docker. No es perfecta — algunos runners no se replican al 100% — pero es útil para iterar sin consumir minutos ni esperar a que GitHub encole el job.
Cómo seguir aprendiendo
Los tutoriales te muestran ejemplos limpios. Los repositorios reales te muestran cómo se maneja la complejidad.
El salto de comprensión más grande viene de leer los directorios .github/workflows/ de proyectos open source conocidos. Proyectos como Next.js, Astro, o cualquier CLI popular tienen pipelines que manejan casos que ningún tutorial cubre: matrices de versiones, deployments condicionales, releases automatizados, caché de dependencias.
Buscá un proyecto que uses en producción, abrí su carpeta de workflows, y leelo como si fuera código de aplicación — porque lo es.