Entrada

CI/CD con GitHub Actions: Automatización Completa de DevOps

CI/CD con GitHub Actions: Automatización Completa de DevOps

CI/CD con GitHub Actions: Automatización Completa de DevOps

En el desarrollo moderno de software, la Integración Continua (CI) y el Despliegue Continuo (CD) son pilares fundamentales para entregar software de calidad de manera rápida y confiable. GitHub Actions ha revolucionado este espacio ofreciendo una plataforma integrada, potente y accesible para automatizar todos los aspectos del ciclo de vida del desarrollo.

¿Qué es CI/CD?

Integración Continua (CI)

La Integración Continua es una práctica de desarrollo donde los desarrolladores integran su código en un repositorio compartido frecuentemente. Cada integración se verifica mediante una build automatizada que incluye pruebas para detectar errores rápidamente.

Beneficios de CI:

  • Detección temprana de errores
  • Reducción de problemas de integración
  • Mejora la calidad del código
  • Facilita la colaboración en equipo

Despliegue Continuo (CD)

El Despliegue Continuo extiende CI automatizando el proceso de entrega de software a producción. Puede referirse a:

  1. Continuous Delivery: Automatización hasta el punto de despliegue (requiere aprobación manual)
  2. Continuous Deployment: Automatización completa incluyendo el despliegue a producción

GitHub Actions: Conceptos Fundamentales

Arquitectura de GitHub Actions

GitHub Actions utiliza varios componentes clave:

  1. Workflows: Procesos automatizados que se ejecutan en respuesta a eventos
  2. Jobs: Conjuntos de pasos que se ejecutan en el mismo runner
  3. Steps: Tareas individuales que pueden ejecutar acciones o comandos
  4. Actions: Aplicaciones reutilizables que realizan tareas específicas
  5. Runners: Servidores que ejecutan los workflows

Sintaxis Básica de un Workflow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
name: CI/CD Pipeline

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

# Variables de entorno globales
env:
  NODE_VERSION: '18'
  REGISTRY: ghcr.io

# Trabajos del workflow
jobs:
  test:
    name: Tests and Quality Checks
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: $
        cache: 'npm'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Run tests
      run: npm test
      
    - name: Run linting
      run: npm run lint

Workflows Esenciales para Diferentes Tecnologías

Node.js/JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
name: Node.js CI/CD

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [16, 18, 20]
        
    steps:
    - uses: actions/checkout@v4
    
    - name: Use Node.js $
      uses: actions/setup-node@v4
      with:
        node-version: $
        cache: 'npm'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Run tests
      run: npm test
      
    - name: Run coverage
      run: npm run coverage
      
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        token: $
        
    - name: Build application
      run: npm run build
      
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-files
        path: dist/

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Download build artifacts
      uses: actions/download-artifact@v3
      with:
        name: build-files
        path: dist/
        
    - name: Deploy to production
      run: |
        # Comandos de despliegue
        echo "Deploying to production..."

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
name: Python CI/CD

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        python-version: ['3.8', '3.9', '3.10', '3.11']
        
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python $
      uses: actions/setup-python@v4
      with:
        python-version: $
        
    - name: Cache pip packages
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: $-pip-$
        restore-keys: |
          $-pip-
          
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -r requirements-dev.txt
        
    - name: Lint with flake8
      run: |
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
        
    - name: Test with pytest
      run: |
        pytest --cov=./ --cov-report=xml
        
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

  security:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.10'
        
    - name: Install security tools
      run: |
        python -m pip install --upgrade pip
        pip install bandit safety
        
    - name: Run Bandit security scan
      run: bandit -r . -f json -o bandit-report.json
      
    - name: Check dependencies for vulnerabilities
      run: safety check --json --output safety-report.json

Docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
name: Docker CI/CD

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: $

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
      
    - name: Log in to Container Registry
      uses: docker/login-action@v3
      with:
        registry: $
        username: $
        password: $
        
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: $/$
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern=
          type=semver,pattern=.
          
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: $
        labels: $
        cache-from: type=gha
        cache-to: type=gha,mode=max
        
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: $/$:latest
        format: 'sarif'
        output: 'trivy-results.sarif'
        
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

Estrategias Avanzadas

Matrix Builds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
jobs:
  test:
    runs-on: $
    
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16, 18, 20]
        include:
          - os: ubuntu-latest
            node-version: 18
            coverage: true
        exclude:
          - os: windows-latest
            node-version: 16
            
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Node.js $
      uses: actions/setup-node@v4
      with:
        node-version: $
        
    - name: Run tests
      run: npm test
      
    - name: Upload coverage
      if: matrix.coverage
      run: npm run coverage

Conditional Jobs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      frontend: $
      backend: $
    steps:
    - uses: actions/checkout@v4
    - uses: dorny/paths-filter@v2
      id: changes
      with:
        filters: |
          frontend:
            - 'frontend/**'
          backend:
            - 'backend/**'

  frontend-tests:
    needs: changes
    if: $
    runs-on: ubuntu-latest
    steps:
    - name: Test frontend
      run: echo "Testing frontend changes"

  backend-tests:
    needs: changes
    if: $
    runs-on: ubuntu-latest
    steps:
    - name: Test backend
      run: echo "Testing backend changes"

