---
name: ci-cd-pipelines
description: CI/CD con GitHub Actions y GitLab CI. Stages de lint, test, build, deploy. Caching, artifacts, matrix builds, secrets management, deployment strategies, monorepos y quality gates.
version: "1.0.0"
herramientasPermitidas: [Read, Bash]
exclusiones:
  - "No cargar para Kubernetes Deployments o Helm releases — para Kubernetes cargar `kubernetes-orquestacion`."
  - "No cargar para Terraform o infraestructura como código — para IaC cargar `terraform-experto`."
  - "No cargar para SRE, SLOs, runbooks o post-mortems — para operaciones en producción cargar `sre-patrones`."
  - "No cargar para Git workflow (branching, commits, PRs) — para workflow de git cargar la regla `git-workflow.md`."
evolvable: true  # default para skill estandar
---
# CI/CD Pipelines — Integración y Entrega Continua

## Cuándo NO cargar

- La tarea es Kubernetes: manifiestos de Deployment, Helm releases, kubectl — cargar `kubernetes-orquestacion`.
- El trabajo es infraestructura como código con Terraform — cargar `terraform-experto`.
- El tema es SRE: SLOs, runbooks, alertas, post-mortems — cargar `sre-patrones`.
- La tarea es Git workflow: branching strategy, mensajes de commit, revisión de PRs — cargar la regla `git-workflow.md`.

Un pipeline bien diseñado detecta errores antes de producción, entrega valor continuamente
y construye confianza en el equipo. Este skill cubre patrones para Python, Node y containers.

---

## Reglas Obligatorias

1. **Lint como primer gate** — no ejecutar tests si el código no formatea.
2. **Cobertura mínima como quality gate** — `--cov-fail-under=80`.
3. **Security scanning** — código, dependencias e imagen Docker.
4. **NUNCA credenciales hardcodeadas** en YAML — usar GitHub Secrets / GitLab CI Variables.
5. **`concurrency: cancel-in-progress: true`** — evitar deploys simultáneos.
6. **Imagen Docker con tag de versión exacta** — NUNCA `:latest` en producción.
7. **Smoke tests post-deploy** — verificar health endpoint después de cada deploy.
8. **Staging antes de producción** — sin excepción.

---

## Estructura de Pipeline (GitHub Actions)

```yaml
# Orden de jobs con dependencias
jobs:
  lint:        # Primero: formato y types
  test:        # Segundo: requiere lint
    needs: lint
  security:    # Paralelo a test: requiere lint
    needs: lint
  build:       # Tercero: requiere test + security
    needs: [test, security]
  deploy-staging:
    needs: build
    if: github.ref == 'refs/heads/develop'
  deploy-production:
    needs: build
    if: github.ref == 'refs/heads/main'
```

Patrón clave: `concurrency` para cancelar runs previos del mismo PR:

```yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
```

---

## Quality Gates — No Negociables

```bash
# 1. Cobertura mínima
pytest --cov-fail-under=80

# 2. Sin vulnerabilidades críticas
trivy image --exit-code 1 --severity CRITICAL mi-imagen:latest

# 3. Lint sin errores
ruff check . --exit-non-zero-on-fix

# 4. Type checking
mypy app/ --strict

# 5. No secrets en código
trufflehog filesystem --fail ./
```

---

## Caching — Reducir Tiempo de Pipeline

```yaml
# Cache automático de pip
- uses: actions/setup-python@v5
  with:
    python-version: "3.12"
    cache: pip

# Docker layer cache
- uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max
```

---

## Secrets — Gestión Correcta

- SIEMPRE usar secrets del CI, NUNCA en código ni archivos de pipeline.
- Environment-scoped secrets: staging y prod tienen valores distintos.
- Rotar secrets periódicamente via CLI: `gh secret set API_KEY --env production`.

---

## Anti-patrones de CI/CD

| Anti-patrón | Corrección |
|-------------|------------|
| Tests que dependen de orden | Tests independientes con fixtures |
| Credenciales en YAML | GitHub Secrets + env vars |
| Pipeline sin caché | `cache:` en dependencias |
| Deploy sin smoke tests | Health check post-deploy |
| Un solo job gigante | Jobs separados con `needs:` |
| Sin `concurrency:` | `cancel-in-progress: true` |
| `:latest` en producción | SHA o semver en imagen |

---

## Checklist de Pipeline Productivo

- [ ] Lint y format check como primer gate
- [ ] Tests con BD real (no solo mocks)
- [ ] Cobertura mínima como quality gate
- [ ] Security scanning (código + deps + imagen)
- [ ] Docker build con cache habilitado
- [ ] Deploy a staging antes de producción
- [ ] Smoke tests post-deploy
- [ ] Notificaciones de fallo en canal de alertas
- [ ] Secrets en el sistema de CI (no en código)
- [ ] `concurrency` configurado
- [ ] Tiempo total < 15 minutos

Para pipelines completos de GitHub Actions y GitLab CI, matrix builds, monorepo y notificaciones, ver [recursos/pipelines-completos.md](recursos/pipelines-completos.md).

Para template de GitHub Actions reutilizable, ver [recursos/github-actions-template.yaml](recursos/github-actions-template.yaml).

## Gotchas / Errores comunes no obvios

**`actions/cache` no hace hit si el lock file cambia en la misma PR que agrega la dependencia**: la clave del cache incluye el hash del `package-lock.json` o `requirements.txt` — cuando el PR agrega una nueva dependencia, la clave cambia y el cache no existe aún, causando una instalación completa. Causa: es el comportamiento correcto y esperado, pero sorprende en PRs de "solo agrego una dependencia". Fix: aceptar que el primer build de una nueva dependencia siempre es lento — el cache se llenará en builds subsiguientes. Optimizar usando `restore-keys` para hit parcial.

**`concurrency: cancel-in-progress: true` cancela el deploy de staging cuando se pushea al PR antes de que el deploy termine**: si alguien hace dos pushes rápidos al mismo PR, el primer deploy de staging se cancela a la mitad. Causa: `cancel-in-progress` cancela cualquier run del mismo grupo de concurrencia, incluyendo deploys en progreso. Fix: para jobs de deploy, usar una concurrencia diferente: `group: deploy-staging-${{ github.ref }}` para agrupar solo deploys del mismo branch, y considerar `cancel-in-progress: false` para deploys que no deben cancelarse a la mitad.

**Secrets definidos en GitHub Actions no están disponibles en workflows de forks en PRs externas**: los workflows disparados por un PR de un fork (contribuidor externo) no tienen acceso a los secrets del repositorio por seguridad. Causa: prevención de exfiltración de secrets a forks maliciosos. Fix: usar el evento `pull_request_target` con cuidado para acceder a secrets en PRs de forks, pero solo para jobs que no ejecutan código del PR — los jobs de lint/test pueden correr sin secrets; los de deploy sí necesitan el evento `push` al branch base.

**`docker/build-push-action` con `cache-from: type=gha` puede hacer el build más lento si el cache es muy grande**: el cache de GitHub Actions (10GB límite) se llena con layers de Docker — si se usan imágenes base grandes y muchos stages, el cache puede alcanzar el límite y las layers más antiguas se evictan, causando rebuilds completos. Causa: el cache de GHA tiene límite de 10GB por repositorio. Fix: usar `cache-from: type=registry,ref=ghcr.io/org/app:cache` con un registro de contenedores para cache ilimitado, o usar `mode=min` en el cache para solo guardar las layers finales en lugar de todas las intermedias.
