CreaRack-SL

Cadena con saltos de línea reales en djangojs.po rompía msgfmt — la imagen v1.87.0 no se reconstruyó

Cuándo

29-08-2026. El commit 991773bd (v1.87.0, “papelera de dispositivos”) introdujo el bug; se detectó y corrigió el mismo día con el commit 39900321 (v1.87.1, PR #471). No hubo caída de PROD — la app siguió sirviendo la imagen anterior — pero la v1.87.0 nunca llegó a construirse ni desplegarse como imagen nueva.

Síntomas visibles

La imagen Docker de la v1.87.0 no se construyó. El job Docker Build del CI no se disparaba: su gate (job changes en ci.yml) solo miraba cambios en Dockerfile* / requirements*.txt / compose*.yml. Un cambio en locale/es/LC_MESSAGES/djangojs.po no lo activaba, aunque las traducciones se compilan dentro de esa misma imagen (django-admin compilemessages).

Causa raíz

Doble causa, cada una suficiente por separado para tumbar el build:

  1. El .po tenía una cadena con saltos de línea reales: el msgid/msgstr del texto de confirmación “Delete… everywhere?” (introducido en 991773bd, feature de papelera de dispositivos) llevaba un salto de línea literal dentro de las comillas en vez de la secuencia escapada \n. El formato .po exige que un mensaje multilínea vaya como una sola línea de texto con \n escapado — una cadena con salto real rompe el parser de msgfmt.
  2. El gate de Docker Build no cubría locale/*.po: aunque el .po se hubiera corregido a tiempo, el job que reconstruye la imagen (donde vive compilemessages) no se disparaba por un cambio de traducciones — solo por Dockerfile/requirements/compose.

Fix aplicado

Commit 39900321 (PR #471, v1.87.1):

  • locale/es/LC_MESSAGES/djangojs.po: la cadena rota se reescribe con \n escapado en una sola línea, tanto en msgid como en msgstr.
  • .github/workflows/ci.yml: el regex del job changes añade locale/.*\.po a los triggers que ponen docker=true.

Lecciones

  • Un .po roto no revienta los jobs de lint/test (no compilan mensajes) — revienta silenciosamente el build de la imagen. Si además ese job ni siquiera se dispara, el fallo queda invisible hasta que alguien nota que el deploy no trae la versión nueva.
  • Cualquier fichero que la imagen Docker procese en tiempo de build (aquí, compilemessages sobre locale/) tiene que estar en el gate de Docker Build, no solo Dockerfile/requirements/compose.

Preventivos futuros

  • El gate de CI ya cubre locale/*.po desde este commit.
  • Pendiente de valorar (fuera del alcance de este fix): un check de formato en pre-commit/CI que rechace saltos de línea reales dentro de msgid/msgstr antes de llegar al build — el proyecto ya usa check_po.py en otros commits de i18n para validar formato, pero no está confirmado que cubra este caso concreto.

Véase también

  • [[ia-tech—automatismos—metodo—ci-workflows]]
  • [[crearack—network—device-page]]
  • [[incident—20260821—add-rack-doble-post-manifest-hash]]
  • [[crearack-tech—admin—dokploy-internals]]
  • [[concept—infra—gh-actions-watchdog]]