⚙️ Jutsus Automatizados: GitHub Actions

“El ninja sabio clona su chakra para realizar mil tareas simultáneas. GitHub Actions es ese jutsu en el mundo del código.” - Maestro de la Automatización

🎯 La Filosofía de la Automatización

Imagina tener robots ninjas que trabajen para ti 24/7:

  • ✅ Ejecutan tests cada vez que haces push
  • ✅ Despliegan tu app automáticamente cuando fusionas a main
  • ✅ Revisan tu código buscando vulnerabilidades
  • ✅ Envían notificaciones a Slack cuando alguien abre un PR
  • ✅ Publican tu paquete npm cuando creas una release

GitHub Actions es el motor de automatización nativo de GitHub. Sin servidores externos, sin configuración compleja. Todo vive en archivos YAML dentro de tu repo.


📜 El Camino del Conocimiento

Parte 1: Anatomía de un Workflow

Un workflow es un proceso automatizado definido en un archivo .yml o .yaml.

Ubicación obligatoria:

tu-repo/
└── .github/
    └── workflows/
        ├── ci.yml
        ├── deploy.yml
        └── tests.yml

Estructura básica:

name: CI Pipeline              # Nombre descriptivo

on:                            # Eventos que activan el workflow
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:                          # Tareas a ejecutar
  test:                        # Nombre del job
    runs-on: ubuntu-latest     # Máquina virtual
    
    steps:                     # Pasos del job
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test

Conceptos clave:

  1. Evento (on): ¿Cuándo se activa? (push, PR, schedule, manual)
  2. Job: Tarea independiente (pueden correr en paralelo)
  3. Runner: Máquina virtual (ubuntu, windows, macos)
  4. Step: Acción individual dentro de un job
  5. Action: Bloque reutilizable (de GitHub o comunidad)

Parte 2: Tu Primer Workflow - Tests Automáticos

Objetivo: Ejecutar tests cada vez que alguien hace push o abre PR.

Archivo .github/workflows/tests.yml:

name: Run Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [18, 20, 22]  # Probar en múltiples versiones
    
    steps:
      - name: 📥 Checkout repository
        uses: actions/checkout@v4
      
      - name: 🟢 Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      
      - name: 📦 Install dependencies
        run: npm ci
      
      - name: 🧪 Run tests
        run: npm test
      
      - name: 📊 Upload coverage to Codecov
        if: success()
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

Explicación:

  • strategy.matrix: Ejecuta el job 3 veces (Node 18, 20, 22)
  • npm ci: Instalación limpia (más rápido que npm install)
  • if: success(): Solo ejecuta si pasos anteriores pasaron
  • secrets.CODECOV_TOKEN: Variable secreta (la defines en Settings)

Resultado: Badge en tu README

![Tests](https://github.com/user/repo/workflows/Run%20Tests/badge.svg)

Parte 3: CI/CD Completo - Deploy Automático

Workflow que despliega a producción al fusionar a main:

name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v4
      
      - name: 🟢 Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: 📦 Install dependencies
        run: npm ci
      
      - name: 🏗️ Build project
        run: npm run build
        env:
          VITE_API_URL: ${{ secrets.PRODUCTION_API_URL }}
      
      - name: 🧪 Run tests
        run: npm test
      
      - name: 🚀 Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'
      
      - name: 📢 Notify Slack
        if: success()
        uses: slackapi/slack-github-action@v1.24.0
        with:
          channel-id: '#deployments'
          slack-message: |
            ✅ Deploy exitoso a producción
            Commit: ${{ github.sha }}
            Autor: ${{ github.actor }}
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

Servicios populares de deploy:

# Vercel
- uses: amondnet/vercel-action@v25

# Netlify
- uses: nwtgck/actions-netlify@v2.0

# AWS S3
- uses: aws-actions/configure-aws-credentials@v4
- run: aws s3 sync ./dist s3://mi-bucket --delete

# GitHub Pages
- uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./dist

# Docker Hub
- uses: docker/build-push-action@v5
  with:
    push: true
    tags: user/repo:latest

Parte 4: Eventos y Triggers Avanzados

1. Ejecutar workflow manualmente:

on:
  workflow_dispatch:  # Botón "Run workflow" en la UI
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        type: choice
        options:
          - staging
          - production
      version:
        description: 'Version tag'
        required: false
        default: 'latest'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to ${{ github.event.inputs.environment }}
        run: |
          echo "Deploying version ${{ github.event.inputs.version }}"
          echo "to ${{ github.event.inputs.environment }}"

2. Cron jobs (schedule):

on:
  schedule:
    - cron: '0 2 * * *'  # Todos los días a las 2am UTC

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Delete old artifacts
        run: |
          # Script para limpiar archivos antiguos

Sintaxis de cron:

┌───────────── minuto (0 - 59)
│ ┌─────────── hora (0 - 23)
│ │ ┌───────── día del mes (1 - 31)
│ │ │ ┌─────── mes (1 - 12)
│ │ │ │ ┌───── día de la semana (0 - 6) (domingo = 0)
│ │ │ │ │
* * * * *

Ejemplos:

  • 0 0 * * * - Diario a medianoche
  • 0 9 * * 1 - Lunes a las 9am
  • */15 * * * * - Cada 15 minutos

3. Filtrar por paths:

on:
  push:
    paths:
      - 'src/**'           # Solo si cambió código fuente
      - '!src/**/*.test.js' # Excepto archivos de test

4. Múltiples eventos:

on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize, reopened]
  release:
    types: [published]

