- Configure workspace Git repository with comprehensive .gitignore - Add Odoo as submodule for ERP reference code - Include documentation: SETUP.md, GIT-STRUCTURE.md - Add gitignore templates for projects (backend, frontend, database) - Structure supports independent repos per project/subproject level Workspace includes: - core/ - Reusable patterns, modules, orchestration system - projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.) - knowledge-base/ - Reference code and patterns (includes Odoo submodule) - devtools/ - Development tools and templates - customers/ - Client implementations template 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
14 KiB
Contributing to GAMILIT
¡Gracias por tu interés en contribuir a GAMILIT! Este documento te guiará a través del proceso de contribución.
📋 Tabla de Contenidos
- Código de Conducta
- Pre-requisitos
- Setup del Entorno de Desarrollo
- Workflow de Desarrollo
- Constants SSOT - Pre-Commit Checklist ⭐
- Estándares de Código
- Commits y Pull Requests
- Testing
- Documentación
Código de Conducta
Este proyecto sigue un código de conducta profesional. Se espera que todos los contribuyentes:
- Sean respetuosos y constructivos en sus interacciones
- Acepten críticas constructivas con profesionalismo
- Se enfoquen en lo mejor para la comunidad y el proyecto
- Muestren empatía hacia otros miembros de la comunidad
Pre-requisitos
Antes de contribuir, asegúrate de tener instalado:
- Node.js >= 18.0.0
- npm >= 9.0.0
- Git >= 2.30
- PostgreSQL >= 15.x (para desarrollo local)
- Docker (opcional, para contenedores)
Setup del Entorno de Desarrollo
1. Fork y Clone
# Fork el repositorio en GitHub
# Luego clona tu fork
git clone https://github.com/TU_USUARIO/gamilit.git
cd gamilit/projects/gamilit
# Agrega el upstream
git remote add upstream https://github.com/GAMILIT_ORG/gamilit.git
2. Instalar Dependencias
# Instalar dependencias root (incluye postinstall que ejecuta sync:enums)
npm install
# Instalar dependencias Backend
cd apps/backend
npm install
# Instalar dependencias Frontend
cd ../frontend
npm install
# Volver a root
cd ../..
3. Configurar Variables de Entorno
# Backend
cp apps/backend/.env.example apps/backend/.env
# Editar apps/backend/.env con tus credenciales
# Frontend
cp apps/frontend/.env.example apps/frontend/.env
# Editar apps/frontend/.env con tu API URL
4. Setup de Base de Datos
# Crear base de datos
createdb gamilit_dev
# Ejecutar migraciones (cuando estén disponibles)
cd apps/backend
npm run migration:run
5. Verificar Setup
# Desde root, ejecutar validaciones
npm run validate:all
# Debe retornar:
# ✅ ENUMs sincronizados exitosamente
# ✅ EXCELENTE! No se encontraron violaciones de hardcoding
# ✅ API Contract validado
Workflow de Desarrollo
1. Crear Branch
# Actualizar main
git checkout main
git pull upstream main
# Crear feature branch
git checkout -b feature/mi-nueva-funcionalidad
# O bug fix branch
git checkout -b fix/corregir-bug-autenticacion
Convención de nombres de branches:
feature/- Nuevas funcionalidadesfix/- Corrección de bugsrefactor/- Refactorización de códigodocs/- Cambios en documentacióntest/- Agregar o mejorar testschore/- Tareas de mantenimiento
2. Desarrollar
# Backend
cd apps/backend
npm run dev # Modo desarrollo con hot-reload
# Frontend (en otra terminal)
cd apps/frontend
npm run dev # Vite dev server
3. Hacer Commits
Ver sección Commits y Pull Requests para convenciones.
Constants SSOT - Pre-Commit Checklist ⭐
IMPORTANTE: Antes de hacer commit, SIEMPRE ejecutar este checklist para asegurar el cumplimiento de la política SSOT.
Checklist Obligatorio
# ✅ 1. Sincronizar ENUMs Backend → Frontend
npm run sync:enums
# ✅ 2. Validar que no haya hardcoding (33 patrones detectados)
npm run validate:constants
# ✅ 3. Validar contrato API Backend ↔ Frontend
npm run validate:api-contract
# ✅ 4. Ejecutar tests
npm run test
# ✅ 5. Ejecutar linter
npm run lint
# ✅ 6. Si TODO pasa, hacer commit
git add .
git commit -m "feat: agregar nueva funcionalidad"
Atajo para validar todo:
npm run validate:all && npm run test && git commit
¿Cuándo actualizar constantes?
Al crear nueva tabla Database:
- ✅ Crear DDL en
platform/db/ddl/schemas/{schema}/tables/{tabla}.sql - ✅ INMEDIATAMENTE agregar entrada en
apps/backend/src/shared/constants/database.constants.ts:
export const DB_TABLES = {
AUTH: {
// ... existing tables
NEW_TABLE: 'new_table', // ← AGREGAR AQUÍ
},
};
- ✅ Crear Entity usando la nueva constante:
import { DB_SCHEMAS, DB_TABLES } from '@/shared/constants';
@Entity({ schema: DB_SCHEMAS.AUTH, name: DB_TABLES.AUTH.NEW_TABLE })
export class NewTable { ... }
Al crear nueva ruta API Backend:
- ✅ PRIMERO agregar entrada en
apps/backend/src/shared/constants/routes.constants.ts:
export const API_ROUTES = {
NEW_MODULE: {
BASE: '/new-module',
BY_ID: (id: string) => `/new-module/${id}`,
},
};
- ✅ Crear Controller usando la nueva constante:
import { API_ROUTES } from '@/shared/constants';
@Controller(API_ROUTES.NEW_MODULE.BASE.replace('/', ''))
export class NewModuleController { ... }
- ✅ INMEDIATAMENTE agregar en Frontend
apps/frontend/src/shared/constants/api-endpoints.ts:
export const API_ENDPOINTS = {
NEW_MODULE: {
BASE: `${API_BASE_URL}/new-module`,
BY_ID: (id: string) => `${API_BASE_URL}/new-module/${id}`,
},
};
- ✅ Validar sincronización:
npm run validate:api-contract
# Debe retornar: ✅ API Contract validado
Al crear nuevo ENUM:
- ✅ Definir ENUM solo en Backend
apps/backend/src/shared/constants/enums.constants.ts:
export enum NewEnum {
VALUE_1 = 'value_1',
VALUE_2 = 'value_2',
}
- ✅ Ejecutar script de sincronización:
npm run sync:enums
- ✅ Verificar que Frontend tiene el mismo ENUM:
diff apps/backend/src/shared/constants/enums.constants.ts \
apps/frontend/src/shared/constants/enums.constants.ts
# Debe retornar: (sin diferencias)
- ✅ NO modificar manualmente el archivo Frontend (se sobrescribe en sync).
Violaciones Comunes y Cómo Evitarlas
❌ Violación 1: Hardcoded schema en Entity
Detectado por: npm run validate:constants
// ❌ MAL
@Entity({ schema: 'auth_management', name: 'users' })
export class User { ... }
// ✅ BIEN
import { DB_SCHEMAS, DB_TABLES } from '@/shared/constants';
@Entity({ schema: DB_SCHEMAS.AUTH, name: DB_TABLES.AUTH.USERS })
export class User { ... }
❌ Violación 2: Hardcoded API URL en Frontend
Detectado por: npm run validate:constants
// ❌ MAL
await fetch('http://localhost:3000/api/v1/users/123');
// ✅ BIEN
import { API_ENDPOINTS } from '@/shared/constants';
await fetch(API_ENDPOINTS.USERS.BY_ID('123'));
❌ Violación 3: Discrepancia Backend ↔ Frontend
Detectado por: npm run validate:api-contract
❌ Ruta "/users/:id/settings" existe en Backend pero NO en Frontend
Solución: Sincronizar ambos archivos (Backend routes.constants.ts y Frontend api-endpoints.ts)
Documentación de Referencia
- Arquitectura completa: CONSTANTS-ARCHITECTURE.md
- Políticas obligatorias: POLITICA-CONSTANTS-SSOT.md
Estándares de Código
TypeScript
- Estilo: Airbnb TypeScript Style Guide
- Linter: ESLint con reglas de @typescript-eslint
- Formatter: Prettier con configuración del proyecto
# Ejecutar linter
npm run lint
# Ejecutar formatter
npm run format
Naming Conventions
Variables y Funciones:
// camelCase
const userName = 'John';
function getUserById(id: string) { ... }
Clases y Interfaces:
// PascalCase
class UserService { ... }
interface IUserRepository { ... }
Constantes:
// UPPER_SNAKE_CASE para constantes globales
export const MAX_LOGIN_ATTEMPTS = 3;
// PascalCase para ENUMs
export enum UserRole {
ADMIN = 'admin',
TEACHER = 'teacher',
STUDENT = 'student',
}
Archivos:
user.entity.ts # Entities
user.dto.ts # DTOs
user.service.ts # Services
user.controller.ts # Controllers
user.module.ts # Modules
user.spec.ts # Tests
Import Organization
// 1. Node modules
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
// 2. Internal modules
import { DB_SCHEMAS, DB_TABLES } from '@/shared/constants';
import { User } from '@/modules/auth/entities/user.entity';
// 3. Relative imports
import { CreateUserDto } from './dto/create-user.dto';
Commits y Pull Requests
Conventional Commits
Usamos Conventional Commits para mensajes de commit:
<type>(<scope>): <subject>
[optional body]
[optional footer]
Types:
feat: Nueva funcionalidadfix: Corrección de bugrefactor: Refactorización de códigodocs: Cambios en documentacióntest: Agregar o mejorar testschore: Tareas de mantenimientostyle: Cambios de formato (no afectan lógica)perf: Mejoras de performance
Ejemplos:
feat(auth): agregar autenticación con Google OAuth
fix(gamification): corregir cálculo de puntos en quiz
refactor(database): migrar entities a usar DB_SCHEMAS constants
docs(readme): actualizar sección de Constants SSOT
test(users): agregar tests para UserService
Pull Request Process
- Actualizar branch con main:
git checkout main
git pull upstream main
git checkout tu-feature-branch
git rebase main
- Ejecutar validaciones:
# ✅ Validar SSOT
npm run validate:all
# ✅ Ejecutar tests
npm run test
# ✅ Ejecutar linter
npm run lint
- Crear Pull Request en GitHub:
Título: Usar Conventional Commits format
feat(auth): agregar autenticación con Google OAuth
Descripción: Incluir template
## Descripción
Breve descripción de los cambios realizados.
## Tipo de cambio
- [ ] 🐛 Bug fix (cambio que corrige un issue)
- [ ] ✨ Nueva funcionalidad (cambio que agrega funcionalidad)
- [ ] 💥 Breaking change (fix o feature que causa cambios incompatibles)
- [ ] 📝 Documentación
## Checklist
- [ ] Mi código sigue las convenciones del proyecto
- [ ] He ejecutado `npm run validate:all` ✅
- [ ] He ejecutado `npm run test` y todos los tests pasan ✅
- [ ] He ejecutado `npm run lint` ✅
- [ ] He actualizado la documentación correspondiente
- [ ] Mis cambios no generan nuevas advertencias
- [ ] He agregado tests que prueban mi fix/funcionalidad
- [ ] Tests unitarios y de integración pasan localmente
- [ ] He sincronizado ENUMs si modifiqué Backend enums (`npm run sync:enums`)
- [ ] He actualizado constantes si agregué schemas/tablas/rutas
## Screenshots (si aplica)
## Notas adicionales
- Code Review:
- Mínimo 1 aprobación requerida
- CI/CD debe pasar (validate-constants.yml + tests)
- Resolver todos los comentarios antes de merge
- Merge:
- Squash and merge preferido para features pequeñas
- Merge commit para features grandes
- Rebase and merge para branches con commits limpios
Testing
Backend Tests
cd apps/backend
# Ejecutar todos los tests
npm run test
# Tests en modo watch
npm run test:watch
# Cobertura de tests
npm run test:cov
Estructura de tests:
describe('UserService', () => {
let service: UserService;
let repository: Repository<User>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [UserService, /* ... */],
}).compile();
service = module.get<UserService>(UserService);
repository = module.get<Repository<User>>(getRepositoryToken(User));
});
describe('findById', () => {
it('should return a user when found', async () => {
const user = { id: '1', email: 'test@test.com' };
jest.spyOn(repository, 'findOne').mockResolvedValue(user as User);
expect(await service.findById('1')).toBe(user);
});
it('should throw NotFoundException when user not found', async () => {
jest.spyOn(repository, 'findOne').mockResolvedValue(null);
await expect(service.findById('999')).rejects.toThrow(NotFoundException);
});
});
});
Frontend Tests
cd apps/frontend
# Ejecutar tests
npm run test
# Tests con UI
npm run test:ui
# Cobertura
npm run test:coverage
Estructura de tests:
import { render, screen, fireEvent } from '@testing-library/react';
import { LoginForm } from './LoginForm';
describe('LoginForm', () => {
it('renders email and password inputs', () => {
render(<LoginForm />);
expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
});
it('calls onSubmit when form is submitted', async () => {
const mockSubmit = jest.fn();
render(<LoginForm onSubmit={mockSubmit} />);
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'test@test.com' },
});
fireEvent.click(screen.getByRole('button', { name: /login/i }));
expect(mockSubmit).toHaveBeenCalledWith({
email: 'test@test.com',
password: expect.any(String),
});
});
});
Documentación
Documentar Código
JSDoc para funciones públicas:
/**
* Obtiene un usuario por su ID
*
* @param id - UUID del usuario
* @returns Promise con el usuario encontrado
* @throws NotFoundException si el usuario no existe
*
* @example
* ```typescript
* const user = await userService.findById('123e4567-e89b-12d3-a456-426614174000');
* ```
*/
async findById(id: string): Promise<User> {
// ...
}
Actualizar Documentación
Si tus cambios afectan:
- API Endpoints: Actualizar Swagger/OpenAPI docs
- Schemas de Database: Actualizar DDL y documentation
- Funcionalidades nuevas: Actualizar README.md y docs relevantes
- Cambios arquitectónicos: Crear ADR (Architecture Decision Record)
¿Preguntas?
- Slack: #dev-support
- Email: dev@gamilit.com
- Issues: GitHub Issues
¡Gracias por contribuir a GAMILIT! 🚀
Versión: 1.0 Última actualización: 2025-11-02