F06 — AI-Assisted Build
10 — CI/CD & DevSecOps
Sección titulada «10 — CI/CD & DevSecOps»AI-First Engineering Framework — Baseline v6.5
Sección titulada «AI-First Engineering Framework — Baseline v6.5»Versión: 3.0.0 | Estado: Activo | Fecha: Marzo 2026 | Tipo: Framework Baseline
1. Branch Strategy — Trunk-Based para Equipos de 3
Sección titulada «1. Branch Strategy — Trunk-Based para Equipos de 3»1.1 Por qué Trunk-Based y no Git Flow
Sección titulada «1.1 Por qué Trunk-Based y no Git Flow»Para equipos de máximo 3 personas, Git Flow (develop, release, hotfix branches) es overhead innecesario. Trunk-Based Development mantiene la velocidad:
| Aspecto | Git Flow | Trunk-Based (nuestro) |
|---|---|---|
| Branches vivas | 5+ (main, develop, feature/, release/, hotfix/*) | 2-3 (main + 1-2 feature) |
| Merge conflicts | Frecuentes | Raros (branches cortas) |
| Time-to-deploy | Días/semanas | Horas |
| Overhead de gestión | Alto | Mínimo |
| Ideal para | Equipos 10+ | Equipos 1-3 |
1.2 Flujo de Branches
Sección titulada «1.2 Flujo de Branches»main (siempre deployable a staging automático) │ ├── feat/dofa-analysis ← Dev A (max 2 días) │ └── PR → review por Dev B o C → merge → auto-deploy staging │ ├── fix/tax-calculation ← Dev B (max 1 día) │ └── PR → review por Dev A o C → merge → auto-deploy staging │ └── feat/mcp-invoicing ← Dev C (max 2 días) └── PR → review por Dev A o B → merge → auto-deploy staging
Deploy a producción: manual approval después de smoke tests en staging1.3 Reglas del Branch Strategy
Sección titulada «1.3 Reglas del Branch Strategy»branch_rules: main: protection: true requires_pr: true min_reviewers: 1 # 1 de los 2 compañeros require_ci_pass: true # Los 9 quality gates deben pasar auto_deploy: "staging" # Merge → staging automático
feature_branches: naming: "feat/{ticket-or-description}" # feat/dofa-analysis max_lifetime: "2 days" # Si pasa de 2 días, es muy grande → dividir from: "main" # Siempre desde main (actualizado) merge_to: "main" # Directo a main squash_merge: true # 1 commit limpio por feature delete_on_merge: true # Limpiar branch automáticamente
fix_branches: naming: "fix/{ticket-or-description}" # fix/tax-rounding max_lifetime: "1 day" same_rules_as: "feature_branches"
hotfix: naming: "hotfix/{description}" # hotfix/critical-auth-bypass from: "main" merge_to: "main" deploy: "production (inmediato)" requires_review: true # Sí, incluso para hotfix
prohibited: - "develop" # No existe en trunk-based - "release/*" # No existe — main es siempre releasable - "branches > 2 días sin merge"1.4 Flujo Diario del Equipo de 3
Sección titulada «1.4 Flujo Diario del Equipo de 3»MAÑANA (15 min standup async o sync): 1. Pull main → actualizar tu feature branch 2. Revisar PRs pendientes de compañeros (prioridad: no bloquear a nadie) 3. Continuar tu feature branch
DURANTE EL DÍA: - Commits frecuentes en tu feature branch - Si tu branch tiene > 1 día: hacer PR parcial (feature flag si necesario) - Si necesitas trabajo de un compañero: comunícalo, no esperes al PR
ANTES DE TERMINAR: - Push tu branch - Si está listo: crear PR - Si NO está listo: rebasa sobre main para evitar divergencia1.5 Feature Flags para Features Incompletos
Sección titulada «1.5 Feature Flags para Features Incompletos»// Ejemplo de feature flags (adaptar al stack elegido)// Opciones: env vars, config service, KV store, database
const FEATURE_FLAGS = { DOFA_ANALYSIS_V2: env.FF_DOFA_V2 === 'true', // Desactivado en prod MCP_INVOICING: env.FF_MCP_INVOICING === 'true', // Solo staging};
// En el código:if (FEATURE_FLAGS.DOFA_ANALYSIS_V2) { return await newDofaAnalysis(request);} else { return await currentDofaAnalysis(request);}2. Pipeline CI/CD AI-First
Sección titulada «2. Pipeline CI/CD AI-First»Developer push → PR creado ↓[CI] 9 Quality Gates (ver Doc 07) ↓[REVIEW] Code review humano ↓Merge a main ↓[CD] Deploy a Staging ↓[TEST] Smoke tests + E2E + AI eval ↓[APPROVE] Manual approval para producción ↓[CD] Deploy a Producción (Blue-Green / Canary) ↓[MONITOR] Observabilidad + alertas activas2. Pipeline Completo (ejemplo GitHub Actions + Python)
Sección titulada «2. Pipeline Completo (ejemplo GitHub Actions + Python)»Nota: Este ejemplo usa GitHub Actions con Python/pytest. Adaptar al stack elegido.\n> Los stages (quality gates, security scan, build, deploy) son universales.
name: la organización AI-First CI/CD
on: push: branches: [main] # Solo main — trunk-based pull_request: branches: [main] # Quality gates en todo PR
env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}
jobs: # ────────────────────────────── # QUALITY GATES # ────────────────────────────── quality-gates: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Python 3.12 uses: actions/setup-python@v5 with: python-version: '3.12' cache: 'pip'
- name: Install dependencies run: pip install -r requirements-dev.txt
# Gate 1 & 2: Type + Lint - name: Type Check (mypy) run: mypy src/ --strict --ignore-missing-imports
- name: Lint (ruff) run: ruff check src/ --output-format=github
# Gate 3: Format - name: Format Check run: | black --check src/ isort --check-only src/
# Gate 4: Tests - name: Unit Tests run: | pytest tests/unit/ \ --cov=src \ --cov-report=xml \ --cov-fail-under=80 \ --junitxml=test-results.xml
- name: Upload coverage uses: codecov/codecov-action@v4 with: file: ./coverage.xml
# Gate 5: Dependencies - name: Dependency Audit run: pip-audit --strict --output=json > audit-report.json
# Gate 6: Secrets - name: Secrets Scan (TruffleHog) uses: trufflesecurity/trufflehog@main with: path: ./ base: ${{ github.event.repository.default_branch }} extra_args: --only-verified
# Gate 7: SAST - name: SAST (Semgrep) uses: semgrep/semgrep-action@v1 with: config: >- p/owasp-top-ten p/python p/secrets p/supply-chain
# Gate 8: Prompt Security - name: Prompt Injection Tests run: pytest tests/security/ -v --tb=short
# Gate 9: UX Accessibility - name: Build Frontend run: cd frontend && npm ci && npm run build
- name: Accessibility Check (axe) run: npx @axe-core/cli frontend/dist --tags wcag2a,wcag2aa --exit
# ────────────────────────────── # SECURITY SCANNING # ────────────────────────────── security-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
# Container scanning - name: Build Docker image run: docker build -t test-image:${{ github.sha }} .
- name: Container Scan (Trivy) uses: aquasecurity/trivy-action@master with: image-ref: test-image:${{ github.sha }} format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH' exit-code: '1'
- name: Upload Trivy results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif'
# ────────────────────────────── # BUILD & PUSH # ────────────────────────────── build: needs: [quality-gates, security-scan] runs-on: ubuntu-latest if: github.event_name == 'push' outputs: image-tag: ${{ steps.meta.outputs.tags }} image-digest: ${{ steps.build.outputs.digest }}
steps: - uses: actions/checkout@v4
- name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=sha,prefix=,suffix=,format=short type=ref,event=branch
- name: Build and push (multi-arch) id: build uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} platforms: linux/amd64,linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max provenance: true # SLSA provenance sbom: true # Software Bill of Materials
# ────────────────────────────── # DEPLOY STAGING # ────────────────────────────── deploy-staging: needs: build runs-on: ubuntu-latest environment: staging
steps: - uses: actions/checkout@v4
- name: Deploy to Staging run: | kubectl set image deployment/app-backend \ backend=${{ needs.build.outputs.image-tag }} \ -n ${K8S_NAMESPACE}-staging kubectl rollout status deployment/app-backend -n ${K8S_NAMESPACE}-staging
- name: Smoke Tests run: | sleep 30 # Wait for pods to be ready pytest tests/smoke/ --base-url=${STAGING_URL} -v
- name: AI Quality Evaluation run: | python scripts/run_ai_eval.py \ --env=staging \ --eval-suite=full \ --min-faithfulness=0.8 \ --min-recall=0.85
# ────────────────────────────── # DEPLOY PRODUCCIÓN # ────────────────────────────── deploy-production: needs: deploy-staging runs-on: ubuntu-latest environment: name: production url: ${APP_URL}
steps: - uses: actions/checkout@v4
- name: Blue-Green Deploy run: | # Crear nuevo deployment (green) kubectl apply -f k8s/deployments/backend-green.yaml kubectl set image deployment/app-backend-green \ backend=${{ needs.build.outputs.image-tag }} \ -n ${K8S_NAMESPACE}
# Esperar que esté listo kubectl rollout status deployment/app-backend-green -n ${K8S_NAMESPACE} --timeout=300s
# Verificar health ./scripts/health-check.sh https://green.${APP_DOMAIN}
# Switch tráfico a green kubectl patch service app-backend -n ${K8S_NAMESPACE} \ -p '{"spec":{"selector":{"version":"green"}}}'
echo "✅ Deploy exitoso: ${{ needs.build.outputs.image-tag }}"3. OWASP LLM Top 10 — Controles
Sección titulada «3. OWASP LLM Top 10 — Controles»| # | Vulnerabilidad | Control Implementado |
|---|---|---|
| LLM01 | Prompt Injection | InputGuard + PromptGuard + tests automatizados |
| LLM02 | Insecure Output Handling | OutputValidator + JSON schema validation |
| LLM03 | Training Data Poisoning | Review manual de datasets, checksums |
| LLM04 | Model Denial of Service | CostGuard + rate limiting + timeouts |
| LLM05 | Supply Chain Vulnerabilities | pip-audit + Trivy + SBOM |
| LLM06 | Sensitive Info Disclosure | PIIGuard + anonimización + RLS |
| LLM07 | Insecure Plugin Design | Tool schemas validados + Tool Execution Guard |
| LLM08 | Excessive Agency | Least privilege + HITL para acciones críticas |
| LLM09 | Overreliance | Fuentes citadas obligatorias + hallucination monitoring |
| LLM10 | Model Theft | API keys rotadas + rate limits por tenant |
4. Supply Chain Security
Sección titulada «4. Supply Chain Security»version: 2updates: # Python dependencies - package-ecosystem: "pip" directory: "/backend" schedule: interval: "weekly" open-pull-requests-limit: 5 groups: security-updates: applies-to: security-updates update-types: ["minor", "patch"]
# Node dependencies - package-ecosystem: "npm" directory: "/frontend" schedule: interval: "weekly"
# Docker base images - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly"
# GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly"5. Dockerfile Hardened (ejemplo Python)
Sección titulada «5. Dockerfile Hardened (ejemplo Python)»Nota: Este ejemplo usa Python. Los principios (multi-stage, non-root, no secrets, health check, minimal image) aplican a cualquier runtime.
# Dockerfile — Hardened para producción
# Usar imagen con versión exacta (no latest)FROM python:3.12.4-slim-bookworm AS builder
WORKDIR /app
# Instalar dependencias en capa separada (cache)COPY pyproject.toml ./RUN pip install --no-cache-dir build && \ pip install --no-cache-dir ".[production]"
# Stage de producciónFROM python:3.12.4-slim-bookworm AS production
# Actualizar paquetes del SO (security patches)RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
# Usuario no-rootRUN groupadd -r appuser && useradd -r -g appuser -u 1000 appuser
WORKDIR /app
# Copiar solo lo necesarioCOPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packagesCOPY --chown=appuser:appuser src/ ./src/
USER appuser
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1
# Puerto sin privilegiosEXPOSE 8000
# No shell por seguridadENTRYPOINT ["python", "-m", "uvicorn", "src.main:app"]CMD ["--host", "0.0.0.0", "--port", "8000", "--workers", "4"]6. GitOps & Gestión de Releases
Sección titulada «6. GitOps & Gestión de Releases»# scripts/release.py — Automatización de releases semánticos
import subprocessimport refrom datetime import datetime
def bump_version(bump_type: str) -> str: """bump_type: major | minor | patch""" current = get_current_version() major, minor, patch = map(int, current.split('.'))
if bump_type == "major": return f"{major+1}.0.0" elif bump_type == "minor": return f"{major}.{minor+1}.0" else: return f"{major}.{minor}.{patch+1}"
def create_release(version: str, notes: str): # 1. Actualizar version en pyproject.toml update_version_file(version)
# 2. Commit y tag subprocess.run(["git", "add", "pyproject.toml"]) subprocess.run(["git", "commit", "-m", f"chore: release v{version}"]) subprocess.run(["git", "tag", "-a", f"v{version}", "-m", f"Release {version}"]) subprocess.run(["git", "push", "--follow-tags"])
# 3. Crear GitHub Release con notas automáticas subprocess.run([ "gh", "release", "create", f"v{version}", "--title", f"v{version}", "--notes", notes, "--target", "main" ])7. Claude Code Integration
Sección titulada «7. Claude Code Integration»# Configuración de Claude Code para CI{ "model": "claude-sonnet-4-5", "permissions": { "allow": [ "Read(*)", "Write(src/**, tests/**)", "Bash(pytest:*, ruff:*, mypy:*, git:status, git:diff)" ], "deny": [ "Bash(rm -rf:*, git:push, git:commit)", "Write(.github/**, .env*)" ] }, "hooks": { "pre_tool_use": ["validate_tool_call"], "post_tool_use": ["log_tool_execution"] }}AI-First Engineering Framework — Baseline v6.5