Parte 5: Actions Reutilizables del Marketplace

Top Actions imprescindibles:

1. Checkout:

- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # Clonar todo el historial

2. Setup de lenguajes:

# Node.js
- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

# Python
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'

# Go
- uses: actions/setup-go@v5
  with:
    go-version: '1.21'

3. Cache de dependencias:

- name: Cache node modules
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

4. Análisis de código:

# ESLint
- name: Run ESLint
  run: npm run lint

# SonarCloud
- uses: SonarSource/sonarcloud-github-action@master
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

# CodeQL (seguridad)
- uses: github/codeql-action/analyze@v3

5. Notificaciones:

# Slack
- uses: slackapi/slack-github-action@v1.24.0

# Discord
- uses: sarisia/actions-status-discord@v1

# Email
- uses: dawidd6/action-send-mail@v3

Parte 6: Secrets y Variables de Entorno

Crear secrets:

  1. Repo > Settings > Secrets and variables > Actions
  2. New repository secret
  3. Name: VERCEL_TOKEN, Value: tu-token-secreto

Usar secrets en workflows:

steps:
  - name: Deploy
    env:
      API_KEY: ${{ secrets.API_KEY }}
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
    run: |
      echo "Deploying with API key"
      # El secret NO se imprime en logs

Variables de entorno globales:

env:
  NODE_ENV: production
  CACHE_VERSION: v1

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      BUILD_ID: ${{ github.run_number }}
    steps:
      - run: echo "Build #$BUILD_ID in $NODE_ENV"

Contexts disponibles:

${{ github.repository }}      # user/repo
${{ github.actor }}           # Usuario que triggereó
${{ github.ref }}             # refs/heads/main
${{ github.sha }}             # Hash del commit
${{ github.event_name }}      # push, pull_request, etc.
${{ runner.os }}              # Linux, Windows, macOS
${{ secrets.MY_SECRET }}      # Secreto definido en Settings

Parte 7: Workflows Avanzados

Matriz de múltiples dimensiones:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [18, 20]
    include:
      - os: ubuntu-latest
        node: 22
    exclude:
      - os: macos-latest
        node: 18

# Genera 6 jobs: ubuntu(18,20,22), windows(18,20), macos(20)

Jobs dependientes:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
  
  test:
    needs: build  # Solo corre si 'build' pasa
    runs-on: ubuntu-latest
    steps:
      - run: npm test
  
  deploy:
    needs: [build, test]  # Necesita que AMBOS pasen
    runs-on: ubuntu-latest
    steps:
      - run: npm run deploy

Artifacts (compartir archivos entre jobs):

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: dist
          path: dist/
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download build artifacts
        uses: actions/download-artifact@v3
        with:
          name: dist
      - run: ls -la dist/

💡 Jutsu Secreto: Self-Hosted Runners

Si necesitas más control (ej: acceso a recursos internos), puedes usar runners propios:

# En tu servidor
curl -o actions-runner.tar.gz -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf actions-runner.tar.gz
./config.sh --url https://github.com/user/repo --token TOKEN
./run.sh

En el workflow:

jobs:
  deploy:
    runs-on: self-hosted  # En lugar de ubuntu-latest

Casos de uso:

  • Deploy a servidores internos
  • Tests que requieren hardware específico (GPUs)
  • Builds de apps nativas (iOS en Mac físico)

🎯 Reto Ninja del Tema

Misión: Crear un pipeline CI/CD completo

  1. Crea un repositorio con un proyecto React/Vite simple

  2. Implementa 3 workflows:

    Workflow 1: Tests (tests.yml)

    • Trigger: push y PR a main
    • Jobs: lint + test
    • Usa matriz de Node 18, 20

    Workflow 2: Deploy Preview (preview.yml)

    • Trigger: PR a main
    • Job: build + deploy a Vercel preview
    • Comenta el URL en el PR

    Workflow 3: Deploy Prod (deploy.yml)

    • Trigger: push a main
    • Jobs: build → test → deploy a producción
    • Envía notificación (Slack o Discord)
  3. Añade badge del workflow de tests al README

  4. Configura al menos 1 secret (VERCEL_TOKEN o similar)

  5. Haz que falle un workflow (a propósito) y corrígelo

Criterios de éxito:

  • ✅ Los 3 workflows existen y están activos
  • ✅ Tests pasan en múltiples versiones de Node
  • ✅ Deploy automático funciona
  • ✅ Badge visible en README
  • ✅ Al menos 1 ejecución exitosa de cada workflow

📚 Recursos Adicionales


Boss Fight Final: El Guardián del Historial - Reto Integrador

¿Automatizaste tu repo? Comparte el link de tus Actions en Discord con #JutsuAutomatizado! ⚙️🥷