raise-commons CI/CD
Arquitectura

Modelo de Branches

Flujo de tres niveles. Las ramas de historia/bug se crean desde release/3.1.0 y vuelven a ella via MR. La rama main solo recibe merges de release.

Jerarquía de branches
flowchart TD
    S["story/sN.M/slug\nbug/RAISE-N/slug"]
    R["release/3.1.0\ndesarrollo activo · MR target"]
    M["main\nestable · solo merges de release"]

    S -->|"MR → merge"| R
    R -->|"merge en release"| M
    M -.->|"hotfix"| R
Branch Rol Pipeline disparado Merge target
main Estable — tag de producción vive aquí Full suite (legacy config)
release/3.1.0 Desarrollo activo. Todos los MRs apuntan aquí Fast MR + Full + Deploy a Dev main
story/sN.M/slug Feature / historia de sprint Fast MR al crear MR release/3.1.0
bug/RAISE-N/slug Bugfix Fast MR al crear MR release/3.1.0
ambiente-qa-gcp Branch dedicado a QA en GCP Cloud Run Build + deploy a GCP QA release/3.1.0
Integración Continua

Perfiles de CI

Dos modos de operación según el contexto: un fast path para MRs (~3–5 min) y una suite completa para pre-release. Los jobs de seguridad están temporalmente deshabilitados.

FAST MR Fast Path

