npm v12 ya está aquí: lo que rompió en mi CI y cómo lo arreglé

Descubre cómo npm v12 puede romper tus pipelines de CI en silencio y aprende a solucionar cada problema con pasos concretos y ejemplos reales.

jueves, 30 de julio de 2026 • 3 min de lectura • Equipo Q2BSTUDIO

Problemas silenciosos de npm v12 y soluciones prácticas

El 8 de julio, cuando npm v12 alcanzó la versión estable, muchos equipos de desarrollo despertaron con pipelines rotos. En Q2BSTUDIO, empresa especializada en aplicaciones a medida, lo vivimos en primera persona: tres pipelines de CI se tiñeron de rojo en menos de doce horas. Lo más inquietante no fue el color, sino el silencio. Una de nuestras integraciones pasó verde durante cuatro horas antes de que alguien notara que los contenedores desplegados estaban rotos. No hubo alertas, ni errores en los logs. El código se desplegó, la aplicación arrancó, pero en la primera petición real a un módulo nativo, todo colapsó. Este artículo no es una lista genérica de cambios; es la crónica de cómo identificamos cada problema, por qué pasaron desapercibidos y cómo los solucionamos, con el enfoque práctico que aplicamos en nuestros proyectos de IA, ciberseguridad y cloud.

El primer escollo lo encontramos con npm ci. Durante años, esta orden había sido sinónimo de fiabilidad. En v12, dejó de ejecutar los scripts de postinstalación de módulos nativos como sharp, bcrypt o esbuild, a menos que estuvieran explícitamente autorizados en una nueva lista blanca. Lo peor: el código de salida seguía siendo cero. El pipeline de CI informaba éxito, el Docker se construía sin errores, el contenedor se desplegaba y la aplicación respondía a peticiones básicas. Pero cualquier funcionalidad que requiriera compilar un binario nativo, como el procesamiento de imágenes con sharp, lanzaba un Error: Cannot find module. En Q2BSTUDIO desarrollamos soluciones de cloud AWS/Azure y agentes de IA que dependen de estos módulos, así que necesitábamos una solución robusta, no parches temporales. Tras auditar los scripts necesarios con npm approve-scripts --allow-scripts-pending dentro del mismo entorno de Docker que nuestra CI (porque node-gyp rebuild es condicional por sistema operativo), generamos una lista blanca de entre tres y ocho paquetes por proyecto. Esa lista se almacena en package.json bajo la clave allowScripts y se versiona. Al hacer commit, la CI la recoge automáticamente sin tocar .npmrc. El cambio fue inmediato: los módulos nativos volvieron a compilarse y las pruebas de integración dejaron de fallar en producción.

Pero la lista blanca no basta si no validas que los módulos realmente se han compilado. Nuestra suite de tests unitarios mockeaba sharp, por lo que los tests pasaban verdes aunque el binario no existiera. Añadimos un test de humo justo después de npm ci, ejecutando un pequeño script que requiere cada módulo nativo crítico y lanza una excepción si falla. En Q2BSTUDIO lo hemos incorporado a nuestra plantilla de CI para todos los proyectos que usan sharp, bcrypt, canvas, prisma o better-sqlite3. También lo aplicamos en proyectos de BI / Power BI donde el rendimiento de cálculos nativos es crítico. Es una línea de defensa que antes no existía y que ahora es obligatoria en nuestro checklist de despliegue.

Otro problema silencioso afectó a nuestras herramientas CLI publicadas. npm v12 ignora npm-shrinkwrap.json sin avisar. En su lugar, usa package-lock.json (o lo genera si no existe). Esto significa que paquetes publicados que dependían del shrinkwrap para garantizar versiones exactas de dependencias ahora entregaban a los usuarios combinaciones no testeadas. En Q2BSTUDIO, donde desarrollamos herramientas internas de automatización y ciberseguridad, esto podía comprometer la reproducibilidad. La solución fue migrar a bundleDependencies: empaquetar las dependencias críticas dentro del tarball de publicación. Así, independientemente de la versión de npm del usuario, las versiones son las que testeamos. Para proyectos legacy que no pueden migrar, renombramos el shrinkwrap a package-lock.json y lo copiamos en el flujo de publicación con cp npm-shrinkwrap.json package-lock.json && npm publish --lockfile-version=3.

La siguiente sorpresa llegó al actualizar nuestros scripts de release. Usábamos npm view my-package version --json para obtener la última versión publicada y compararla. En v12, la salida JSON siempre devuelve un array, incluso para un solo resultado. Nuestro script de Makefile hacía jq -r '.' y obtenía un string en v11, pero en v12 recibía [

¿UNA PAUSA?

Juega un momento antes de irte

NUESTROS SERVICIOS

Cómo podemos ayudarte

¿Tienes un proyecto en mente?

Cuéntanos tu visión y la convertimos en una solución de software. Sea cual sea el alcance, hacemos realidad tu idea.