Skip to content

CI CD Pipeline

Anderson Fabian Garcia Nieto edited this page Jul 9, 2026 · 4 revisions

Linker1 implementa un pipeline de integración y despliegue continuo completo, compuesto por tres workflows de GitHub Actions que cubren desde la compilación hasta el rollback automático en producción.


Visión General

                        ┌──────────────────────────────────────┐
                        │           CÓDIGO EN GITHUB           │
                        └──────────────┬───────────────────────┘
                                       │
                    ┌──────────────────┼──────────────────┐
                    ▼                  ▼                  ▼
              push a DEV/main    push tag v*.*.*     push a main
              pull_request                          workflow_dispatch
                    │                  │                  │
                    ▼                  ▼                  ▼
            ┌──────────────┐   ┌──────────────┐   ┌──────────────────┐
            │    ci.yml    │   │ release.yml  │   │  pipeline.yml    │
            │  (CI Gate)   │   │  (Release)   │   │  (CD Pipeline)   │
            └──────────────┘   └──────────────┘   └──────────────────┘

1. CI Workflow (ci.yml)

Trigger: push o PR a DEV, main o master
Propósito: Validar la calidad del código antes de merge

Flujo de Jobs

Build ──▶ Tests ──▶ Package ──▶ Summary
                        │       Smoke Test
                        │           │
                        │    API Tests (Live)
                        │           │
                        └───▶ Publish GitHub Package

Descripción de cada Job

Build

  • Checkout del repositorio
  • Setup de JDK 21 (Temurin) con caché de Maven
  • Compilación de fuentes: mvn -B -ntp compile

Tests

  • Ejecución de tests unitarios: mvn -B -ntp test
  • Verificación de cobertura con JaCoCo: mvn -B -ntp verify -DskipTests
    • Umbral de cobertura: 100% de líneas (configurado en pom.xml, excluyendo Main.class)
  • Upload del reporte de cobertura como artefacto

Package

  • Generación del fat JAR: mvn -B -ntp package -DskipTests
  • Inclusión del launcher script (scripts/linker1)
  • Upload del artefacto linker1-jar

Summary

  • Descarga y verificación del artefacto empaquetado
  • Confirma que el JAR y el launcher existen y son ejecutables

Smoke Test

  • Inicia la aplicación real con el JAR empaquetado
  • Espera hasta 20 segundos por el startup
  • Validaciones funcionales contra http://localhost:8080:
    • GET / → 200 (página principal)
    • GET /app.js → 200 (JavaScript)
    • GET /styles.css → 200 (CSS)
    • POST /link con URL → crea link + verifica redirección 301
    • POST /link con alias → crea alias + verifica redirección 301
    • POST /link con alias duplicado → verifica conflicto 409
    • GET /does-not-exist → 404

API Tests (Live)

  • Solo en push (no en PRs)
  • Ejecuta la colección de Postman/Newman contra la instancia de producción: https://1.n-la-c.app
  • Genera y sube reporte JUnit XML

Publish GitHub Package

  • Solo tras smoke test + API tests exitosos
  • Genera versión única con metadata de CI: 1.0-ci.{RUN_NUMBER}.{RUN_ATTEMPT}.{SHA:7}
  • Publica en GitHub Packages (Maven registry)

2. Deployment Pipeline (pipeline.yml)

Trigger: push a main o workflow_dispatch
Propósito: Despliegue automatizado a producción con rollback

Flujo de Jobs

Build ──▶ Deploy to Production ──▶ Validate Production
                  │                        │
                  │        ┌───────────────┘
                  │        │ (si alguno falla)
                  │        ▼
                  └──▶ Rollback (automático)

Descripción de cada Job

Build

  • Compila y empaqueta el JAR (linker-app artifact)
  • Independiente del ci.yml (los artefactos de upload-artifact no cruzan entre workflows)

Deploy to Production

  • Environment: prod (scoping de secrets/variables)
  • Mecanismo: OCI Bastion (SSH tunnel gestionado, sin IP pública)
  • Acción: co-eiv-devsecops/material-curso/actions/oci-bastion-deploy@main