Corre en todo MR hacia release/*. Excluye tests lentos, de integración, ML y E2E. Usa -n 4 (paralelo) y --no-cov.

JobDuraciónShards
test:raise-cli-fast~3.5 min×3
test:raise-core~1 min×1
test:raise-server-fast~1 min×1
test:raise-cli-no-xdist~1 min×1
check-migrations~30 s×1
test:docs-guards~1 min×1
FULL Pre-Release

Corre en push directo a release/3.1.0, schedules o manual desde release/*. Incluye tests lentos e integración.

JobMarkers incluidos
test:raise-cli-fullTodo excepto ml, e2e, no_xdist
test:raise-server-fullTodo excepto tests/e2e/
test:raise-cli-recall-gateSolo ml — serial, ONNX model

Markers de pytest y cobertura

MarkerQué contieneFast MRFull
slow Tests de carga, ciclos largos, init de grafo EXCLUIDO INCLUIDO
integration Tests que requieren Jira/Confluence real o SQLite con datos EXCLUIDO INCLUIDO
ml Tests que requieren modelo ONNX multilingual-e5 EXCLUIDO SEPARADO recall-gate
e2e Flujos CLI completos y E2E del servidor EXCLUIDO SOLO deployed
no_xdist Tests que no pueden correr en paralelo (xdist incompatible) JOB PROPIO JOB PROPIO

Estrategia de Cache

PerfilPolicyKeyPaths
Fast MR pull $PYTHON_ABI-$JOB_NAME + uv.lock .uv-cache/
Full pre-release pull-push Mismo prefix que fast (compartido) .uv-cache/
Nota sobre prefijos de cache: Al renombrar jobs, el prefix histórico debe mantenerse. test:raise-cli-fast y test:raise-cli-full comparten el prefix test:raise-cli. Un rename sin mantener el prefix crea una cache nueva y provoca regresiones por cache miss.
Ambiente

Dev — Fly.io (raise-server.fly.dev)

Primer ambiente de integración real. Se despliega al crear un tag dev. Corre smoke + E2E deployed antes de poder promover a producción.

Dev / Staging Fly.io
https://raise-server.fly.dev
App name raise-server
Org aquiles-lazaro
Region lax (Los Angeles)
VM size shared-cpu-1x · 512 MB
Strategy rolling
RAI_ENV staging
GitLab env dev
Runner jinkoniwashi
Trigger
Formato de tag requerido
git tag v3.1.0-dev.20260821
git push origin v3.1.0-dev.20260821

El tag dispara el job deploy:dev que usa fly.staging.toml. Requiere que pasen test:raise-server-full y guard:tag-ancestry primero.

Configuración Fly.io (fly.staging.toml)

/fly.staging.toml — valores clave
app = 'raise-server'
primary_region = 'lax'

[deploy]
  release_command = "sh -c 'cd /app/packages/raise-server && alembic upgrade head'"
  # Migración en máquina efímera ANTES del corte (ADR-093)
  # Sin esto → drift schema↔código (patrón de error RAISE-11462)

[env]
  RAI_ENV     = 'staging'
  RAI_LOG_LEVEL = 'INFO'
  RAI_PORT    = '8080'
  # RAI_DATABASE_URL — Fly secret (fly postgres attach)

[http_service]
  internal_port = 8080
  force_https   = true
  auto_stop_machines = 'off'   # nunca hibernar en staging
  min_machines_running = 1

  [[http_service.checks]]
    interval     = '15s'
    timeout      = '5s'
    grace_period = '45s'       # buffer para alembic en cold start
    method       = 'GET'
    path         = '/health'

[[vm]]
  size   = 'shared-cpu-1x'
  memory = '512mb'

Smoke Check en Deploy

CheckEndpointCriterioTimeout
HealthGET /health{"status":"ok"}90 s
DatabaseGET /health{"database":"connected"}
VersionGET /healthversion presente
API v2GET /api/v2/projectsHTTP ≠ 000
Migration driftflyctl ssh consolealembic current == heads
E2E después del smoke: El job e2e:dev corre contra raise-server.fly.dev con tests marcados @pytest.mark.deployed en packages/raise-server/tests/e2e/deployed/. Falla → bloquea promoción a producción.
Ambiente

QA — GCP Cloud Run

Ambiente de validación en Google Cloud Platform. Ya está parcialmente operativo — es la base del plan de migración. Trigger: push a la branch dedicada ambiente-qa-gcp.

Ya está en producción (parcial): Los jobs build:gcp-qa, deploy:gcp-qa y smoke:gcp-qa están activos en .gitlab-ci.yml. El QA de Cloud Run ya existe y ya funciona — esta sección describe exactamente cómo.
QA — GCP Cloud Run
raise-server-qa-333073224212.northamerica-south1.run.app
Service raise-server-qa
Project vaultwarden-server-485620
Region northamerica-south1 (MX)
Registry Artifact Registry
Build Kaniko (no Docker daemon)
Migración Cloud Run Job (manual)
GitLab env qa/gcp
Trigger
Branch push
git push origin ambiente-qa-gcp

Automático en push. No requiere tag. La migración de BD (migrate:gcp-qa) es manual — se ejecuta desde GitLab CI.

Flujo de Build — Kaniko

build:gcp-qa — comandos efectivos
# Autenticación en Artifact Registry (sin Docker daemon)
echo '{"auths":{"northamerica-south1-docker.pkg.dev": {"auth":"..."}}}' \
  > /kaniko/.docker/config.json

# Build multi-stage (target=server) + push con dos tags
/kaniko/executor \
  --context "${CI_PROJECT_DIR}" \
  --dockerfile "${CI_PROJECT_DIR}/Dockerfile" \
  --target server \
  --build-arg "GIT_SHA=${CI_COMMIT_SHA}" \
  --build-arg "BUILD_DATE=$(date -u +%F)" \
  --destination "northamerica-south1-docker.pkg.dev/vaultwarden-server-485620/raise-docker-qa/raise-server:${CI_COMMIT_SHORT_SHA}" \
  --destination "northamerica-south1-docker.pkg.dev/vaultwarden-server-485620/raise-docker-qa/raise-server:latest" \
  --cache=true \
  --skip-unused-stages

Migración de Base de Datos (Cloud Run Job)

La migración es MANUAL: El job migrate:gcp-qa tiene when: manual. Hay que ejecutarlo explícitamente desde el pipeline de GitLab antes (o después) del deploy, según sea necesario. Esto es intencional para QA — en el ambiente de producción se deberá automatizar con ordenamiento de jobs (como lo hace Fly.io con release_command).
migrate:gcp-qa — script efectivo
# Autenticar con service account
echo "$GCP_SA_KEY_QA" | gcloud auth activate-service-account --key-file=-
gcloud config set project vaultwarden-server-485620

# Actualizar imagen del migration job
gcloud run jobs update raise-migrate-qa \
  --image "$GCP_REGISTRY/raise-server:$CI_COMMIT_SHORT_SHA" \
  --command "sh,-c,cd /app/packages/raise-server && alembic upgrade head" \
  --region northamerica-south1 --quiet

# Ejecutar y esperar a que termine
gcloud run jobs execute raise-migrate-qa \
  --region northamerica-south1 --wait --quiet

Deploy a Cloud Run

deploy:gcp-qa — script efectivo
gcloud run deploy raise-server-qa \
  --image "$GCP_REGISTRY/raise-server:$CI_COMMIT_SHORT_SHA" \
  --region northamerica-south1 \
  --platform managed \
  --quiet
Ambiente

Producción — Fly.io (raise-server-prod.fly.dev)

Ambiente de producción. Solo se despliega con tags de release (RC o final) y requiere aprobación manual en GitLab antes de ejecutar el deploy.

Producción Fly.io
https://raise-server-prod.fly.dev
App name raise-server-prod
Org aquiles-lazaro
Region lax (Los Angeles)
VM size shared-cpu-1x · 2048 MB
Strategy rolling (CLI override)
RAI_ENV production
Log level WARNING
Runner docker
Triggers (MANUAL gate)
Tags que habilitan el job
# Release Candidate
git tag v3.1.0-rc.1

# Release Final
git tag v3.1.0

El job tiene when: manual. El tag crea el pipeline, pero un humano debe aprobar el deploy desde la UI de GitLab.

Discrepancia TOML vs CLI: fly.production.toml define strategy = 'bluegreen' en la sección [deploy], pero el job CI pasa --strategy rolling en la línea de comando. El flag CLI tiene precedencia — el deploy efectivo de producción usa rolling, no bluegreen. Esto debe resolverse antes de la migración: decidir cuál es la estrategia real deseada.

Configuración Fly.io (fly.production.toml)

/fly.production.toml — valores clave
app = 'raise-server-prod'
primary_region = 'lax'

[deploy]
  release_command = "sh -c 'cd /app/packages/raise-server && alembic upgrade head'"
  strategy = 'bluegreen'   # ⚠ SOBREESCRITO por --strategy rolling en CI

[env]
  RAI_ENV             = 'production'
  RAI_LOG_LEVEL       = 'WARNING'
  RAI_PORT            = '8080'
  RAI_FEATURE_CARTRIDGES = 'off'   # kill-switch de feature flags (ADR-093)
  # RAI_DATABASE_URL  → Fly secret

[http_service]
  internal_port = 8080
  force_https   = true
  auto_stop_machines = 'off'
  min_machines_running = 1

  [[http_service.checks]]
    interval     = '15s'
    timeout      = '5s'
    grace_period = '45s'
    method       = 'GET'
    path         = '/health'

[[vm]]
  size   = 'shared-cpu-1x'
  memory = '2048mb'          # 4× más que staging
Ambiente

Admin — Cloudflare Pages

El frontend raise-admin (SPA Vite/React) se despliega a Cloudflare Pages. Existen tres variantes: dev automático, QA (GCP) y producción manual.

VarianteURLTriggerBackendGate
Dev raise-admin-dev.pages.dev Push release/3.1.0 con cambios en packages/raise-admin/ raise-server.fly.dev AUTO
QA Cloudflare Pages QA project Push ambiente-qa-gcp con cambios en admin raise-server-qa-333...run.app AUTO
Producción raise.sh Push release/3.1.0 con cambios en admin api.raise.sh MANUAL
Flujos

Diagramas de Deploy

Secuencia completa de jobs según el trigger. Cada flujo es independiente — los triggers no se solapan.

Flujo 1 — MR Fast Path

Corre en todo MR hacia release/*. Sólo test stage — sin build ni deploy.

Trigger: MR → release/*
flowchart LR
    trigger["MR → release/*"]
    trigger --> t1["test:raise-cli-fast\n×3 shards paralelos"]
    trigger --> t2["test:raise-core"]
    trigger --> t3["test:raise-server-fast"]
    trigger --> t4["test:raise-cli-no-xdist"]
    trigger --> t5["check-migrations"]
    trigger --> t6["test:docs-guards"]
    t1 & t2 & t3 & t4 & t5 & t6 --> done["MR verde\n~3–5 min"]

Flujo 2 — Push a release/3.1.0

Corre la suite full + si hay cambios en archivos relevantes, despliega a Dev (Fly.io).

Trigger: push directo a release/3.1.0
flowchart TD
    push["push → release/3.1.0"]
    push --> full["test:raise-cli-full\ntest:raise-server-full\ntest:raise-cli-recall-gate"]
    push --> guard["check-migrations\nbundle-guard:customui"]
    push --> admin["build:admin → deploy:admin-dev\n(si cambia packages/raise-admin)"]
    push --> docs["docs-deploy\n(si cambian ADRs / portal)"]

Flujo 3 — Tag Dev → Fly.io

Tag v*.*.*-dev.YYYYMMDD. El primer ambiente con deploy real. E2E al final bloquea la promoción.

Trigger: git tag v3.1.0-dev.YYYYMMDD
flowchart LR
    tag["git tag\nv3.1.0-dev.YYYYMMDD"]
    tag --> A["test:raise-server-full"]
    tag --> B["guard:tag-ancestry"]
    tag --> C["build:onnx-model\nallow_failure: true"]
    A & B & C --> D["deploy:dev\nfly.staging.toml\napp: raise-server\nstrategy: rolling\nrunner: jinkoniwashi"]
    D --> E["smoke:dev\n/health · /api/v2/projects\nalembic drift · 90s"]
    E --> F["e2e:dev\ndeployed tests"]

Flujo 4 — Tag RC/Release → Producción Fly.io

Tag de release. El deploy requiere aprobación manual en GitLab. No hay E2E post-production.

Trigger: git tag v3.1.0-rc.N o v3.1.0
flowchart LR
    tag["git tag v3.1.0-rc.N\no v3.1.0"]
    tag --> A["test:raise-server-full"]
    tag --> B["guard:tag-ancestry"]
    tag --> C["build:onnx-model\nallow_failure: true"]
    A & B & C --> GATE["MANUAL\naprobación en GitLab"]
    GATE --> D["deploy:production\nfly.production.toml\napp: raise-server-prod\nstrategy: rolling\nrunner: docker"]
    D --> E["smoke:production\n/health · 90s"]

Flujo 5 — Branch GCP QA

Push a ambiente-qa-gcp. Build con Kaniko, migración manual, deploy a Cloud Run.

Trigger: push a ambiente-qa-gcp
flowchart LR
    push["push → ambiente-qa-gcp"]
    push --> O["build:onnx-model\nallow_failure: true"]
    push --> AB["build:admin-qa\nCloudflare Pages QA"]
    O --> K["build:gcp-qa\nKaniko → Artifact Registry\nnortamerica-south1"]
    K --> M["migrate:gcp-qa\nMANUAL\nCloud Run Job"]
    K --> D["deploy:gcp-qa\ngcloud run deploy\nraise-server-qa"]
    D --> S["smoke:gcp-qa\n/health · 120s"]
    AB --> DAD["deploy:admin-qa\nCloudflare Pages"]
Infraestructura

Dockerfile y Stack de Servicios

Un único Dockerfile multi-target para todo el monorepo. Estrategia de capas ordenada por frecuencia de cambio para optimizar la cache de Kaniko.

Targets del Dockerfile

TargetQué despliegaPuertoEntrypoint
server raise-server — API REST del knowledge graph 8080 uvicorn raise_server.app:app
daemon rai-agent — Telegram bot + pipeline engine 8000 python -m rai_agent.daemon

Estrategia de Capas (Layer Cache)

CapaContenidoFrecuencia de cambio
BasePython 3.13-slim + uv + usuario raiMuy baja (imagen base)
Layer 1Todos los pyproject.toml + uv.lockBaja (solo con deps)
Layer 2Third-party deps vía uv sync --no-install-workspaceBaja (solo con deps)
Layer 3Código fuente (packages/)Alta (cada commit)
Layer 4Link de workspace packages en venvAlta (~5s, siempre reconstruye)

Apps Fly.io en el ecosistema

AppURLStackConfig
raise-server raise-server.fly.dev raise-server (Python/FastAPI) fly.staging.toml
raise-server-prod raise-server-prod.fly.dev raise-server (Python/FastAPI) fly.production.toml
raise-pmo Caddy + hermes-gateway + commitment-server + litellm infra/fly.toml (compose)

Patrón de Migración DB (crítico para GCP)

El patrón de Fly.io ejecuta Alembic antes del corte de tráfico en una máquina efímera. En GCP QA esto se hace manualmente con un Cloud Run Job. Para producción en GCP, el Job debe ejecutarse y esperar éxito antes del gcloud run deploy.

Fly.io (actual)
release_command en TOML
release_command = "sh -c 'cd \
/app/packages/raise-server \
&& alembic upgrade head'"
# ↑ Corre en máquina efímera.
# Si falla → deploy cancelado.
# Tráfico nunca se corta.
GCP Cloud Run (objetivo)
Patrón Job → Deploy
gcloud run jobs execute \
  raise-migrate-prod \
  --region northamerica-south1 \
  --wait --quiet         # ← bloqueante
# Si exit != 0 → abortar.
# Solo después:
gcloud run deploy raise-server-prod \
  ...
Variables CI/CD

Secrets y Variables

Variables configuradas en GitLab Settings → CI/CD → Variables. Las marcadas como Protected solo están disponibles en branches/tags protegidos.

VariableUsado porTipoScope
FLY_API_TOKEN_STAGING deploy:dev, smoke:dev Masked + Protected Tag v*.*.*-dev.*
FLY_API_TOKEN_PRODUCTION deploy:production, smoke:production Masked + Protected Tag v*.*.*
GCP_SA_KEY_QA build:gcp-qa, migrate:gcp-qa, deploy:gcp-qa Masked + Protected Branch ambiente-qa-gcp
CLOUDFLARE_API_TOKEN deploy:admin-* Masked release/3.1.0
CLOUDFLARE_ACCOUNT_ID deploy:admin-* Variable release/3.1.0
ADMIN_QA_VITE_API_URL build:admin-qa Variable ambiente-qa-gcp
RAISE_ALPHA_DEPLOY_TOKEN nightly:publish:public Masked Schedule
RAISE_CI_PYTHON_IMAGE Todos los jobs de test Variable Global
RAI_DATABASE_URL raise-server (en Fly.io como secret) Fly secret Fly.io secrets — NO en GitLab CI
RAI_DATABASE_URL no vive en GitLab CI — es un Fly.io secret, inyectado via fly postgres attach. Para GCP, debe configurarse como variable de entorno en Cloud Run o como Secret Manager ref.
Contexto

Deuda Técnica — Jobs Deshabilitados

Seis jobs de seguridad fueron comentados en .gitlab-ci.yml durante la fase de desarrollo activo de v3 para mantener ciclos de iteración cortos. La migración a GCP es una oportunidad para reactivarlos.

JobQué detectaRiesgo sin él
snyk:sca CVEs en dependencias (requirements, uv.lock) ALTO — dependencias con vulnerabilidades conocidas pasan silenciosamente
snyk:sast Vulnerabilidades en código fuente (SQL injection, path traversal, etc.) ALTO — código nuevo no auditado por seguridad
snyk:iac Misconfigurations en archivos IaC (Dockerfile, fly.toml, etc.) MEDIO — configuraciones inseguras en infra no detectadas
sonarqube:scan Calidad de código + seguridad estática acumulada MEDIO — sin métricas de calidad histórica ni technical debt tracking
GitLab SAST template SAST adicional via GitLab built-in BAJO — redundante con Snyk SAST, pero capa adicional
Dependency Scanning template Vulnerabilidades en dependencias vía GitLab built-in BAJO — redundante con Snyk SCA
La config legacy en main (pipeline para branch dev) tiene Snyk y SonarQube activos — es la referencia de lo que debería correr cuando se reactiven. Para una primera reactivación, allow_failure: true permite visibilidad sin bloquear el CI.
Migración

Referencia para Migración a GCP

Lo que ya existe en GCP, lo que hace falta replicar, y los patrones críticos a respetar durante la transición.

Punto de partida favorable: El pipeline de QA en GCP (ambiente-qa-gcp) ya existe y está validado. La migración consiste en replicar ese patrón para los ambientes dev y producción, y afinar los detalles operativos.

Estado actual por componente

ComponenteEstado en GCPPendiente
raise-server QA EXISTE Cloud Run, northamerica-south1
Artifact Registry EXISTE raise-docker-qa Crear repo raise-docker-dev y raise-docker-prod
Cloud Run Job (migrate) EXISTE raise-migrate-qa Crear raise-migrate-dev y raise-migrate-prod
Service Account EXISTE raise-deployer-qa Crear SA para dev y prod con scope mínimo
raise-server Dev PENDIENTE Cloud Run service raise-server-dev
raise-server Prod PENDIENTE Cloud Run service raise-server-prod + domain mapping
GitLab CI dev job PENDIENTE Replicar deploy:dev apuntando a Cloud Run en vez de Fly.io
GitLab CI prod job PENDIENTE Replicar deploy:production con manual gate y Cloud Run
Smoke post-deploy PARCIAL existe en QA (básico) Replicar el smoke completo de Fly.io (alembic drift check)
RAI_DATABASE_URL PENDIENTE Configurar en Cloud Run (env var o Secret Manager)

Patrones críticos a respetar

Migración antes del deploy

En Fly.io el release_command garantiza que Alembic corre en máquina efímera antes del corte. En GCP, el Cloud Run Job debe ejecutarse y completar con éxito antes de que gcloud run deploy enrute tráfico a la nueva imagen.

Smoke check con estructura

El smoke de Fly.io verifica: /health status:ok, database:connected, /api/v2/projects accesible, y alembic current == heads. El smoke de GCP QA actual solo verifica /health. Extender antes de migrar dev/prod.

ONNX model provisioning

El modelo ONNX se sube como artifact en el stage prepare (job build:onnx-model, allow_failure: true). El Dockerfile tiene fallback a HuggingFace Hub si el artifact llega vacío. Este patrón ya funciona en build:gcp-qa — reusarlo para dev/prod.

Manual gate en producción

El deploy a producción siempre requiere aprobación manual en GitLab (when: manual). No automatizar este gate en la migración — es un control deliberado, no una limitación técnica.

Variables de entorno a configurar en Cloud Run

VariableValor DevValor ProdFuente
RAI_ENV staging production Hardcoded en CI (como --set-env-vars)
RAI_LOG_LEVEL INFO WARNING Hardcoded en CI
RAI_PORT 8080 8080 Cloud Run ya usa 8080 por default
RAI_DATABASE_URL PostgreSQL dev PostgreSQL prod Secret Manager → Cloud Run secret ref
RAI_FEATURE_CARTRIDGES off off Kill-switch ADR-093 — mantener off hasta decisión T3

Health check en Cloud Run

Configuración equivalente al health check de Fly.io
gcloud run deploy raise-server-prod \
  --image "$GCP_REGISTRY/raise-server:$CI_COMMIT_SHORT_SHA" \
  --region northamerica-south1 \
  --platform managed \
  --port 8080 \
  --set-env-vars "RAI_ENV=production,RAI_LOG_LEVEL=WARNING" \
  --set-secrets "RAI_DATABASE_URL=raise-db-url-prod:latest" \
  # Cloud Run health check — equivale al [[http_service.checks]] de Fly.io
  # Se configura en el servicio, no en el deploy command:
  #   Startup probe: GET /health, initial delay 45s (equivale a grace_period)
  #   Liveness probe: GET /health, interval 15s, timeout 5s, failures 3
  --quiet
Nota sobre región: El QA ya está en northamerica-south1 (México) — región apropiada para el equipo. Fly.io usa lax (Los Ángeles). La latencia percibida debería mejorar para usuarios en MX con la migración a GCP México.