Integracion CI/CD

Guia completa para integrar intake en pipelines de integracion continua. Incluye ejemplos para GitHub Actions, GitLab CI, Jenkins y Azure DevOps.


Estrategia general

Que ejecutar en CI

AccionCuandoComando
Verificar specEn cada PR / pushintake verify specs/<spec>/ -p . -f junit
Exportar specAl mergear a mainintake export specs/<spec>/ -f <formato> -o .
Detectar driftProgramado (semanal)Comparar hashes de spec.lock.yaml con fuentes
Doctor checkProgramado / manualintake doctor
FeedbackCuando verify fallaintake feedback specs/<spec>/ -p .

Exit codes

CodigoSignificadoAccion CI
0Todos los checks requeridos pasaronJob pasa
1Al menos un check requerido falloJob falla
2Error de ejecucionJob falla (investigar)

GitHub Actions

Action oficial de intake (recomendado)

intake incluye una GitHub Action oficial (action/action.yml) que simplifica la integracion. La action instala intake, ejecuta verificacion, genera reportes y sube artefactos automaticamente.

name: Verify Spec
on: [push, pull_request]

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Verify spec compliance
        uses: Diego303/intake-cli/action@main
        with:
          spec-dir: specs/mi-feature/

Inputs

InputDefaultDescripcion
spec-dir(requerido)Path al directorio de la spec
project-dir.Directorio del proyecto contra el cual verificar
report-formatjunitFormato del reporte: terminal, json, junit
report-outputintake-verify-report.xmlPath del archivo de reporte
tags""Tags para filtrar checks (separados por coma)
fail-fastfalseDetener en el primer fallo
python-version3.12Version de Python
intake-versionlatestVersion de intake (ej: >=0.4.0)

Outputs

OutputDescripcion
resultResultado: pass, fail, o error
total-checksTotal de checks ejecutados
passed-checksChecks que pasaron
failed-checksChecks que fallaron
report-pathPath al reporte generado

Ejemplo avanzado con outputs

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Verify spec compliance
        id: verify
        uses: Diego303/intake-cli/action@main
        with:
          spec-dir: specs/mi-feature/
          tags: "api,security"
          fail-fast: "true"

      - name: Comment on PR
        if: failure()
        run: |
          echo "Spec verification failed: ${{ steps.verify.outputs.failed-checks }} checks failed"

Verificacion manual en PR

Si prefieres configurar los steps manualmente sin la action oficial:

name: Verify Spec Compliance
on: [push, pull_request]

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Cache pip packages
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-intake

      - name: Install intake
        run: pip install intake-ai-cli

      - name: Run acceptance checks
        run: intake verify specs/mi-feature/ -p . -f junit > test-results.xml

      - name: Upload test results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: spec-verification
          path: test-results.xml

      - name: Publish test results
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: Spec Compliance
          path: test-results.xml
          reporter: java-junit

Workflow completo: verificar + exportar

name: Spec Pipeline
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install intake-ai-cli
      - run: intake verify specs/mi-feature/ -p . -f junit > test-results.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: test-results.xml

  export:
    needs: verify
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install intake-ai-cli
      - run: intake export specs/mi-feature/ -f claude-code -o .
      - uses: actions/upload-artifact@v4
        with:
          name: exported-spec
          path: |
            CLAUDE.md
            .intake/

Deteccion de spec drift (scheduled)

name: Spec Drift Detection
on:
  schedule:
    - cron: "0 9 * * 1"  # Lunes a las 9am

jobs:
  check-drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install intake-ai-cli

      - name: Check for stale specs
        id: drift
        run: |
          OUTPUT=$(intake show specs/mi-feature/ 2>&1)
          if echo "$OUTPUT" | grep -qi "stale\|changed"; then
            echo "drift=true" >> $GITHUB_OUTPUT
          else
            echo "drift=false" >> $GITHUB_OUTPUT
          fi

      - name: Create issue if drift detected
        if: steps.drift.outputs.drift == 'true'
        run: |
          gh issue create \
            --title "Spec drift detected: mi-feature" \
            --body "Las fuentes de la spec han cambiado. Regenerar con: \`intake add specs/mi-feature/ --regenerate\`" \
            --label "spec-drift"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Multiples specs en paralelo

jobs:
  verify:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        spec: [api-auth, api-payments, frontend-dashboard]
      fail-fast: false
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install intake-ai-cli
      - run: intake verify specs/${{ matrix.spec }}/ -p . -f junit > results-${{ matrix.spec }}.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: results-${{ matrix.spec }}
          path: results-${{ matrix.spec }}.xml

GitLab CI

Pipeline basico

# .gitlab-ci.yml
stages:
  - verify

verify-spec:
  stage: verify
  image: python:3.12-slim
  before_script:
    - pip install intake-ai-cli
  script:
    - intake verify specs/mi-feature/ -p . -f junit > test-results.xml
  artifacts:
    when: always
    reports:
      junit: test-results.xml

GitLab muestra automaticamente los resultados JUnit en el widget de Merge Request.

Pipeline completo con cache

stages:
  - verify
  - export

.intake-base:
  image: python:3.12-slim
  cache:
    key: intake-pip
    paths:
      - .cache/pip
  variables:
    PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  before_script:
    - pip install intake-ai-cli

verify-spec:
  extends: .intake-base
  stage: verify
  script:
    - intake verify specs/mi-feature/ -p . -f junit > test-results.xml
  artifacts:
    when: always
    reports:
      junit: test-results.xml

export-spec:
  extends: .intake-base
  stage: export
  only:
    - main
  script:
    - intake export specs/mi-feature/ -f claude-code -o .
  artifacts:
    paths:
      - CLAUDE.md
      - .intake/

Jenkins

