BookinglyTech News
Infraestructura

Submódulos de Git y doble CI: las dos trampas que este equipo pagó caro

Un proyecto montado sobre cinco repositorios independientes y submódulos de Git desde el primer commit cuenta qué le costó mantenerlo: punteros sin subir y una CI que prueba el mismo código en dos contextos distintos.

2 min de lecturaDev.to0 vistas

El proyecto Pipeline vive repartido en cinco repositorios independientes: pipeline, engine, design-docs, user-docs y ude_promotion. No llegaron ahí por necesidad: eligieron los submódulos de Git en el primer commit, con el archivo .gitmodules ya en su sitio, buscando historial separado, aislamiento de CI y permisos distintos por repositorio. En papel era limpio. En producción ha costado pipelines rotos, y el equipo ha contado por dónde.

La trampa del gitlink

Editar dentro de engine/ no es editar un directorio más. El flujo exige dos push: primero desde la carpeta del propio submódulo, y después, ya en la raíz de Pipeline, un commit del puntero actualizado y su push correspondiente. Olvidar el primer paso es facilísimo, y no se nota en local: el commit existe en el portátil, compila y los tests pasan en verde. El problema aparece en el checkout limpio de CI, cuando git submodule update --init responde con

fatal: remote error: upload-pack: not our ref <sha>

Un día cayeron tres submódulos a la vez (design-docs, engine y user-docs), todos por la misma causa. El cuarto, ude_promotion, que aloja el sitio, se había quedado tan atrás respecto a su rama remota que no bastó con un push: hubo que pasar por un rebase interactivo antes de subirlo.

Una CI, dos personalidades

El repositorio engine es el núcleo y arrastra un problema de identidad: es un repo autónomo con su propio build y, a la vez, un submódulo que se prueba dentro del proyecto padre. Los dos contextos chocaron en dos tandas.

La primera fue por dependencias ausentes. El test test_integration_scripts.py llamaba a los módulos verify_pages/check_links, que solo existen en Pipeline. En un checkout suelto de engine esos archivos no están en disco. El parche fue una línea en ci.yml ignorando ese test con pytest --ignore=....

La segunda fue peor, porque afectaba a un patrón usado en varios sitios: Path(__file__).resolve().parents[2]. En la ejecución anidada ese salto llegaba justo a la raíz del padre, pero en la ejecución autónoma se pasaba de largo y aterrizaba en el sistema de ficheros del runner, con AssertionError. Ahí dejaron los parches puntuales y adoptaron una convención: los tests buscan un directorio marcador (.workspace_config) en el nivel ascendido. Si está, se ejecutan; si no, se saltan con pytest.mark.skipif sin poner la CI en rojo.

La conclusión del equipo es sensata: una arquitectura multi-repo no regala acoplamiento débil el primer día. Impone disciplina de push y obliga a escribir código que aguante varios contextos de ejecución. Lo que no cuenta esta entrega es que los problemas de Git y del doble CI fueron solo la antesala: después llegó un fallo de red que describen como un fantasma en los cables.