Proceso de despliegue remoto:

  1. Secrets se transportan en base64 para evitar inyección de shell
  2. Valores se decodifican y escapan para systemd (%%%, "\")
  3. JAR se copia a /opt/linker1/linker1.jar
  4. Se genera/actualiza la unidad systemd (linker1.service)
  5. systemctl daemon-reload && systemctl restart linker1.service
  6. Verificación post-deploy:
    • systemctl is-active linker1.service
    • curl localhost:8080/ → 200 ✓
    • curl localhost:8080/healthz → status (informativo)

Validate Production

  • Smoke test de solo lectura contra https://1.n-la-c.app
  • Verifica assets estáticos y respuesta 404 para rutas inexistentes

Rollback (Automático)

  • Se ejecuta solo si deploy-prod o validate-prod fallan (if: failure())
  • Identifica el tag SemVer previo al actual
  • Descarga el JAR del release previo desde GitHub Releases
  • Lo despliega vía Bastion con el mismo mecanismo
  • Verifica que el servicio responde 200 post-rollback

3. Release Workflow (release.yml)

Trigger: push de tag v*.*.* o workflow_dispatch
Propósito: Publicar releases oficiales

Flujo

Checkout ──▶ Resolve Tag ──▶ Verify Quality Gates ──▶ Build Package ──▶ Publish GitHub Release

Proceso

  1. Validación del tag: formato SemVer estricto (vMAJOR.MINOR.PATCH)
  2. Quality gates: mvn -B -ntp clean verify (incluye tests + cobertura)
  3. Build del paquete: mvn -B -ntp package -DskipTests
  4. Publicación: GitHub Release con:
    • linker1-1.0-jar-with-dependencies.jar (artefacto ejecutable)
    • linker1 (launcher script)
    • Release notes auto-generados por GitHub

Diagrama Completo del Pipeline

┌─────────────────────────────────────────────────────────────────────────────────┐
│                                                                                 │
│  Developer                                                                      │
│     │                                                                           │
│     ├─── feature/* branch                                                       │
│     │       │                                                                   │
│     │       ▼                                                                   │
│     │   Pull Request → DEV                                                      │
│     │       │                                                                   │
│     │       ▼                                                                   │
│     │   ┌──────────── ci.yml ─────────────┐                                     │
│     │   │ Build → Test → Package → Smoke  │  ← Branch protection: requiere      │
│     │   │           │                     │     CI verde + 1 approval            │
│     │   │      Coverage ≥ 100%            │                                     │
│     │   └─────────────────────────────────┘                                     │
│     │       │                                                                   │
│     │       ▼ (merge aprobado)                                                  │
│     │                                                                           │
│     ├─── Pull Request → main                                                    │
│     │       │                                                                   │
│     │       ▼                                                                   │
│     │   ┌──────────── ci.yml ─────────────┐                                     │
│     │   │ Build → Test → Package → Smoke  │                                     │
│     │   │    + API Tests + Publish Pkg    │                                     │
│     │   └─────────────────────────────────┘                                     │
│     │       │                                                                   │
│     │       ▼ (merge a main)                                                    │
│     │                                                                           │
│     │   ┌──────── pipeline.yml ───────────┐                                     │
│     │   │ Build → Deploy Prod             │                                     │
│     │   │            │                    │                                     │
│     │   │            ▼                    │                                     │
│     │   │     Validate Prod               │                                     │
│     │   │            │                    │                                     │
│     │   │     (falla?) → Rollback         │                                     │
│     │   └─────────────────────────────────┘                                     │
│     │                                                                           │
│     └─── git tag v1.x.x + push                                                 │
│             │                                                                   │
│             ▼                                                                   │
│         ┌──────── release.yml ────────────┐                                     │
│         │ Verify → Build → GitHub Release │                                     │
│         └─────────────────────────────────┘                                     │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

Concurrencia y Optimizaciones

Característica Detalle
Concurrency groups ci-${{ github.ref }} con cancel-in-progress: true — un push nuevo cancela la ejecución anterior
Maven cache cache: maven en setup-java acelera builds subsecuentes
Batch mode mvn -B -ntp — sin output interactivo ni transfer progress
Artefactos entre jobs upload-artifact / download-artifact pasan el JAR entre jobs

Secrets y Variables

Nombre Tipo Nivel Propósito
LD_SDK_KEY Secret Repo LaunchDarkly SDK key
GITHUB_TOKEN Secret Auto Autenticación para GitHub Packages
DEPLOYMENT_PRIVATE_KEY Secret Repo SSH key para deploy vía Bastion
DEPLOYMENT_PUBLIC_KEY Secret Repo Matching public key
OCI_CLI_* Secrets Repo/Org Autenticación OCI CLI
OCI_BASTION_OCID Variable Org ID del Bastion
OCI_INSTANCE_OCID Variable Repo ID de la VM de producción
OTEL_EXPORTER_OTLP_* Variable/Secret Repo Configuración de Grafana Cloud
MYSQL_* Variable/Secret Repo Conexión MySQL para healthcheck

Clone this wiki locally