Entornos y despliegue
El panel es una SPA de Vite desplegada en Vercel, más una función serverless para los reportes. El código entra porstaging y llega a producción con una PR de staging a main.
Esta página cubre los tres entornos, cómo se construye cada uno, qué hacen los workflows de GitHub y cómo el panel recoge un deploy nuevo sin romper las sesiones abiertas.
Entornos
El entorno lo decideVITE_APP_ENV. src/config/env.ts lo valida con Zod al arrancar y solo acepta tres valores:
Los detalles de Sentry y PostHog por entorno están en Observabilidad.
Scripts
Cada modo de Vite carga su
.env.<modo> (.env.localdev, .env.staging, .env.prod). Todos los .env* están en .gitignore. Para montar el tuyo, sigue el inicio rápido.
Variables de entorno
Nunca pongas valores en la wiki ni en el repo. Estos son solo los nombres:env.template no incluye las variables de PostHog ni las de Slack. Las de PostHog aparecen en src/config/env.ts. Las de Slack solo existen en el entorno de Vercel.Flujo de ramas
- Crea tu rama desde
staging. En el historial verás prefijos comofeat/,fix/,chore/yrefactor/, normalmente con el área del panel (feat/feedback/report-images). También hay ramas generadas desde Linear (<usuario>/viv-XXXX-...). - Abre la PR contra
staging. El título sigue Conventional Commits y lleva el ID de Linear:feat(feedback): attach screenshots to panel reports (VIV-2787). - Para publicar, abre una PR de
stagingamain. Cada merge amaines un deploy de producción.
Vercel
vercel.json define dos cosas.
Rewrite SPA. Cualquier ruta que no empiece por api/ y no contenga un punto se sirve con /index.html. Así React Router resuelve las rutas del panel. Los archivos estáticos (/version.json, /assets/...) se sirven tal cual. La exclusión de api/ deja pasar las peticiones a la función de reportes.
Redirects temporales (permanent: false) para enlaces profundos del panel antiguo:
Función serverless
api/reports.ts se despliega como función de Vercel junto a la SPA. La documentación completa está en Reportes. Dos detalles de build:
tsconfig.api.jsontypecheaapi/ysrc/core/feedback/. Está referenciado desdetsconfig.json, así quetsc -blo incluye.- Los imports de
api/llevan la extensión.jsexplícita (./_lib/auth.js). El runtime de la función lo necesita. Si añades un import sin extensión, la función falla en Vercel aunque el typecheck pase.
Nombre de la release
vite.config.ts calcula el nombre de la release como panel-front@<sha>. El SHA sale de VERCEL_GIT_COMMIT_SHA. Si no existe, usa git rev-parse HEAD, y si eso falla, unknown. Ese nombre se usa en tres sitios:
- Se inyecta en la app como la constante global
__APP_RELEASE__. - Se escribe en
version.jsonen la raíz del build. - Se usa como release de Sentry al subir los source maps.
Workflows de GitHub
CI (ci.yml)
Corre en cada PR y en cada push a staging y main. Un push nuevo a la misma rama cancela la ejecución anterior.
Pasos, con Node 20:
yarn install --frozen-lockfilenpx tsc -bnpx eslint .npx vite build, el mismo build genérico que ejecuta Vercel
tsc.
Notificación de deploy (deploy-notify.yml)
Corre en cada push a main y avisa en Slack cuando producción sirve el commit nuevo.
1
Esperar a producción
Consulta
/version.json en los dos dominios de producción (panel.vivla.com y panel.vivla.dev) cada 10 segundos, hasta 60 veces. Termina cuando alguno devuelve panel-front@<sha del push>.2
Montar la lista de cambios
Lee con
git log --merges los cuerpos de los merge commits entre el main anterior y el nuevo, es decir, los títulos de las PRs. Si hay más de uno, descarta el primero, que es la propia PR de staging a main. Se queda con un máximo de 10. Si no puede calcular el rango, usa la primera línea del mensaje del commit.3
Publicar
Envía el mensaje con el webhook del secret
SLACK_WEBHOOK_URL. Si el secret no existe, se salta el paso sin fallar.- Verde si producción sirvió el commit. Rojo si no lo hizo en 10 minutos.
- Campos Commit, Author e Incluye.
- Enlaces al panel, a la release en Sentry y a la ejecución del workflow.
El workflow consulta los dos dominios porque el panel está en transición hacia
panel.vivla.com. Si ninguno sirve el commit, el enlace Panel apunta al último dominio que respondió, o a panel.vivla.com si no respondió ninguno.CX Assistant (cx-assistant.yml)
Permite que alguien del equipo de CX pida un cambio pequeño en el panel y reciba una PR generada por Claude Code. Solo se lanza a mano con workflow_dispatch. No reacciona a pushes ni a PRs.
Recibe cuatro entradas: issue_number (issue de GitHub de la solicitud), prompt (el cambio pedido), requester (email de quien lo pide) y base_branch (por defecto main).
1
Marcar el issue
Quita la etiqueta
cx-request del issue y pone cx-processing.2
Crear el issue en Linear
Crea un issue en Linear con el prompt y un enlace al issue de GitHub. Deja un comentario de “procesando”. Si Linear falla, solo emite un aviso y sigue.
3
Generar el cambio
Crea la rama
cx-assistant/<issue> desde base_branch y ejecuta anthropics/claude-code-action. Las herramientas están limitadas: editar, escribir, leer y buscar archivos, más git diff, git status y git log. El prompt pide cambios mínimos y que se respeten las convenciones del repo.4
Abrir la PR o avisar
Si hay cambios, hace commit, abre la PR
[CX Assistant] #<issue> contra base_branch y etiqueta el issue con cx-pr-created. Si no hay cambios, etiqueta con cx-no-changes. Si algo falla, con cx-failed. En los tres casos comenta en el issue de GitHub y en el de Linear.ANTHROPIC_API_KEY y LINEAR_API_KEY. Las PRs siempre necesitan revisión de un desarrollador antes del merge.
Recarga tras un deploy
Los chunks de la SPA llevan hash. Cada deploy borra los anteriores, y una pestaña abierta con la versión vieja rompe al navegar a una ruta que aún no había cargado. El panel lo resuelve con dos mecanismos. Ninguno muestra un aviso: los dos recargan la página en silencio.Recarga en la siguiente navegación (useVersionCheck)
PortalRoot monta useVersionCheck, que consulta /version.json con cache: "no-store":
- al volver a la pestaña (
focusyvisibilitychange), - cada 5 minutos.
__APP_RELEASE__, marca que hay una versión nueva. En el siguiente cambio de ruta hace window.location.reload(). Nunca recarga en mitad de lo que estás haciendo: espera a que navegues.
El chequeo solo corre dentro del área autenticada, donde está
PortalRoot. Solo reacciona a cambios de pathname, no de query string. En yarn dev no hay version.json (se genera solo en el build), así que no hace nada.Recuperación de chunks (lazyWithReload)
Todas las páginas del router se cargan con lazyWithReload en vez de React.lazy. Si el import() de un chunk falla:
- La primera vez guarda una marca en
sessionStoragey recarga la página. - Si tras recargar el chunk carga bien, borra la marca y envía a Sentry el mensaje informativo “Recovered from stale chunk after deploy”.
- Si vuelve a fallar con la marca puesta, la borra y relanza el error. Lo recoge el
RouteErrorBoundary.
Anuncio del release
La skill de Claude Code.claude/skills/product-release no despliega nada. Te ayuda a redactar el anuncio en Slack de lo que acabas de publicar: qué cambia, a quién afecta y si hay que hacer algo. Parte de la plantilla del handbook de producto y devuelve el mensaje listo para pegar, con un checklist previo a la publicación.