Metadata-Version: 2.4
Name: strx-odoo-updater
Version: 1.0.1
Summary: Incremental Odoo module updater with checkpoint/resume support
Author-email: "STRACONX S.A." <desarrollo@straconx.com>
License: OPL-1
Project-URL: Repository, https://github.com/straconxsa/strx_tools
Keywords: odoo,migration,update,checkpoint,resume
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: psycopg2
Provides-Extra: checksums
Requires-Dist: click-odoo-contrib; extra == "checksums"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"

# strx-odoo-updater

[![PyPI version](https://badge.fury.io/py/strx-odoo-updater.svg)](https://pypi.org/project/strx-odoo-updater/)
[![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/)
[![License: OPL-1](https://img.shields.io/badge/License-OPL--1-blue.svg)](https://www.odoo.com/documentation/16.0/legal/licenses.html)

Wrapper de `click-odoo-update` con soporte de **checkpoint y reanudación
automática**. Evita reprocesar módulos ya actualizados cuando la migración
falla a mitad.

---

## El problema

`click-odoo-update` ejecuta la actualización completa dentro de **una sola
transacción** (`Registry.new()`). Si el proceso es interrumpido (OOM killer,
error SQL, timeout), PostgreSQL hace rollback y **todos los checksums
almacenados se revierten** — aunque 150 de los 200 módulos ya se hubieran
actualizado correctamente.

La siguiente ejecución vuelve a procesar los 200 módulos desde cero.

## La solución

`strx-odoo-updater` actualiza **módulo por módulo**. Cada módulo se
ejecuta en un subproceso independiente (`odoo-bin -u <module>
--stop-after-init`) que tiene su propia transacción y hace su propio
`commit`. Tras cada módulo exitoso guarda el progreso en
`ir_config_parameter`:

| Clave en `ir_config_parameter` | Contenido |
|---|---|
| `strx.update_resume.checkpoint` | JSON: lista de módulos completados |
| `strx.update_resume.failed` | JSON: lista de módulos fallidos |
| `strx.update_resume.last_update` | ISO timestamp del último progreso |

Al reiniciar tras un fallo, el script lee el checkpoint y **salta
automáticamente los módulos ya completados**.

---

## Instalación

```bash
# Desde PyPI (recomendado):
pip install strx-odoo-updater

# Dentro del venv de Odoo 16:
/opt/odoo16/venv16/bin/pip install strx-odoo-updater

# Modo editable (para desarrollo):
pip install -e /opt/odoo16/strx_tools/strx_odoo_updater
```

### Dependencias

| Paquete | Requerido | Notas |
|---|---|---|
| `psycopg2` | Sí | Conexión directa a PostgreSQL |
| `click-odoo-contrib` | Opcional | Para cálculo exacto de checksums |
| `odoo-bin` | En PATH/env | Ejecutable de Odoo (no pip) |

---

## Uso rápido

```bash
# Exportar PYTHONPATH si Odoo no está instalado como paquete pip:
export PYTHONPATH=/opt/odoo16/odoo-server:$PYTHONPATH

# Primera ejecución (o reanudación tras fallo):
strx-odoo-updater \
    -c /opt/odoo16/conf/odoo-server.conf \
    -d mi_base_de_datos \
    --i18n-overwrite \
    --skip-failed

# Ver qué módulos quedan pendientes (sin ejecutar nada):
strx-odoo-updater -c ... -d ... --list-only

# Limpiar checkpoint y empezar desde cero:
strx-odoo-updater -c ... -d ... --reset

# Sin pase final de click-odoo-update:
strx-odoo-updater -c ... -d ... --no-final-update
```

---

## Referencia de opciones CLI

| Opción | Descripción |
|---|---|
| `-c`, `--config PATH` | Archivo de configuración de Odoo (**requerido**) |
| `-d`, `--database NAME` | Nombre de la base de datos (**requerido**) |
| `--i18n-overwrite` | Sobreescribir traducciones existentes |
| `--reset` | Elimina el checkpoint y sale |
| `--list-only` | Lista módulos pendientes/completados sin actualizar |
| `--skip-failed` | Continúa aunque un módulo falle (registra el fallo y sigue) |
| `--no-final-update` | Omite el pase final de `click-odoo-update` |

---

## Variables de entorno

Permiten reusar el paquete en diferentes instalaciones de Odoo sin
modificar el código:

| Variable | Default | Descripción |
|---|---|---|
| `ODOO_BIN` | `/opt/odoo16/odoo-server/odoo-bin` | Ruta al ejecutable `odoo-bin` |
| `CLICK_ODOO_UPDATE` | `<venv>/bin/click-odoo-update` | Ruta a `click-odoo-update` |
| `ODOO_SERVER_PATH` | `/opt/odoo16/odoo-server` | Raíz del servidor para PYTHONPATH |

### Ejemplo para Odoo 17 en otro servidor

```bash
export ODOO_BIN=/opt/odoo17/odoo-bin
export ODOO_SERVER_PATH=/opt/odoo17
export CLICK_ODOO_UPDATE=/opt/odoo17/venv/bin/click-odoo-update
export PYTHONPATH=/opt/odoo17:$PYTHONPATH

strx-odoo-updater -c /opt/odoo17/conf/odoo.conf -d produccion --skip-failed
```

---

## Flujo interno

```
strx-odoo-updater
│
├─ Conecta a PostgreSQL directo (lee configuración de odoo.conf)
├─ Detecta módulos con checksums distintos (igual que click-odoo-update)
│   └─ Fallback: módulos con state='to upgrade'
├─ Lee checkpoint de ir_config_parameter
├─ Calcula: pendientes = detectados - ya_completados
│
└─ Para cada módulo pendiente:
    ├─ odoo-bin -u <module> --stop-after-init --no-http (subprocess)
    ├─ returncode == 0?
    │   ├─ SÍ  → checkpoint += module, actualiza checksum, commit
    │   └─ NO  → failed += module
    │           └─ --skip-failed? continúa : detiene y guarda estado
│
└─ Al finalizar (sin fallos):
    ├─ click-odoo-update (pase final: detecta 0 diferencias)
    └─ Limpia checkpoint de ir_config_parameter
```

---

## Uso como librería Python

```python
from strx_odoo_updater.core import (
    _connect,
    _get_modules_to_update,
    _load_checkpoint,
    _save_checkpoint,
    _update_single_module,
    _sync_checksum,
)

conn = _connect("/etc/odoo/odoo.conf", "mi_bd")
pending = _get_modules_to_update(conn, "/etc/odoo/odoo.conf")
done = _load_checkpoint(conn)

for module in pending:
    if module in done:
        continue
    ok = _update_single_module("/etc/odoo/odoo.conf", "mi_bd", module)
    if ok:
        done.add(module)
        _save_checkpoint(conn, done)
        _sync_checksum(conn, module)

conn.close()
```

---

## Casos de uso

### Migración de base de datos (Odoo 12 → 16)

Durante migraciones largas (200+ módulos), es habitual encontrar errores SQL
que detienen el proceso. Con `strx-odoo-updater`:

1. Se ejecuta hasta el módulo problemático
2. Se diagnostica y corrige el error en la BD
3. Se vuelve a ejecutar — continúa **desde donde quedó**, no desde cero

### Servidor con poca RAM (OOM killer)

Si el proceso es matado por el sistema operativo por falta de memoria:

```bash
# El módulo que causó el OOM queda registrado en .failed
# Los módulos anteriores están en el checkpoint

# Próxima ejecución: salta los ya completados, reintenta el fallido
strx-odoo-updater -c ... -d ... --skip-failed
```

### Actualización nocturna desatendida

```bash
#!/bin/bash
# /etc/cron.d/odoo-update — ejecuta a las 2am
export PYTHONPATH=/opt/odoo16/odoo-server:$PYTHONPATH
/opt/odoo16/venv16/bin/strx-odoo-updater \
    -c /opt/odoo16/conf/odoo-server.conf \
    -d produccion \
    --i18n-overwrite \
    --skip-failed \
    >> /var/log/odoo/strx-updater.log 2>&1
```

---

## Diagnóstico del checkpoint

```sql
-- Ver estado del checkpoint directamente en PostgreSQL:
SELECT key, 
       left(value, 120) as value_preview,
       write_date
FROM ir_config_parameter
WHERE key LIKE 'strx.update_resume.%'
ORDER BY key;

-- Limpiar checkpoint manualmente:
DELETE FROM ir_config_parameter WHERE key LIKE 'strx.update_resume.%';
```

---

## Desarrollo

```bash
git clone <repo>
cd strx_tools/strx_odoo_updater
pip install -e ".[dev]"
pytest tests/
```

### Estructura del proyecto

```
strx_odoo_updater/
├── pyproject.toml              ← Configuración del paquete pip
├── README.md                   ← Este archivo
└── strx_odoo_updater/
    ├── __init__.py             ← Versión del paquete
    ├── core.py                 ← Lógica principal (importable)
    └── cli.py                  ← Entry point del comando CLI
```

### Publicar nueva versión a PyPI

```bash
# 1. Actualizar version en pyproject.toml y __init__.py
# 2. Construir y publicar:
cd strx_odoo_updater/
pip install build twine
python -m build
twine upload dist/*
```

---

## Licencia

OPL-1 — © STRACONX S.A.
