Metadata-Version: 2.4
Name: brainstack-config-loader
Version: 0.1.1
Summary: A robust, flexible configuration management system for Python applications
Author-email: Development Team <dev@example.com>
License: MIT
Project-URL: Homepage, https://github.com/dev-brainstack/config-loader
Project-URL: Repository, https://github.com/dev-brainstack/config-loader.git
Project-URL: Issues, https://github.com/dev-brainstack/config-loader/issues
Keywords: config,configuration,yaml,pydantic,environment
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: pyyaml
Requires-Dist: pydantic
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: flake8; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Requires-Dist: python-semantic-release; extra == "dev"

# config-loader 🚀

Una librería robusta y flexible para gestionar configuración en aplicaciones Python. Carga configuración desde múltiples archivos YAML, aplica overrides desde variables de entorno y valida todo con Pydantic.

**Versión:** 0.1.0  
**Estado:** Alpha  
**Licencia:** MIT

---

## 📋 Tabla de Contenidos

- [Características](#características)
- [Instalación](#instalación)
- [Uso Rápido](#uso-rápido)
- [Ejemplos Detallados](#ejemplos-detallados)
- [Desarrollo](#desarrollo)
- [Configuración del Ambiente de Desarrollo](#configuración-del-ambiente-de-desarrollo)
- [API Reference](#api-reference)
- [Manejo de Errores](#manejo-de-errores)
- [Convenciones](#convenciones)
- [GitHub Actions Pipelines](#github-actions-pipelines)
- [Branching Strategy](#branching-strategy)
- [Commit Conventions](#commit-conventions)
- [Development Workflow](#development-workflow)
- [Semantic Versioning](#semantic-versioning)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)

---

## ✨ Características

✅ **Carga desde múltiples YAML** - Combina archivos base + overrides con precedencia clara  
✅ **Overrides de variables de entorno** - Aplica ENV vars con convención `PREFIX__NESTED__KEY`  
✅ **Validación con Pydantic** - Type-safe con mensajes de error claros  
✅ **Manejo granular de errores** - Excepciones específicas para cada tipo de error  
✅ **Tipos seguros** - Type hints completos para mejor IDE support  
✅ **Sin mutaciones** - Las funciones retornan nuevos objetos, no modifican los originales  

---

## 📦 Instalación

### Instalación básica

```bash
pip install config-loader
```

### Instalación desde repositorio (desarrollo)

```bash
git clone https://github.com/dev-brainstack/config-loader.git
cd config-loader
pip install -e .
```

### Instalación con dependencias de desarrollo

```bash
pip install -e ".[dev]"
```

---

## 🚀 Uso Rápido

### Ejemplo Básico

```python
from pydantic import BaseModel
from config_loader import load_config

# 1. Define tu modelo de configuración
class DatabaseConfig(BaseModel):
    host: str
    port: int = 5432
    username: str
    password: str

class AppConfig(BaseModel):
    debug: bool = False
    database: DatabaseConfig

# 2. Crea archivos YAML
# config/base.yaml
# debug: false
# database:
#   host: localhost
#   port: 5432
#   username: admin
#   password: secret

# 3. Carga la configuración
config = load_config(
    AppConfig,
    paths=["config/base.yaml"],
    use_env=True,
    env_prefix="APP"
)

# 4. Usa la configuración
print(config.debug)  # False
print(config.database.host)  # localhost
```

---

## 📚 Ejemplos Detallados

### Ejemplo 1: Configuración Simple

**Entrada - Archivo YAML (`config.yaml`):**
```yaml
app_name: MyApp
debug: true
port: 8000
```

**Código Python:**
```python
from pydantic import BaseModel
from config_loader import load_config

class Config(BaseModel):
    app_name: str
    debug: bool
    port: int

config = load_config(Config, paths=["config.yaml"])

# Salida
print(config.app_name)  # "MyApp"
print(config.debug)     # True
print(config.port)      # 8000
```

---

### Ejemplo 2: Múltiples Archivos YAML con Overrides

**Entrada - Archivos YAML:**

`config/base.yaml`:
```yaml
app_name: MyApp
debug: false
database:
  host: localhost
  port: 5432
  username: admin
```

`config/production.yaml`:
```yaml
debug: false
database:
  host: prod-db.example.com
  port: 5432
```

**Código Python:**
```python
from pydantic import BaseModel
from typing import Dict, Any
from config_loader import load_config

class AppConfig(BaseModel):
    app_name: str
    debug: bool
    database: Dict[str, Any]

# Los archivos se cargan en orden, el último sobrescribe al anterior
config = load_config(
    AppConfig,
    paths=[
        "config/base.yaml",
        "config/production.yaml"
    ]
)

# Salida
print(config.app_name)           # "MyApp" (del base.yaml)
print(config.database["host"])   # "prod-db.example.com" (del production.yaml)
print(config.database["port"])   # 5432 (del production.yaml)
print(config.database["username"]) # "admin" (del base.yaml, no sobrescrito)
```

---

### Ejemplo 3: Overrides desde Variables de Entorno

**Entrada - Variables de Entorno:**
```bash
export APP__DEBUG=true
export APP__DATABASE__HOST=env-db.example.com
export APP__DATABASE__PORT=3306
export APP__DATABASE__USERNAME=envuser
export APP__DATABASE__PASSWORD=envpass
```

**Código Python:**
```python
from pydantic import BaseModel
from typing import Dict, Any
from config_loader import load_config

class AppConfig(BaseModel):
    debug: bool = False
    database: Dict[str, Any]

# use_env=True habilita los overrides desde variables de entorno
config = load_config(
    AppConfig,
    paths=["config/base.yaml"],
    use_env=True,
    env_prefix="APP"
)

# Salida
print(config.debug)                  # True (de ENV)
print(config.database["host"])       # "env-db.example.com" (de ENV)
print(config.database["port"])       # 3306 (de ENV, convertido a int)
print(config.database["username"])   # "envuser" (de ENV)
print(config.database["password"])   # "envpass" (de ENV)
```

---

### Ejemplo 4: Combinación Completa (YAML + ENV)

**Entrada - Archivo YAML (`config/base.yaml`):**
```yaml
app_name: MyApp
debug: false
database:
  host: localhost
  port: 5432
  username: admin
  password: secret
cache:
  enabled: true
  ttl: 3600
```

**Entrada - Variables de Entorno:**
```bash
export APP__DEBUG=true
export APP__DATABASE__HOST=prod-db.example.com
export APP__CACHE__TTL=7200
```

**Código Python:**
```python
from pydantic import BaseModel
from typing import Dict, Any
from config_loader import load_config

class CacheConfig(BaseModel):
    enabled: bool
    ttl: int

class DatabaseConfig(BaseModel):
    host: str
    port: int
    username: str
    password: str

class AppConfig(BaseModel):
    app_name: str
    debug: bool
    database: DatabaseConfig
    cache: CacheConfig

config = load_config(
    AppConfig,
    paths=["config/base.yaml"],
    use_env=True,
    env_prefix="APP"
)

# Salida
print(config.app_name)              # "MyApp" (del YAML)
print(config.debug)                 # True (de ENV, sobrescribe YAML)
print(config.database.host)         # "prod-db.example.com" (de ENV)
print(config.database.port)         # 5432 (del YAML, no sobrescrito)
print(config.database.username)     # "admin" (del YAML)
print(config.database.password)     # "secret" (del YAML)
print(config.cache.enabled)         # True (del YAML)
print(config.cache.ttl)             # 7200 (de ENV, sobrescribe YAML)
```

---

### Ejemplo 5: Coerción Automática de Tipos

Las variables de entorno son siempre strings, pero `config-loader` las convierte automáticamente:

**Entrada - Variables de Entorno:**
```bash
export APP__DEBUG=true              # String "true" → Boolean True
export APP__PORT=8000               # String "8000" → Integer 8000
export APP__TIMEOUT=30.5            # String "30.5" → Float 30.5
export APP__NAME=MyApp              # String "MyApp" → String "MyApp"
```

**Código Python:**
```python
from pydantic import BaseModel
from config_loader import load_config

class AppConfig(BaseModel):
    debug: bool
    port: int
    timeout: float
    name: str

config = load_config(
    AppConfig,
    paths=[],  # Sin archivos YAML
    use_env=True,
    env_prefix="APP"
)

# Salida
print(config.debug)     # True (bool)
print(config.port)      # 8000 (int)
print(config.timeout)   # 30.5 (float)
print(config.name)      # "MyApp" (str)
```

---

## 🛠️ Desarrollo

### Comandos Rápidos con Makefile

El proyecto incluye un `Makefile` para facilitar tareas comunes de desarrollo:

```bash
# Ver todos los comandos disponibles
make help

# Ejecutar verificaciones de calidad (Black, Flake8, Mypy)
make lint

# Auto-formatear código con Black
make format

# Ejecutar tests con reporte de cobertura
make test

# Construir paquete de distribución
make build

# Limpiar artefactos de build y cache
make clean

# Instalar paquete en modo producción
make install

# Instalar paquete con dependencias de desarrollo
make dev-install
```

### Flujo de Desarrollo Recomendado

1. **Hacer cambios en el código**
2. **Formatear automáticamente:**
   ```bash
   make format
   ```
3. **Verificar calidad:**
   ```bash
   make lint
   ```
4. **Ejecutar tests:**
   ```bash
   make test
   ```
5. **Si todo pasa, hacer commit**

---

## 🛠️ Configuración del Ambiente de Desarrollo

### Requisitos Previos

- Python 3.7 o superior
- pip (gestor de paquetes de Python)
- git (para clonar el repositorio)

### Pasos de Instalación

#### 1. Clonar el repositorio

```bash
git clone https://github.com/dev-brainstack/config-loader.git
cd config-loader
```

#### 2. Crear un ambiente virtual (recomendado)

```bash
# En Linux/macOS
python3 -m venv .venv
source .venv/bin/activate

# En Windows
python -m venv .venv
.venv\Scripts\activate
```

#### 3. Instalar dependencias de desarrollo

```bash
pip install -e ".[dev]"
```

Esto instala:
- **config-loader** en modo editable (cambios se reflejan inmediatamente)
- **pytest** - Framework de testing
- **pytest-cov** - Cobertura de código
- **black** - Formateador de código
- **flake8** - Linter
- **mypy** - Type checker

#### 4. Verificar la instalación

```bash
# Ejecutar tests
pytest

# Ver cobertura de código
pytest --cov=src --cov-report=html

# Verificar formato de código
black --check src/

# Ejecutar linter
flake8 src/

# Type checking
mypy src/
```

### Estructura del Proyecto

```
config-loader/
├── .clinerules/              # Documentación interna
├── .gitignore
├── pyproject.toml            # Configuración del proyecto
├── README.md                 # Este archivo
├── requirements.txt          # Dependencias (alternativa a pip)
├── src/
│   └── config_loader/        # Código fuente
│       ├── __init__.py       # API pública
│       ├── loader.py         # Orquestación principal
│       ├── env.py            # Manejo de variables de entorno
│       ├── exceptions.py     # Excepciones personalizadas
│       ├── types.py          # Definiciones de tipos
│       └── utils.py          # Utilidades internas
├── test/
│   └── test_loader.py        # Suite de tests (60+ tests)
└── openspec/                 # Documentación de cambios
```

### Comandos Útiles para Desarrollo

```bash
# Ejecutar todos los tests
pytest

# Ejecutar tests con salida detallada
pytest -v

# Ejecutar un archivo de test específico
pytest test/test_loader.py

# Ejecutar un test específico
pytest test/test_loader.py::test_load_config_simple

# Ver cobertura de código
pytest --cov=src --cov-report=term-missing

# Generar reporte HTML de cobertura
pytest --cov=src --cov-report=html
# Abre htmlcov/index.html en el navegador

# Formatear código con black
black src/ test/

# Verificar formato sin cambiar archivos
black --check src/ test/

# Ejecutar linter
flake8 src/ test/

# Type checking
mypy src/

# Ejecutar todo (tests + coverage + lint + type check)
pytest && black --check src/ && flake8 src/ && mypy src/
```

### Flujo de Desarrollo Típico

1. **Crear una rama para tu cambio:**
   ```bash
   git checkout -b feature/mi-cambio
   ```

2. **Hacer cambios en el código**

3. **Ejecutar tests para verificar:**
   ```bash
   pytest
   ```

4. **Formatear código:**
   ```bash
   black src/
   ```

5. **Verificar linting:**
   ```bash
   flake8 src/
   ```

6. **Hacer commit:**
   ```bash
   git add .
   git commit -m "Descripción del cambio"
   ```

7. **Push a GitHub:**
   ```bash
   git push origin feature/mi-cambio
   ```

---

## 📖 API Reference

### `load_config()`

Función principal para cargar y validar configuración.

```python
def load_config(
    model_class: Type[T],
    paths: List[str],
    use_env: bool = True,
    env_prefix: str = ""
) -> T:
    """
    Carga configuración desde múltiples YAML + ENV + Validación.

    Args:
        model_class: Clase Pydantic que define el esquema de configuración
        paths: Lista de rutas a archivos YAML (orden = prioridad)
        use_env: Habilitar overrides desde variables de entorno (default: True)
        env_prefix: Prefijo para filtrar variables de entorno (default: "")

    Returns:
        Instancia del modelo validado

    Raises:
        ConfigFileNotFoundError: Si un archivo YAML no existe
        ConfigParseError: Si hay error al parsear YAML
        ConfigTypeError: Si la estructura no es válida
        ConfigValidationError: Si la validación Pydantic falla
    """
```

**Ejemplo:**
```python
config = load_config(
    AppConfig,
    paths=["config/base.yaml", "config/production.yaml"],
    use_env=True,
    env_prefix="APP"
)
```

---

### Excepciones

#### `ConfigError` (base)
Excepción base para todos los errores de configuración.

#### `ConfigFileNotFoundError`
Se lanza cuando un archivo YAML no existe.

```python
try:
    config = load_config(AppConfig, paths=["nonexistent.yaml"])
except ConfigFileNotFoundError as e:
    print(f"Archivo no encontrado: {e.path}")
```

#### `ConfigParseError`
Se lanza cuando hay error al parsear YAML.

```python
try:
    config = load_config(AppConfig, paths=["invalid.yaml"])
except ConfigParseError as e:
    print(f"Error parseando {e.path}: {e.original_error}")
```

#### `ConfigTypeError`
Se lanza cuando la estructura no es válida.

```python
try:
    config = load_config(AppConfig, paths=["config.yaml"])
except ConfigTypeError as e:
    print(f"Se esperaba {e.expected}, se recibió {e.received}")
```

#### `ConfigValidationError`
Se lanza cuando la validación Pydantic falla.

```python
try:
    config = load_config(AppConfig, paths=["config.yaml"])
except ConfigValidationError as e:
    print(f"Error de validación: {e.raw_message}")
```

---

## ⚠️ Manejo de Errores

### Ejemplo Completo

```python
from config_loader import load_config, ConfigError
from config_loader.exceptions import (
    ConfigFileNotFoundError,
    ConfigParseError,
    ConfigValidationError
)

try:
    config = load_config(
        AppConfig,
        paths=["config/base.yaml"],
        use_env=True,
        env_prefix="APP"
    )
except ConfigFileNotFoundError as e:
    print(f"❌ Archivo no encontrado: {e.path}")
    exit(1)
except ConfigParseError as e:
    print(f"❌ Error parseando YAML: {e.original_error}")
    exit(1)
except ConfigValidationError as e:
    print(f"❌ Configuración inválida: {e.raw_message}")
    exit(1)
except ConfigError as e:
    print(f"❌ Error de configuración: {e}")
    exit(1)

print("✅ Configuración cargada exitosamente")
```

---

## 📋 Convenciones

### Convención de Variables de Entorno

Las variables de entorno siguen la convención `PREFIX__NESTED__KEY`:

```
APP__DEBUG=true
APP__DATABASE__HOST=localhost
APP__DATABASE__PORT=5432
APP__CACHE__REDIS__HOST=redis.local
```

Se convierte en:

```python
{
    "debug": True,
    "database": {
        "host": "localhost",
        "port": 5432
    },
    "cache": {
        "redis": {
            "host": "redis.local"
        }
    }
}
```

### Reglas de Coerción de Tipos

Las variables de entorno se convierten automáticamente:

| Valor ENV | Tipo Detectado | Resultado |
|-----------|----------------|-----------|
| `"true"` o `"false"` | Boolean | `True` o `False` |
| `"123"` | Integer | `123` |
| `"45.67"` | Float | `45.67` |
| `"hello"` | String | `"hello"` |

### Precedencia de Configuración

1. **Archivos YAML** - Base (primer archivo)
2. **Archivos YAML** - Overrides (archivos posteriores sobrescriben anteriores)
3. **Variables de Entorno** - Máxima prioridad (sobrescriben todo)

```
base.yaml → override.yaml → ENV vars
   ↓            ↓            ↓
   └────────────┴────────────┘
         Configuración Final
```

---

## 🧪 Testing

El proyecto incluye una suite completa de tests con >90% de cobertura.

```bash
# Ejecutar todos los tests
pytest

# Ver cobertura detallada
pytest --cov=src --cov-report=term-missing

# Generar reporte HTML
pytest --cov=src --cov-report=html
```

---

## 🔄 GitHub Actions Pipelines

El proyecto utiliza dos pipelines de GitHub Actions para automatizar la validación y el lanzamiento de versiones.

### CI Pipeline

**Cuándo se ejecuta:**
- En cada Pull Request (creación y actualizaciones)
- En cada push a la rama main

**Qué valida:**
- ✅ Formato de código (Black)
- ✅ Linting (Flake8)
- ✅ Tests unitarios e integración
- ✅ Cobertura de código

**Requisitos:**
- Python 3.9
- Todas las dependencias de desarrollo instaladas

**Si falla:**
- El Pull Request no puede ser mergeado a main
- Se bloquea el merge hasta que se corrijan los errores

**Pasos del pipeline:**
1. Checkout del código
2. Setup de Python 3.9
3. Instalación de dependencias (`pip install -e ".[dev]"`)
4. Verificación de formato (`make format`)
5. Ejecución de tests (`make test`)

### Release Pipeline

**Cuándo se ejecuta:**
- Solo en pushes a la rama main (después de merges)
- NO se ejecuta en Pull Requests

**Qué hace:**
- Analiza los commits desde el último release
- Determina el tipo de versión a crear (major, minor, patch)
- Crea un tag de versión en GitHub
- Publica el paquete en PyPI
- Genera notas de release automáticamente

**Requisitos:**
- Python 3.9
- Tokens de autenticación (GH_TOKEN, PYPI_API_TOKEN)

**Cómo funciona:**
- Lee los mensajes de commit
- Usa semantic versioning para determinar el bump de versión
- Crea releases automáticamente basadas en los tipos de commits

### Comparación de Pipelines

| Aspecto | CI Pipeline | Release Pipeline |
|---------|-------------|------------------|
| **Trigger** | PR + push a main | Push a main |
| **Propósito** | Validar código | Crear releases |
| **Validaciones** | Format, Lint, Tests | Tests + Semantic Release |
| **Bloquea merge** | Sí | No |
| **Crea releases** | No | Sí |
| **Publica a PyPI** | No | Sí |

### Flujo de Ejecución

```
Developer crea PR
    ↓
CI Pipeline ejecuta (format, lint, tests)
    ↓
¿Pasa CI? → No → PR bloqueado, developer corrige
    ↓ Sí
Code review y aprobación
    ↓
Merge a main
    ↓
Release Pipeline ejecuta
    ↓
Analiza commits desde último release
    ↓
¿Hay cambios que justifiquen release? → No → Fin
    ↓ Sí
Determina versión (feat→minor, fix→patch)
    ↓
Crea tag y release en GitHub
    ↓
Publica en PyPI
    ↓
Fin
```

---

## 🌿 Branching Strategy

El proyecto utiliza una estrategia de branching simple pero efectiva.

### Estructura de Branches

**Main Branch**
- Rama principal donde reside el código en producción
- Solo acepta cambios a través de Pull Requests
- Todos los commits en main deben pasar CI
- Protegida contra pushes directos

**Feature Branches**
- Ramas temporales para desarrollar nuevas características o fixes
- Se crean desde main
- Se eliminan después de mergear a main
- Convención de nombres: `feature/<descripción>` o `fix/<descripción>`

### Flujo de Trabajo

```
1. Crear rama desde main
   git checkout -b feature/nueva-caracteristica

2. Hacer cambios y commits
   git add .
   git commit -m "feat: agregar nueva caracteristica"

3. Push a GitHub
   git push origin feature/nueva-caracteristica

4. Crear Pull Request
   - Describe los cambios
   - Espera a que CI pase
   - Solicita revisión de código

5. Code Review
   - Otros desarrolladores revisan
   - Sugieren cambios si es necesario
   - Aprueban cuando está listo

6. Merge a main
   - Squash o merge según preferencia
   - CI ejecuta nuevamente
   - Release pipeline se ejecuta si hay cambios que justifiquen release

7. Eliminar rama
   - GitHub ofrece eliminar automáticamente
   - O manualmente: git branch -d feature/nueva-caracteristica
```

### Reglas de Branch Protection

- ✅ Requiere Pull Request para cambios
- ✅ Requiere que CI pase antes de merge
- ✅ Requiere revisión de código (recomendado)
- ✅ Bloquea pushes directos a main

### Mejores Prácticas

- Mantén branches pequeñas y enfocadas
- Crea un PR por cada feature/fix
- Actualiza tu rama con main antes de mergear
- Resuelve conflictos localmente antes de push
- Elimina branches después de mergear

---

## 📝 Commit Conventions

El proyecto utiliza **Semantic Commit Conventions** para automatizar el versionado.

### Formato de Commits

```
<tipo>: <descripción>

[cuerpo opcional]

[pie opcional]
```

**Ejemplo:**
```
feat: agregar soporte para archivos JSON

Permite cargar configuración desde archivos JSON además de YAML.
Implementa parser JSON con validación de esquema.

BREAKING CHANGE: El parámetro 'format' ahora es requerido
```

### Tipos de Commits

| Tipo | Descripción | ¿Release? | Versión |
|------|-------------|-----------|---------|
| `feat` | Nueva característica | ✅ Sí | Minor (1.0.0 → 1.1.0) |
| `fix` | Corrección de bug | ✅ Sí | Patch (1.0.0 → 1.0.1) |
| `perf` | Mejora de performance | ✅ Sí | Patch (1.0.0 → 1.0.1) |
| `docs` | Cambios en documentación | ❌ No | - |
| `chore` | Tareas de mantenimiento | ❌ No | - |
| `test` | Cambios en tests | ❌ No | - |
| `refactor` | Refactorización de código | ❌ No | - |

### Ejemplos de Commits

**Feature (crea release minor):**
```
feat: agregar soporte para TOML
feat: implementar hot-reloading de configuración
feat: agregar validación de esquema JSON
```

**Fix (crea release patch):**
```
fix: corregir parsing de YAML anidado
fix: resolver memory leak en merge profundo
fix: manejar variables de entorno vacías
```

**Performance (crea release patch):**
```
perf: optimizar merge de diccionarios grandes
perf: cachear archivos YAML parseados
```

**Documentation (NO crea release):**
```
docs: actualizar README con ejemplos
docs: agregar guía de contribución
docs: documentar API de excepciones
```

**Chore (NO crea release):**
```
chore: actualizar dependencias
chore: configurar pre-commit hooks
chore: limpiar código legacy
```

**Test (NO crea release):**
```
test: agregar tests para edge cases
test: aumentar cobertura a 95%
test: agregar tests de integración
```

**Refactor (NO crea release):**
```
refactor: simplificar lógica de merge
refactor: extraer funciones comunes
refactor: mejorar nombres de variables
```

### Breaking Changes

Para cambios que rompen compatibilidad, agrega `BREAKING CHANGE:` en el cuerpo:

```
feat: cambiar firma de load_config

BREAKING CHANGE: El parámetro 'env_prefix' ahora es requerido
```

Esto crea un release **major** (1.0.0 → 2.0.0).

---

## 🔄 Development Workflow

Flujo completo de desarrollo desde idea hasta producción.

### Paso 1: Crear una Rama

```bash
# Actualizar main
git checkout main
git pull origin main

# Crear rama para tu trabajo
git checkout -b feature/mi-caracteristica
```

### Paso 2: Hacer Cambios

```bash
# Editar archivos
# Agregar tests
# Actualizar documentación
```

### Paso 3: Commit Local

```bash
# Ver cambios
git status

# Agregar cambios
git add .

# Commit con mensaje semántico
git commit -m "feat: agregar nueva caracteristica"
```

### Paso 4: Verificar Localmente

```bash
# Ejecutar tests
make test

# Verificar formato
make format

# Verificar linting
make lint
```

### Paso 5: Push a GitHub

```bash
# Push de la rama
git push origin feature/mi-caracteristica
```

### Paso 6: Crear Pull Request

- Ve a GitHub
- Haz click en "Compare & pull request"
- Describe los cambios
- Espera a que CI pase

### Paso 7: Code Review

- Otros desarrolladores revisan
- Responde a comentarios
- Haz cambios si es necesario
- Solicita re-review

### Paso 8: Merge

Una vez aprobado:
```bash
# GitHub: Click en "Merge pull request"
# O desde línea de comandos:
git checkout main
git pull origin main
git merge feature/mi-caracteristica
git push origin main
```

### Paso 9: Cleanup

```bash
# Eliminar rama local
git branch -d feature/mi-caracteristica

# Eliminar rama remota
git push origin --delete feature/mi-caracteristica
```

### Qué Sucede Después del Merge

1. **CI Pipeline ejecuta** - Valida el código en main
2. **Release Pipeline ejecuta** - Analiza commits
3. **Si hay cambios que justifiquen release:**
   - Crea nuevo tag de versión
   - Publica en PyPI
   - Genera release notes en GitHub

---

## 📊 Semantic Versioning

El proyecto utiliza **Semantic Versioning** (SemVer) para versiones.

### Formato de Versión

```
MAJOR.MINOR.PATCH
  ↓      ↓      ↓
  1      2      3
```

- **MAJOR**: Cambios incompatibles (breaking changes)
- **MINOR**: Nuevas características (backward compatible)
- **PATCH**: Correcciones de bugs (backward compatible)

### Ejemplos de Progresión

**Scenario 1: Múltiples fixes**
```
1.0.0
  ↓ (fix: corregir parsing)
1.0.1
  ↓ (fix: manejar edge case)
1.0.2
```

**Scenario 2: Feature + fixes**
```
1.0.0
  ↓ (feat: agregar JSON support)
1.1.0
  ↓ (fix: corregir JSON parsing)
1.1.1
```

**Scenario 3: Breaking change**
```
1.0.0
  ↓ (feat: cambiar API - BREAKING CHANGE)
2.0.0
```

### Cómo Funciona el Versionado Automático

1. **Merge a main** → Release Pipeline ejecuta
2. **Analiza commits** desde último tag
3. **Determina tipo de cambio:**
   - ¿Hay `feat:`? → MINOR bump
   - ¿Hay `fix:` o `perf:`? → PATCH bump
   - ¿Solo `docs:`, `chore:`, etc.? → Sin release
4. **Crea nuevo tag** con versión calculada
5. **Publica en PyPI** con nueva versión

### Verificar Versión Actual

```bash
# Ver último tag
git describe --tags

# Ver todos los tags
git tag -l

# Ver releases en GitHub
# https://github.com/dev-brainstack/config-loader/releases
```

---

## ✨ Best Practices

Recomendaciones para trabajar efectivamente con el proyecto.

### Commits

- ✅ **Commits atómicos**: Un cambio lógico por commit
- ✅ **Mensajes claros**: Describe QUÉ y POR QUÉ
- ✅ **Usa tipos semánticos**: feat, fix, docs, etc.
- ✅ **Minúsculas**: `feat:` no `Feat:`
- ❌ **Evita**: Commits genéricos como "fix stuff" o "update"

**Buen commit:**
```
feat: agregar validación de esquema JSON

Implementa validación de esquema JSON usando jsonschema.
Permite a usuarios validar configuración contra esquemas.
```

**Mal commit:**
```
fix stuff
```

### Branches

- ✅ **Nombres descriptivos**: `feature/json-support`, `fix/yaml-parsing`
- ✅ **Ramas cortas**: Completa en 1-2 días
- ✅ **Actualiza desde main**: Antes de mergear
- ❌ **Evita**: Branches de larga vida, nombres genéricos

### Pull Requests

- ✅ **Descripción clara**: Explica QUÉ y POR QUÉ
- ✅ **PRs pequeños**: Más fáciles de revisar
- ✅ **Tests incluidos**: Nuevas features deben tener tests
- ✅ **CI debe pasar**: Antes de merge
- ❌ **Evita**: PRs gigantes, sin descripción

**Buen PR:**
```
## Descripción
Agrega soporte para archivos de configuración JSON.

## Cambios
- Implementa parser JSON
- Agrega validación de esquema
- Incluye 15 nuevos tests

## Testing
- Todos los tests pasan
- Cobertura aumenta a 95%
```

### Code Review

- ✅ **Sé constructivo**: Sugiere mejoras, no critiques
- ✅ **Sé rápido**: Revisa en 24 horas
- ✅ **Aprende**: Usa reviews para aprender
- ❌ **Evita**: Bloquear sin razón, comentarios vagos

### Testing

- ✅ **Tests para nuevas features**: Siempre
- ✅ **Tests para bugs**: Antes de fix
- ✅ **Mantén cobertura >90%**: Objetivo del proyecto
- ✅ **Tests claros**: Nombres descriptivos
- ❌ **Evita**: Tests que pasan por suerte

### Documentación

- ✅ **Actualiza README**: Si cambias comportamiento
- ✅ **Docstrings claros**: En funciones públicas
- ✅ **Ejemplos**: Para features complejas
- ❌ **Evita**: Documentación desactualizada

### Errores Comunes

1. **Commit directo a main**
   - ❌ Incorrecto: `git push origin main`
   - ✅ Correcto: Crear PR, pasar CI, mergear

2. **Mensaje de commit vago**
   - ❌ Incorrecto: `fix: bug`
   - ✅ Correcto: `fix: corregir parsing de YAML anidado`

3. **PR sin tests**
   - ❌ Incorrecto: Feature sin tests
   - ✅ Correcto: Feature + tests + documentación

4. **Ignorar CI failures**
   - ❌ Incorrecto: Mergear con CI rojo
   - ✅ Correcto: Corregir errores, pasar CI

5. **Branches no actualizadas**
   - ❌ Incorrecto: Mergear sin actualizar desde main
   - ✅ Correcto: `git rebase origin/main` antes de merge

---

## 🔧 Troubleshooting

Soluciones para problemas comunes.

### CI Pipeline Failures

#### Error: Format Check Failed

**Problema:** Black detecta código mal formateado

**Solución:**
```bash
# Auto-formatear código
make format

# O manualmente
black src/ test/

# Commit cambios
git add .
git commit -m "style: formatear código"
git push
```

#### Error: Tests Failed

**Problema:** Uno o más tests fallan

**Solución:**
```bash
# Ver qué tests fallan
pytest -v

# Ver detalles del error
pytest -v --tb=long

# Ejecutar test específico
pytest test/test_loader.py::test_name -v

# Corregir código
# ...

# Verificar que pasa
pytest

# Commit
git add .
git commit -m "fix: corregir test fallido"
git push
```

#### Error: Linting Failed

**Problema:** Flake8 detecta problemas de estilo

**Solución:**
```bash
# Ver errores
flake8 src/

# Corregir manualmente o con herramientas
# Algunos errores se pueden auto-corregir con black

# Verificar
flake8 src/

# Commit
git add .
git commit -m "style: corregir linting"
git push
```

### Merge Conflicts

**Problema:** Conflicto al mergear rama

**Solución:**
```bash
# Actualizar rama desde main
git fetch origin
git rebase origin/main

# O merge (menos limpio)
git merge origin/main

# Resolver conflictos manualmente
# Editar archivos con conflictos
# Buscar marcadores: <<<<<<<, =======, >>>>>>>

# Después de resolver
git add .
git rebase --continue  # Si usaste rebase
# O
git commit -m "Merge main"  # Si usaste merge

# Push
git push origin feature/mi-rama --force-with-lease
```

### Pull Request Feedback

**Problema:** Reviewer solicita cambios

**Solución:**
```bash
# Hacer cambios solicitados
# Editar archivos

# Commit con cambios
git add .
git commit -m "Cambios solicitados en review"

# Push (actualiza automáticamente el PR)
git push origin feature/mi-rama

# Responder en GitHub indicando que hiciste los cambios
```

### Check Pipeline Status

**Problema:** Quiero ver el estado del CI/Release pipeline

**Solución:**
```bash
# En GitHub:
# 1. Ve a tu PR
# 2. Scroll down a "Checks"
# 3. Haz click en "Details" para ver logs

# O desde línea de comandos:
# Ver commits recientes
git log --oneline

# Ver tags (releases)
git tag -l

# Ver releases en GitHub
# https://github.com/dev-brainstack/config-loader/releases
```

### Local Testing Before Push

**Problema:** Quiero verificar que todo pasa antes de push

**Solución:**
```bash
# Ejecutar todo localmente
make test      # Tests
make format    # Formateo
make lint      # Linting

# O todo junto
pytest && black --check src/ && flake8 src/ && mypy src/

# Si todo pasa, push con confianza
git push origin feature/mi-rama
```

---

## 📝 Licencia


MIT License - Ver LICENSE para más detalles.

---

## 🤝 Contribuciones

Las contribuciones son bienvenidas. Por favor:

1. Fork el repositorio
2. Crea una rama para tu cambio (`git checkout -b feature/mi-cambio`)
3. Commit tus cambios (`git commit -am 'Agrega mi cambio'`)
4. Push a la rama (`git push origin feature/mi-cambio`)
5. Abre un Pull Request

---

## 📞 Soporte

Para reportar bugs o sugerir mejoras, abre un issue en:
https://github.com/dev-brainstack/config-loader/issues

---

**Hecho con ❤️ por el equipo de desarrollo**
