Todo workflow de GitHub Actions vive en .github/workflows/ y define tres cosas: el disparador, la máquina y el orden de los pasos. El YAML de abajo corre lint, typecheck, test y build en un proyecto Node con pnpm.
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test --coverage
- run: pnpm build
Cuatro pasos cubren lo que suele romperse antes del merge: el lint agarra formato fuera del estándar, el typecatch agarra tipos mal cerrados, el test agarra regresiones y el build agarra un bundle roto. El resto del archivo resuelve equipo e infraestructura.
Cache de dependencias
Sin cache, cada ejecución instala todas las dependencias desde cero y el run pierde de 2 a 3 minutos en eso. Enciendes el cache en el propio setup-node, que usa el hash del lockfile como clave:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # clave: hash del lockfile
Tocaste el lockfile y el runner instala de nuevo. Un proyecto con npm usa cache: 'npm' y uno con yarn usa cache: 'yarn'. La guía de caching de GitHub Actions documenta la clave, la ruta y la restauración del cache.
CI y deploy en jobs separados
La verificación corre en cada pull request. El deploy corre cuando el código llega a main. Dos jobs unidos por needs y limitados por if sostienen esa frontera:
jobs:
ci:
runs-on: ubuntu-latest
steps:
# lint, typecheck, test, build
deploy:
needs: ci # depende del job de CI
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production # aprobación manual si está configurada
steps:
- uses: actions/checkout@v4
- name: Deploy to production
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: ./scripts/deploy.sh
environment: production ata el deploy a los environments del repositorio. Tú defines revisores obligatorios ahí y el deploy espera la aprobación antes de arrancar. La documentación de environments cubre reglas de ambiente y reglas de rama.
Secrets fuera del YAML
Una credencial escrita en el YAML entra en el historial de Git y en cada log de ejecución. Guardas el valor en Settings > Secrets and variables > Actions y lo inyectas solo en el paso que lo necesita:
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: ./scripts/deploy.sh
Los environment secrets separan valores por ambiente y heredan las reglas de aprobación del environment. La guía de secrets define el alcance de cada valor y qué pasa cuando imprimes un secret en un echo.
Matrix builds para varias versiones de Node
Una librería que promete soporte para Node 18, 20 y 22 tiene que probarlo en cada PR. La matriz corre el mismo job en paralelo, una ejecución por versión:
jobs:
ci:
strategy:
matrix:
node-version: [18, 20, 22]
runs-on: ubuntu-latest
name: Test on Node ${{ matrix.node-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
La documentación de matrix cubre fail-fast, que cancela las ejecuciones restantes cuando una falla, y la lista de combinaciones excluidas.
Artifacts entre jobs
El deploy consume la carpeta dist/ que generó el job de CI. Upload y download mueven esa carpeta:
# en el job ci
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
# en el job deploy
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
El reporte de cobertura usa el mismo camino: un upload con name: coverage-report conserva el HTML del run para quien quiera abrirlo después.
El pipeline completo
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test --coverage
- run: pnpm build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: ci
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: ./scripts/deploy.sh
El archivo tiene 88 líneas y cubre cache, la separación entre verificación y deploy, artifacts entre jobs y protección de environment. La mayoría de los proyectos cierra el pipeline acá.
Referencias
- Workflow syntax for GitHub Actions (GitHub Docs)
- Caching dependencies to speed up workflows (GitHub Docs)
- Using environments for deployment (GitHub Docs)
- Using secrets in GitHub Actions (GitHub Docs)
- Using a matrix for your jobs (GitHub Docs)
¿Te gustó el contenido?
Construyo productos web y soluciones con IA de la manera correcta — arquitectura sólida, código sostenible y entrega real.
Hablemos