Deployment Environments

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
    - name: Deploy to staging
      run: echo "Deploying to staging environment"
      
  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    if: github.ref == 'refs/heads/main'
    steps:
    - name: Deploy to production
      run: echo "Deploying to production environment"

Seguridad y Mejores Prácticas

Gestión de Secrets

1
2
3
4
5
6
7
8
9
10
11
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Deploy with secrets
      env:
        API_KEY: $
        DATABASE_URL: $
      run: |
        # Usar las variables de entorno de forma segura
        curl -H "Authorization: Bearer $API_KEY" https://api.example.com

Security Scanning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
jobs:
  security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Run CodeQL Analysis
      uses: github/codeql-action/init@v2
      with:
        languages: javascript
        
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2
      
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
      
    - name: Run Snyk to check for vulnerabilities
      uses: snyk/actions/node@master
      env:
        SNYK_TOKEN: $
      with:
        command: test
        args: --severity-threshold=high

Artifact Management

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Build application
      run: npm run build
      
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: production-files
        path: dist/
        retention-days: 30
        
    - name: Upload test results
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: test-results
        path: test-results.xml

Monitoring y Observabilidad

Workflow Notifications

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
jobs:
  notify:
    runs-on: ubuntu-latest
    if: always()
    needs: [test, build, deploy]
    steps:
    - name: Notify Slack
      uses: 8398a7/action-slack@v3
      with:
        status: $
        channel: '#deployments'
        webhook_url: $
        
    - name: Update deployment status
      if: github.ref == 'refs/heads/main'
      run: |
        curl -X POST \
          -H "Authorization: token $" \
          -H "Content-Type: application/json" \
          -d '{"state":"success","description":"Deployment successful"}' \
          https://api.github.com/repos/$/deployments

Performance Monitoring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jobs:
  performance:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Run Lighthouse CI
      run: |
        npm install -g @lhci/cli@0.11.x
        lhci autorun
      env:
        LHCI_GITHUB_APP_TOKEN: $
        
    - name: Run load tests
      run: |
        docker run --rm -i loadimpact/k6 run - <loadtest.js

Optimización de Workflows

Caching Strategies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    # Cache de dependencias
    - name: Cache node modules
      uses: actions/cache@v3
      with:
        path: ~/.npm
        key: $-node-$
        restore-keys: |
          $-node-
          
    # Cache de Docker layers
    - name: Cache Docker layers
      uses: actions/cache@v3
      with:
        path: /tmp/.buildx-cache
        key: $-buildx-$
        restore-keys: |
          $-buildx-

Parallel Jobs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
    - name: Lint code
      run: npm run lint
      
  test-unit:
    runs-on: ubuntu-latest
    steps:
    - name: Run unit tests
      run: npm run test:unit
      
  test-integration:
    runs-on: ubuntu-latest
    steps:
    - name: Run integration tests
      run: npm run test:integration
      
  build:
    needs: [lint, test-unit, test-integration]
    runs-on: ubuntu-latest
    steps:
    - name: Build application
      run: npm run build

Troubleshooting Común

Debug de Workflows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jobs:
  debug:
    runs-on: ubuntu-latest
    steps:
    - name: Debug information
      run: |
        echo "GitHub context:"
        echo "$"
        echo "Runner context:"
        echo "$"
        echo "Environment variables:"
        env | sort
        
    - name: Setup tmate session
      if: failure()
      uses: mxschmitt/action-tmate@v3
      timeout-minutes: 15

Logs y Diagnósticos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Deploy with detailed logging
      run: |
        set -x  # Enable debug mode
        echo "Starting deployment..."
        # Comandos de deployment con logging detallado
      
    - name: Upload logs on failure
      if: failure()
      uses: actions/upload-artifact@v3
      with:
        name: deployment-logs
        path: |
          deployment.log
          error.log

Conclusión

GitHub Actions ha transformado la manera en que implementamos CI/CD, ofreciendo una plataforma integrada, poderosa y accesible. Las ventajas clave incluyen:

  • Integración nativa con GitHub
  • Ecosistema rico de actions reutilizables
  • Flexibilidad para casos de uso complejos
  • Escalabilidad desde proyectos pequeños hasta empresariales
  • Seguridad robusta con manejo de secrets y permisos granulares

Implementar CI/CD efectivo requiere:

  1. Planificación cuidadosa de los workflows
  2. Pruebas exhaustivas en múltiples etapas
  3. Monitoreo continuo y alertas
  4. Optimización constante para mejorar tiempos
  5. Seguridad como prioridad en todo el pipeline

Con GitHub Actions, puedes crear pipelines de CI/CD robustos que mejoren significativamente la calidad, velocidad y confiabilidad de tus entregas de software.



Andrés Nuñez - t4ifi

Esta entrada está licenciada bajo CC BY 4.0 por el autor.