Jenkinsfile declarativo

pipeline {
    agent {
        docker {
            image 'python:3.12-slim'
        }
    }

    environment {
        ANTHROPIC_API_KEY = credentials('anthropic-api-key')
    }

    stages {
        stage('Install') {
            steps {
                sh 'pip install intake-ai-cli'
            }
        }

        stage('Verify') {
            steps {
                sh 'intake verify specs/mi-feature/ -p . -f junit > test-results.xml'
            }
            post {
                always {
                    junit 'test-results.xml'
                }
            }
        }

        stage('Export') {
            when {
                branch 'main'
            }
            steps {
                sh 'intake export specs/mi-feature/ -f claude-code -o .'
                archiveArtifacts artifacts: 'CLAUDE.md,.intake/**'
            }
        }
    }
}

Jenkinsfile con multiples specs

pipeline {
    agent any
    stages {
        stage('Verify All Specs') {
            parallel {
                stage('API Auth') {
                    steps {
                        sh 'intake verify specs/api-auth/ -p . -f junit > results-auth.xml'
                    }
                }
                stage('API Payments') {
                    steps {
                        sh 'intake verify specs/api-payments/ -p . -f junit > results-payments.xml'
                    }
                }
            }
            post {
                always {
                    junit 'results-*.xml'
                }
            }
        }
    }
}

Azure DevOps

azure-pipelines.yml

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: UsePythonVersion@0
    inputs:
      versionSpec: '3.12'

  - script: pip install intake-ai-cli
    displayName: 'Install intake'

  - script: intake verify specs/mi-feature/ -p . -f junit > test-results.xml
    displayName: 'Verify spec compliance'

  - task: PublishTestResults@2
    condition: always()
    inputs:
      testResultsFormat: 'JUnit'
      testResultsFiles: 'test-results.xml'
      testRunTitle: 'Spec Compliance'

Formatos de reporte para CI

JUnit XML

El formato mas compatible con CI. Todos los sistemas lo soportan:

intake verify specs/mi-feature/ -p . -f junit > test-results.xml
PlataformaComo se consume
GitHub Actionsdorny/test-reporter action o actions/upload-artifact
GitLab CIartifacts.reports.junit (aparece en MR widget)
Jenkinsjunit post step (aparece en dashboard)
Azure DevOpsPublishTestResults@2 task

JSON

Para integraciones personalizadas:

intake verify specs/mi-feature/ -p . -f json > results.json

Extraer metricas con jq:

# Total de checks
jq '.total_checks' results.json

# Solo nombres de checks fallidos
jq '.results[] | select(.passed == false) | .name' results.json

# Resumen: passed/failed
jq '{passed: .passed, failed: .failed, all_ok: .all_required_passed}' results.json

Pre-commit hooks

Ver Despliegue > Pre-commit hooks para configuracion detallada.

Resumen rapido:

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: intake-verify
        name: Verify spec compliance
        entry: intake verify specs/mi-feature/ -p . --fail-fast -t structure
        language: system
        pass_filenames: false
        files: ^src/

Deteccion de spec drift

Que es

“Spec drift” ocurre cuando las fuentes de requisitos cambian pero la spec no se regenera. El spec.lock.yaml almacena hashes SHA-256 de las fuentes originales, permitiendo detectar cambios.

Cuando verificar

  • Semanal (cron job): detectar drift por fuentes que cambian sin regenerar
  • En PR: si un PR modifica una fuente listada en spec.lock.yaml, alertar que la spec puede estar desactualizada

Implementacion

El patron general:

# 1. intake show detecta staleness automaticamente
intake show specs/mi-feature/

# 2. Si las fuentes cambiaron, regenerar
intake add specs/mi-feature/ --regenerate

# 3. Revisar cambios
intake diff specs/mi-feature-old/ specs/mi-feature/

Ver los ejemplos de GitHub Actions y GitLab CI arriba para implementaciones en CI.


Notificaciones

Slack (webhook)

# GitHub Actions: notificar en Slack si verify falla
- name: Notify Slack on failure
  if: failure()
  run: |
    curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
      -H "Content-Type: application/json" \
      -d '{
        "text": "Spec verification failed for mi-feature",
        "blocks": [
          {
            "type": "section",
            "text": {
              "type": "mrkdwn",
              "text": "*Spec Verification Failed*\nSpec: `mi-feature`\nBranch: `${{ github.ref_name }}`\nPR: ${{ github.event.pull_request.html_url }}"
            }
          }
        ]
      }'

Microsoft Teams (webhook)

- name: Notify Teams on failure
  if: failure()
  run: |
    curl -X POST "${{ secrets.TEAMS_WEBHOOK }}" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "Spec Verification Failed",
        "text": "La spec mi-feature no paso los checks de aceptacion en la branch ${{ github.ref_name }}"
      }'

Tips

Cache de dependencias

Cachear pip reduce ~30 segundos por ejecucion:

# GitHub Actions
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-intake-${{ hashFiles('**/requirements*.txt') }}

# GitLab CI
cache:
  key: intake-pip
  paths:
    - .cache/pip

verify.sh sin instalar intake

El exporter generic genera un verify.sh standalone. En CI donde no quieres instalar intake:

# Una vez: generar verify.sh y commitearlo
intake export specs/mi-feature/ -f generic -o output/
git add output/verify.sh
git commit -m "Add standalone verify script"

# En CI: ejecutar directamente (sin intake)
chmod +x output/verify.sh
./output/verify.sh .

Tradeoff: menos flexible que intake verify (sin tags, sin JUnit), pero zero dependencias.

Verificacion solo de estructura

Para checks rapidos en pre-commit, ejecutar solo checks de estructura (sin tests):

intake verify specs/mi-feature/ -p . -t structure --fail-fast