1. Introducción
El ecosistema Nvito implementa un sistema de calidad automatizada basado en Husky v9 que ejecuta scripts automáticamente en eventos clave de Git. Cada vez que un desarrollador hace commit, push o cambia de rama, una serie de verificaciones se ejecutan de forma transparente para garantizar que:
- El código cumple con las reglas de linting y formato establecidas.
- Los mensajes de commit siguen el estándar Conventional Commits.
- No se filtran secrets ni archivos prohibidos al repositorio.
- Los tipos de TypeScript compilan correctamente antes de publicar cambios.
- Los tests pasan antes de enviar código al remoto.
Este sistema opera en los 7 repositorios del ecosistema (nvito-api, nvito-admin, nvito-invitations, nvito-client, nvito-pwa, nvito-docs y nvito-landing) con configuraciones adaptadas al tier de cada proyecto.
2. Hooks implementados
El sistema cuenta con 6 hooks de Git que cubren todo el ciclo de vida de un cambio:
| Hook | Momento | Verificaciones | Tiempo |
|---|---|---|---|
pre-commit | Antes de crear el commit | lint-staged (ESLint + Prettier) + secretlint + archivos prohibidos + tamaño | Menos de 10s |
commit-msg | Valida el mensaje de commit | Conventional Commits vía commitlint | Menos de 1s |
prepare-commit-msg | Antes de abrir el editor | Detecta branch gitflow y genera prefijo automático | Menos de 1s |
pre-push | Antes de enviar al remoto | Protección de branches + tsc + tests completos | 30s–2min |
post-checkout | Al cambiar de branch | Aviso si package-lock.json o Prisma cambiaron | Menos de 1s |
post-merge | Tras merge o pull | Aviso si package-lock.json cambió | Menos de 1s |
Los hooks post-checkout y post-merge son informativos: muestran un aviso en la terminal
recomendando ejecutar npm install o npx prisma generate, pero no bloquean la operación.
Los demás hooks sí bloquean si detectan problemas.
pre-commit: la primera línea de defensa
El hook pre-commit ejecuta cuatro verificaciones en paralelo:
- lint-staged: ejecuta ESLint y Prettier únicamente sobre los archivos staged (modificados), no sobre todo el proyecto.
- secretlint: analiza los archivos staged buscando patrones de API keys, tokens, contraseñas y otros secrets.
- Archivos prohibidos: rechaza el commit si se incluyen archivos como
.env,.env.local, claves privadas o archivos de credenciales. - Tamaño de archivos: advierte si algún archivo staged supera un umbral razonable.
pre-push: verificación profunda
El hook pre-push es el más exhaustivo y ejecuta:
- Protección de branches: impide push directo a
mainydevelop(solo se permite vía merge request). - TypeScript check: ejecuta
tsc --noEmitpara verificar que no hay errores de tipos. - Tests completos: ejecuta la suite completa de tests del proyecto con
--bailpara fallar rápido.
3. Conventional Commits
Todos los repositorios de Nvito utilizan el estándar Conventional Commits validado por commitlint. Esto garantiza un historial de Git limpio, legible y apto para generación automática de changelogs.
Formato
<tipo>(<scope>): <descripción>
- tipo: categoría del cambio (obligatorio).
- scope: repositorio o módulo afectado (obligatorio en Nvito).
- descripción: resumen breve en minúsculas, sin punto final (obligatorio).
Tipos permitidos
| Tipo | Descripción | Ejemplo |
|---|---|---|
feat | Nueva funcionalidad | feat(admin): add guest capacity bar component |
fix | Corrección de bug | fix(api): correct timezone offset in RSVP response |
docs | Cambios en documentación | docs(docs): add git hooks guide |
style | Formato, espacios, puntos y comas (sin cambio funcional) | style(admin): fix indentation in sidebar component |
refactor | Reestructuración de código sin cambio funcional | refactor(api): split notification service into sub-services |
perf | Mejora de rendimiento | perf(api): add database index for event queries |
test | Agregar o modificar tests | test(client): add unit tests for auth reducer |
build | Cambios en sistema de build o dependencias | build(pwa): upgrade Next.js to 16.x |
ci | Cambios en integración continua | ci(api): add staging deploy workflow |
chore | Tareas de mantenimiento | chore(deps): update eslint plugins |
Scopes del ecosistema
| Scope | Repositorio |
|---|---|
api | nvito-api |
admin | nvito-admin |
client | nvito-client |
pwa | nvito-pwa |
invitations | nvito-invitations |
docs | nvito-docs |
landing | nvito-landing |
deps | Dependencias transversales |
Ejemplos de buenos mensajes de commit: - feat(api): add endpoint for cash fund contributions
fix(admin): resolve infinite loop in event wizard validation-refactor(client): extract auth logic into dedicated reducer-test(invitations): add security tests for XSS payloads-docs(docs): update deployment guide with Railway setup
Ejemplos de mensajes rechazados por commitlint: - updated stuff — falta tipo y scope - Fix bug — falta scope, la descripción empieza con mayúscula - feat: add new feature — falta scope
(obligatorio en Nvito) - feat(api): Add endpoint. — la descripción no debe empezar con mayúscula
ni terminar con punto
Auto-prefijo con prepare-commit-msg
El hook prepare-commit-msg detecta automáticamente la rama actual y genera un prefijo basado en el patrón gitflow:
- Si estás en
feature/admin-guest-capacity, al ejecutargit commitel editor se abre confeat(admin):prellenado. - Si estás en
fix/api-timezone-offset, el editor muestrafix(api):prellenado. - Si estás en
refactor/client-auth-split, el editor muestrarefactor(client):prellenado.
Esto reduce errores y acelera el flujo de trabajo. El desarrollador solo necesita completar la descripción.
Flujo de un Commit con Hooks
4. Plugins ESLint
Además de las reglas estándar de ESLint, los repositorios de tier 1 incluyen tres plugins especializados:
no-only-tests
Previene que se suban tests con .only() al repositorio. Cuando un desarrollador usa describe.only() o it.only() para depurar localmente y olvida quitarlo, solo ese test ejecutaría en CI y el resto se ignoraría silenciosamente.
Protección activa: este plugin convierte cualquier .only() en un error de ESLint que
bloquea el commit automáticamente vía lint-staged. Esto protege las 254+ suites de nvito-api
(y las de todos los repos) de ejecutarse parcialmente sin que nadie lo note.
unused-imports
Detecta y auto-elimina imports que no se usan en el archivo. Al ejecutar eslint --fix, los imports huérfanos se eliminan automáticamente, manteniendo el código limpio sin intervención manual.
simple-import-sort
Ordena automáticamente los imports de cada archivo de forma consistente al ejecutar --fix. El orden agrupa:
- Dependencias externas (
react,next,@nestjs/*) - Alias internos (
@/components/*,@modules/*) - Imports relativos (
./utils,../types)
Esto elimina discusiones de estilo y reduce conflictos de merge en los imports.
5. Configuración por repositorio
La configuración varía según el tier del repositorio:
| Repositorio | Tier | ESLint plugins | lint-staged | pre-push |
|---|---|---|---|---|
| nvito-api | 1 | no-only-tests, unused-imports, simple-import-sort | ESLint + Prettier | tsc + tests (--bail) |
| nvito-admin | 1 | no-only-tests, unused-imports, simple-import-sort | ESLint + Prettier | tsc + tests |
| nvito-invitations | 1 | no-only-tests, unused-imports, simple-import-sort | ESLint + Prettier | tsc + tests |
| nvito-client | 1 | no-only-tests, unused-imports, simple-import-sort | ESLint + Prettier | tsc + tests |
| nvito-pwa | 1 | no-only-tests, unused-imports, simple-import-sort | ESLint + Prettier | tsc + tests |
| nvito-docs | 2 | — | Prettier only | build |
| nvito-landing | 2 | — | Prettier only | build |
Los repositorios de tier 2 (docs y landing) no tienen tests unitarios significativos ni código TypeScript complejo, por lo que solo verifican formato con Prettier y que el build estático se genere correctamente.
6. Resolución de problemas
"Mi commit fue rechazado por commitlint"
El mensaje no cumple el formato tipo(scope): descripción. Verificar:
- El tipo es uno de los permitidos:
feat,fix,docs,style,refactor,perf,test,build,ci,chore. - El scope está entre paréntesis y es válido:
api,admin,client,pwa,invitations,docs,landing,deps. - La descripción empieza en minúscula y no termina con punto.
- Hay un espacio después de los dos puntos:
feat(api): descripción(correcto) vsfeat(api):descripción(incorrecto).
"ESLint encontró errores en pre-commit"
lint-staged ejecuta ESLint con --fix automáticamente, pero algunos errores no son auto-corregibles. Para resolverlos manualmente:
# Ver errores detallados
npx eslint archivo.ts
# Intentar fix automatico
npx eslint --fix archivo.ts
# Si el error persiste, corregirlo manualmente y re-stagear
git add archivo.ts
"secretlint reportó un falso positivo"
Si secretlint detecta un string como potencial secret pero es un falso positivo (por ejemplo, un hash de ejemplo en documentación):
# Opcion 1: agregar al archivo .secretlintignore
echo "ruta/al/archivo.ts" >> .secretlintignore
# Opcion 2: bypass puntual (NO recomendado)
git commit --no-verify -m "tipo(scope): descripcion"
"Pre-push tarda mucho"
Es normal. El hook pre-push ejecuta la suite completa de tests, lo que puede tomar entre 30 segundos y 2 minutos dependiendo del repositorio. Los tiempos aproximados son:
- nvito-api: ~90s (254+ suites)
- nvito-admin: ~60s (154+ suites)
- nvito-client: ~30s (32+ suites)
- nvito-pwa: ~20s (18+ suites)
- nvito-invitations: ~15s (17+ suites)
Bypass de emergencia: los flags HUSKY=0 git push y git commit --no-verify deben usarse
exclusivamente en emergencias. Omitir las verificaciones compromete la calidad del código y
puede introducir regresiones. Todo bypass debe justificarse al equipo.
7. Referencia técnica
Archivos de configuración
| Archivo | Propósito |
|---|---|
.husky/pre-commit | Archivos prohibidos + tamaño + secretlint + lint-staged |
.husky/commit-msg | Validación con commitlint |
.husky/prepare-commit-msg | Auto-prefijo desde nombre de branch |
.husky/pre-push | Branch protection + tsc + tests |
.husky/post-checkout | Aviso de npm install si dependencias cambiaron |
.husky/post-merge | Aviso de npm install si dependencias cambiaron |
commitlint.config.js | Reglas de Conventional Commits |
lint-staged.config.mjs | Qué linters ejecutar por tipo de archivo |
.secretlintrc.json | Reglas de detección de secrets |
.prettierrc | Configuración de formato de código |
eslint.config.mjs | Reglas de ESLint + plugins |