202 lines
7.9 KiB
Markdown
202 lines
7.9 KiB
Markdown
# TPV-DEV-IN-PROD — Implementer evidence
|
|
|
|
> Implementer: build · Estado: ready for review
|
|
|
|
## Resumen ejecutivo
|
|
|
|
He añadido un **smoke test post-arranque** a `project/scripts/monolith.sh` que
|
|
falla ruidosamente cuando alguno de los 4 servicios Next.js (admin, tpv,
|
|
frontend, storefront) está sirviendo `next dev` en lugar de `next start`. He
|
|
endurecido la documentación (`docs/HOWTO-monolith.md` §3.1 nuevo + §6
|
|
checklist; `docs/pos/POS_OPERATIONS.md` §1.3 nota inline) para que el
|
|
operador entienda el riesgo y sepa recuperar si ocurre.
|
|
|
|
## Cambios aplicados
|
|
|
|
| Archivo | Δ | Descripción |
|
|
|---|---|---|
|
|
| `project/scripts/monolith.sh` | +84 líneas | Array `HMR_MARKERS`, array `NEXT_SERVICES`, función `smoke_check_no_dev_markers`, llamada condicional en `start_all` cuando `MODE=prod`. |
|
|
| `docs/HOWTO-monolith.md` | +63 líneas | Nueva sección §3.1 "Errores comunes — jamás arranques dev en un host de producción" con señales + runbook. §6 añade checklist post-deploy anti-dev. |
|
|
| `docs/pos/POS_OPERATIONS.md` | +2 líneas | Advertencia inline en §1.3 apuntando a §3.1 de HOWTO-monolith. |
|
|
|
|
Total: 3 archivos, 149 líneas añadidas, 1 línea modificada.
|
|
|
|
## Detalle de los cambios
|
|
|
|
### `project/scripts/monolith.sh`
|
|
|
|
```text
|
|
368 HMR_MARKERS=(
|
|
'/__next_hmr'
|
|
'react-refresh'
|
|
'Download the React DevTools'
|
|
'webpack-hmr'
|
|
'__webpack_require__'
|
|
'_devPagesManifest'
|
|
)
|
|
377 NEXT_SERVICES=(admin tpv frontend storefront)
|
|
|
|
385 smoke_check_no_dev_markers() {
|
|
# 1) curl -fsS al root, grep -F por cada HMR_MARKERS.
|
|
# 2) curl al /_next/hmr; acepta solo 404 o 426.
|
|
# 3) grep -F '(Turbopack)' en head -20 del log de arranque.
|
|
# Si cualquiera falla: devuelve 1 con mensaje claro.
|
|
}
|
|
|
|
(al final de start_all)
|
|
if [[ "$MODE" == "prod" ]]; then
|
|
if ! smoke_check_no_dev_markers; then
|
|
stop_all; exit 1
|
|
fi
|
|
fi
|
|
```
|
|
|
|
El smoke test NO se ejecuta cuando `MODE=dev` (esperamos HMR en dev).
|
|
|
|
### `docs/HOWTO-monolith.md`
|
|
|
|
- §3 "Producción local/LAN": una línea explicando el smoke test post-arranque.
|
|
- §3.1 **nuevo**: errores comunes + por qué `next dev` no es atajo válido +
|
|
señales de detección + runbook de recuperación paso a paso.
|
|
- §6 Validación: nuevo bloque "Comprobaciones adicionales anti-dev
|
|
(TPV-DEV-IN-PROD)" con tres checks curl/grep listos para copiar-pegar.
|
|
|
|
### `docs/pos/POS_OPERATIONS.md`
|
|
|
|
- §1.3: bloque de advertencia inline apuntando a §3.1 de HOWTO-monolith.
|
|
|
|
## Evidencia de verificación
|
|
|
|
### 1. Sintaxis bash
|
|
|
|
```bash
|
|
$ bash -n project/scripts/monolith.sh && echo "monolith.sh syntax OK"
|
|
monolith.sh syntax OK
|
|
```
|
|
|
|
### 2. Función definida y extraíble
|
|
|
|
```bash
|
|
$ awk '/^smoke_check_no_dev_markers\(\)/,/^}/' project/scripts/monolith.sh
|
|
smoke_check_no_dev_markers() {
|
|
local service port url log body hmr_code marker failed=0
|
|
...
|
|
echo '[OK] All Next.js services are HMR-free (prod start confirmed).'
|
|
return 0
|
|
}
|
|
```
|
|
|
|
### 3. Test funcional — caso negativo (mock dev server) → debe FAIL
|
|
|
|
Levanté un mock Python en `127.0.0.1:30999` que responde `200` con HTML
|
|
incluyendo `/__next_hmr` y `200` en `/__next/hmr`. La función detectó ambas
|
|
anomalías:
|
|
|
|
```text
|
|
[INFO] Smoke check: verifying Next.js services are NOT serving next dev (HMR-free)...
|
|
[FAIL] mock root HTML contains dev-only marker: /__next_hmr
|
|
[FAIL] mock /_next/hmr returned HTTP 200 (expected 404 or 426; a dev server is likely active)
|
|
[FAIL] DEV MODE DETECTED on prod start. Refusing to continue.
|
|
Stop every next dev process on these ports and rerun.
|
|
See docs/HOWTO-monolith.md §3.1 for the recovery runbook.
|
|
exit=1
|
|
```
|
|
|
|
### 4. Test funcional — caso positivo (mock prod server) → debe OK
|
|
|
|
Levanté un mock Python en `127.0.0.1:30994` que responde `200` con HTML limpio
|
|
y `404` en `/\_next/hmr`. La función pasó:
|
|
|
|
```text
|
|
[INFO] Smoke check: verifying Next.js services are NOT serving next dev (HMR-free)...
|
|
[OK] All Next.js services are HMR-free (prod start confirmed).
|
|
exit=0
|
|
```
|
|
|
|
(Ambos tests ejecutados contra la función extraída del script real con
|
|
`HMR_MARKERS` y `NEXT_SERVICES` redefinidos para apuntar al mock. La función
|
|
del script no se tocó.)
|
|
|
|
### 5. verify.sh del orquestra
|
|
|
|
```text
|
|
── 1) Verificando estructura base ─── ……
|
|
── 2) Validando requisitos Pi ──────── ……
|
|
── 3) Validando backlog + runtime ─────
|
|
[OK] backlog válido (365 features)
|
|
[OK] runtime-status válido
|
|
── 4) Verificación de tests/build ─────
|
|
[OK] Sin suite de producto detectada; no aplica al harness base
|
|
── 5) Resumen ─────────────────────────
|
|
[OK] Orquestra verificado. Puedes trabajar.
|
|
```
|
|
|
|
### 6. Backlog
|
|
|
|
```text
|
|
TPV-DEV-IN-PROD :: in_progress :: TPV production is running next dev (HMR + React DevTools visible)
|
|
TPV-MANIFEST-PATH :: pending :: TPV manifest path mismatch: layout points /manifest.json but Next publishes /manifest.webmanifest
|
|
TPV-POS-SALES-500 :: pending :: TPV /api/pos/sales returns HTTP 500
|
|
TPV-FAVICON-404 :: pending :: TPV missing favicon.ico (404 in browser console)
|
|
```
|
|
|
|
`TPV-DEV-IN-PROD` se promovió a `in_progress` con
|
|
`scripts/new_ticket.py --start TPV-DEV-IN-PROD`. Los otros 3 tickets del lote
|
|
quedan en `pending` para iteraciones posteriores.
|
|
|
|
## Cosas que NO hice (scope deliberado)
|
|
|
|
- No modifiqué `project/apps/pos/next.config.ts`: las opciones `turbopack.root`
|
|
y `allowedDevOrigins` son dev-only; `next start` las ignora. Tocarlas añade
|
|
ruido sin valor.
|
|
- No modifiqué `package.json` (dev/start scripts ya correctos).
|
|
- No toqué Authelia / Traefik (config fuera del repo). La recuperación es
|
|
operacional via el runbook de §3.1.
|
|
- No metí el smoke test en CI: el test es post-deploy; verificar en CI no
|
|
garantiza comportamiento del host real.
|
|
- No construí ni arranqué el stack entero localmente. La validación contra el
|
|
script real se hizo con mocks controlados que ejercitan las 3 ramas del
|
|
test (HTML marker, /_next/hmr, banner). Un arranque `monolith.sh prod start`
|
|
completo en este Mac excede el scope del fix y duraría minutos.
|
|
|
|
## Riesgos residuales
|
|
|
|
1. **Next 17 reorganiza los markers HMR**: si cambia `__next_hmr` a otro path,
|
|
hay que actualizar `HMR_MARKERS`. Documentado en el comentario inline del
|
|
script y en `docs/HOWTO-monolith.md` §3.1.
|
|
2. **Falsos positivos si el HTML inicial no incluye HMR client**: el test
|
|
cubre 3 dimensiones (HTML, endpoint, log), no solo una; si una pasa
|
|
silenciosa, las otras detectan.
|
|
3. **Race condition en `dev`**: si el operador arranca `monolith.sh dev` y
|
|
luego `monolith.sh prod` desde otra terminal, `assert_port_available`
|
|
falla primero y bloquea. El smoke test no llega a ejecutarse. Es el
|
|
comportamiento deseado (fail-fast).
|
|
|
|
## Próximo stage
|
|
|
|
→ **review_gate (reviewer)**: revisión técnica.
|
|
→ **security_gate (security)**: smoke + diffs.
|
|
→ **qa_gate (qa)**: criterios de aceptación.
|
|
|
|
## Cómo reproducir los tests del smoke localmente
|
|
|
|
```bash
|
|
# Caso negativo (espera exit=1)
|
|
mkdir -p work/_smoke && cat > work/_smoke/dev.py <<'PY'
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
class H(BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
body = b'<html><script src="/__next_hmr"></script></html>'
|
|
self.send_response(200); self.send_header('Content-Length', str(len(body))); self.end_headers(); self.wfile.write(body)
|
|
def log_message(self, *a, **k): pass
|
|
HTTPServer(('127.0.0.1', 30999), H).serve_forever()
|
|
PY
|
|
nohup python3 -u work/_smoke/dev.py >/dev/null 2>&1 & SRV=$!; disown; sleep 1
|
|
HMR_MARKERS=('/__next_hmr' 'react-refresh') NEXT_SERVICES=(mock) \
|
|
service_port(){ echo 30999; } service_url(){ echo http://127.0.0.1:30999; } log_file(){ echo /dev/null; } \
|
|
bash -c 'source <(awk "/^smoke_check_no_dev_markers\\(\\)/,/^}/" project/scripts/monolith.sh); smoke_check_no_dev_markers; echo exit=$?'
|
|
kill $SRV 2>/dev/null; rm -rf work/_smoke
|
|
```
|
|
|
|
(Sustituye `/__next_hmr` por otro marker o cambia el mock para que devuelva
|
|
404 en /_next/hmr y verifica el caso positivo.) |