Yarn: Espacios de trabajo: archivo de bloqueo por espacio de trabajo

Creado en 28 feb. 2018  ·  54Comentarios  ·  Fuente: yarnpkg/yarn

¿Quieres solicitar una función o informar de un error ?
característica

¿Cuál es el comportamiento actual?
El uso de espacios de trabajo de hilo para un monorepo que incluye un módulo de nodo de nivel superior crea solo un yarn.lock en la raíz del monorepo, sin yarn.lock que sea específico para el módulo de nodo de nivel superior.

¿Cuál es el comportamiento esperado?
Quiero usar espacios de trabajo de hilo para administrar un monorepo que incluye tanto aplicaciones (módulos de nodo de nivel superior) como bibliotecas. Pero tener solo un archivo yarn.lock en la raíz del monorepo me impide empaquetar mi aplicación en una imagen de la ventana acoplable, por ejemplo. Me encantaría una forma de obtener un archivo yarn.lock para los espacios de trabajo elegidos que necesitan tener uno propio, porque ese espacio de trabajo puede usarse más tarde fuera del monorepo.

Un ejemplo:
Si tengo un monorepo con 2 espacios de trabajo: workspace-a y workspace-b . workspace-a utiliza algunos de los módulos exportados de workspace-b . Si quiero empaquetar workspace-a en una imagen de la ventana acoplable (o cualquier otra forma de empaquetar ese espacio de trabajo por sí solo, sin el monorepo completo), no tengo un yarn.lock para ello. Eso significa que cuando mueva los archivos de workspace-a a un entorno diferente aparte del monorepo, me faltará un archivo yarn.lock y al instalar las dependencias, perderé todos los ventajas de un archivo de bloqueo (sabiendo que estoy instalando las mismas dependencias utilizadas en el desarrollo).

Estoy bastante sorprendido de no haber podido encontrar un problema al respecto. ¿Soy el único que quiere trabajar con monorepos de esa manera? ¿Quizás me estoy perdiendo algo? Mi solución actual es usar lerna sin levantar nada, así que tendré un archivo de bloqueo por paquete.
La función nohoist recientemente lanzada tampoco parece ayudar (aunque esperaba), ya que no crea un yarn.lock por espacio de trabajo.
Este problema está relacionado de alguna manera con este comentario sobre otro tema. Pensé que podría merecer una edición propia.

Por favor, mencione su versión de node.js, yarn y sistema operativo.
nodo 8.9.3, hilo 1.5.1, OSX 10.13.3

cat-feature triaged

Comentario más útil

Entonces, para nosotros, no queremos empaquetar todo el monorepo en el contenedor de la ventana acoplable resultante. Estamos usando Docker en producción y esas imágenes deben ser lo más claras posible. Nuestro repositorio mono es bastante grande y contiene varios microservicios que comparten código entre ellos mediante paquetes de biblioteca (y algunas de las bibliotecas son relevantes para algunos de los microservicios, pero no para todos). Entonces, cuando empaquetamos un microservicio, queremos que la imagen contenga los archivos de ese microservicio y cualquier otra dependencia como dependencias adecuadas, descargadas de nuestro registro privado y compiladas para el arco de la imagen de la ventana acoplable.

Entonces, creo que la consideración principal aquí es mantener nuestras imágenes de la ventana acoplable lo más ligeras posible, y empaquetar todo el monorepo no se ajusta a nuestras necesidades. Además, cuando ejecutamos "hilo" dentro de la imagen del microservicio, no queremos tener enlaces simbólicos allí, solo una dependencia normal.

La solución aquí no tiene que ser la creación de un archivo yarn.lock por espacio de trabajo, también podría ser un comando de hilo, que ayuda en el proceso de empaquetar un espacio de trabajo determinado, generar un archivo yarn.lock para un espacio de trabajo bajo demanda, etc. ..

Espero que haya ayudado a aclarar el caso de uso ... 🍻

Todos 54 comentarios

basado en el blog de hilo :

Cuando publica un paquete que contiene un yarn.lock, ningún usuario de esa biblioteca se verá afectado por él. Cuando instala dependencias en su aplicación o biblioteca, solo se respeta su propio archivo yarn.lock. Los archivos de bloqueo dentro de sus dependencias serán ignorados.

no parece necesario agrupar yarn.lock al publicar el paquete individual ... es más un artefacto de desarrollo para todo el repositorio, por lo tanto, no es necesario ponerlo en cada paquete.

@connectdotz puede que no sea necesario para una biblioteca o paquete publicado, pero para crear un contenedor de ventana acoplable, querrá implementarlo en algún lugar donde definitivamente estaría.

seguro ... pero ¿el contenedor docker de desarrollo no tendría todo el repositorio y, por lo tanto, el hilo.lock de todos modos? Puedo ver que usamos contenedores docker para probar nuestro proyecto monorepo para diferentes sistemas operativos o plataformas, en cuyo caso simplemente implementamos todo el repositorio y su yarn.lock. ¿Puede darme un caso de uso de ejemplo en el que necesite implementar paquetes individuales del proyecto monorepo en contenedores Docker durante el ciclo de desarrollo, para que pueda obtener una comprensión más concreta ...

Entonces, para nosotros, no queremos empaquetar todo el monorepo en el contenedor de la ventana acoplable resultante. Estamos usando Docker en producción y esas imágenes deben ser lo más claras posible. Nuestro repositorio mono es bastante grande y contiene varios microservicios que comparten código entre ellos mediante paquetes de biblioteca (y algunas de las bibliotecas son relevantes para algunos de los microservicios, pero no para todos). Entonces, cuando empaquetamos un microservicio, queremos que la imagen contenga los archivos de ese microservicio y cualquier otra dependencia como dependencias adecuadas, descargadas de nuestro registro privado y compiladas para el arco de la imagen de la ventana acoplable.

Entonces, creo que la consideración principal aquí es mantener nuestras imágenes de la ventana acoplable lo más ligeras posible, y empaquetar todo el monorepo no se ajusta a nuestras necesidades. Además, cuando ejecutamos "hilo" dentro de la imagen del microservicio, no queremos tener enlaces simbólicos allí, solo una dependencia normal.

La solución aquí no tiene que ser la creación de un archivo yarn.lock por espacio de trabajo, también podría ser un comando de hilo, que ayuda en el proceso de empaquetar un espacio de trabajo determinado, generar un archivo yarn.lock para un espacio de trabajo bajo demanda, etc. ..

Espero que haya ayudado a aclarar el caso de uso ... 🍻

@netanelgilad gracias por los detalles, ayuda a aclarar que su caso de uso es más sobre la publicación de paquetes individuales, para producción o desarrollo, en contenedores Docker. Únase a la discusión en el n. ° 4521 para que podamos comenzar a consolidarlos.

Si bien puedo ver el uso de archivos de bloqueo individuales, no son necesarios. Si ejecuta la ventana acoplable desde la raíz del repositorio con el indicador -f apuntando a los archivos individuales, tendrá todo el repositorio como contexto y podrá copiar en package.json y yarn.lock desde la raíz.

Solo necesita el package.json para los paquetes que creará en la imagen y yarn solo instalará paquetes para los archivos package.json que haya copiado, incluso si yarn.lock incluye mucho más.

EDITAR: Dicho esto. hace que la caché de la ventana acoplable no se use para cambios de paquete en ningún paquete aunque no esté incluido en la compilación

4206 está relacionado / duplicado, y el caso de uso descrito allí es exactamente el problema al que nos enfrentamos:

Digamos que tenemos diez paquetes diferentes. Queremos que todos vivan en su propio repositorio para que las personas puedan trabajar en ellos de forma independiente si así lo desean, pero también queremos poder vincularlos si es necesario. Para hacer esto, tenemos un mega repositorio con un submódulo para cada paquete y un package.json que hace referencia a cada uno de estos submódulos como un espacio de trabajo.

Tengo problemas similares con los espacios de trabajo. Mi proyecto es un web-app que depende de muchos paquetes locales:

web-app/
|--node_modules/
|--packages/
|  |--controls/
|  |  |--src/
|  |  |--package.json
|  |--utils/
|     |--src/
|     |--package.json
|--src/
|--package.json
|--yarn.lock

Los paquetes de espacio de trabajo controls y utils no se publican y son utilizados por rutas. El problema es que necesito lanzar el paquete controls ( yarn pack ) y no quiero construirlo / probarlo solo. Eso significa que quiero hacer yarn install dentro de web-app/packages/constols/ . Con los espacios de trabajo, usará el archivo web-app/yarn.lock de nivel superior junto con el archivo web-app/node-modules/ nivel superior. Entonces, instala todos los paquetes en lugar de un subconjunto, especificado en web-app/packages/controls/package.json . Pero necesito verificar que mi paquete tenga todas las dependencias requeridas en su propio package.json y no funciona por suerte al completar los departamentos faltantes de otros espacios de trabajo.

Hay 2 posibles soluciones:

  1. Si no es root, use yarn.lock root, pero instale solo los paquetes especificados en package.json locales.
  2. No busque configuraciones de nivel superior, sino .yarnrc/.npmrc .

También luchando con esto. Tenemos un proyecto CLI angular junto con nuestra API, por lo que están en el mismo repositorio e intentan enviar la interfaz a Heroku.

Estamos usando un paquete de compilación que le dice a Heroku que salte primero al repositorio de frontend: https://github.com/lstoll/heroku-buildpack-monorepo

El problema es que no hay yarn.lock dentro de ese paquete nohoist por lo que Heroku simplemente se instala con npm y terminamos con todos los paquetes nuevos en lugar de los bloqueados.

Puede usar el archivo global yarn.lock con los paquetes individuales. Recientemente me acerqué a mi Dockerfile de esta manera:

WORKDIR /app
ENV NODE_ENV=production

ADD yarn.lock /app/
ADD package.json /app/

# Only copy the packages that I need
ADD packages/my-first-package /app/packages/my-first-package
ADD packages/my-second-package /app/packages/my-second-package

RUN cd /app && yarn install --frozen-lockfile

Esto instalará solo las dependencias que estén realmente en uso por los dos paquetes que copié y no por nadie más.

Tengo un proceso de compilación en el que primero me gustaría crear un artefacto de lanzamiento a partir de un paquete y luego no tener ninguna de sus dependencias instalada. Esto es bastante fácil con la compilación multietapa de Docker.

  1. Agregue solo yarn.lock, package.json y el paquete de interfaz de usuario a la ventana acoplable
  2. ejecutar yarn install --frozen-lockfile
  3. ejecutar el proceso de construcción
  4. comience una nueva etapa y agregue yarn.lock, package.json y los paquetes de tiempo de ejecución / carpetas de espacio de trabajo necesarios
  5. Haz un COPY --from=<stage> por el artefacto construido
  6. ejecute yarn install --frozen-lockfile y exponga un comando RUN .

Y terminará con un contenedor pequeño que solo contiene las dependencias especificadas en su archivo yarn.lock y necesarias en producción.

@ johannes-scharlach
Terminé usando un método casi idéntico al que estás describiendo. La construcción de varias etapas es un buen consejo :).

@connectdotz Creo que desde mi punto de vista, este problema podría cerrarse y podemos seguir trabajando en el problema # 4521. Dado que el archivo principal yarn.lock podría funcionar con un subconjunto de los paquetes, parece que no es necesario un yarn.lock por espacio de trabajo (aunque sigo pensando que este podría ser un mejor flujo de trabajo de desarrollo 😉). Pero el problema en # 4521 sigue siendo importante, porque en la solución a la que llegamos aquí, necesitamos mencionar cada espacio de trabajo de dependencia en el Dockerfile, aunque yarn debe conocer las interdependencias y cómo "vender" un espacio de trabajo determinado.

Pasé los últimos días tratando de convertir nuestro monorepo primero en espacios de trabajo de Lerna y luego de Yarn. Yarn funcionó en general de manera más confiable y está muy cerca de lo que necesitamos, especialmente con la reciente introducción de yarn workspaces run <script> y otras cosas agradables como wsrun .

Sin embargo, el único yarn.lock es un punto de dolor:

  • No estoy seguro de cómo migrar correctamente nuestros archivos de bloqueo existentes a uno solo, consulte https://github.com/yarnpkg/yarn/issues/6563. Tenemos decenas de miles de líneas allí y la adición de paquetes existentes como espacios de trabajo introdujo muchos problemas sutiles de control de versiones.
  • La instalación de dependencias solo en un paquete específico ("desmontaje" / "venta") La compilación de Dockerized no está bien admitida, consulte más arriba (https://github.com/yarnpkg/yarn/issues/5428#issuecomment-403722271) o https: //github.com/yarnpkg/yarn/issues/4521.

¿Qué pensaría usted de que los espacios de trabajo de Yarn sean solo un pequeño núcleo , una declaración de paquetes sin ninguna funcionalidad específica? Por ejemplo:

  • Si quisiera ejecutar algún script en sus espacios de trabajo, haría yarn workspaces run <script> .
  • Si quisiera un solo archivo de bloqueo e izado (¿están necesariamente unidos los dos?), Esta sería su raíz package.json :
    json "workspaces": { "packages": ["packages/*"], "hoistDependencies": true }
  • Si quisiera migrar sus archivos de bloqueo actuales a una estructura elevada, ejecutaría yarn workspaces hoist-dependencies .

Etc. Estos son solo ejemplos y, en la práctica, algunas características probablemente serían optar por no participar en lugar de optar por participar (por ejemplo, la gente espera un solo yarn.lock y izar ahora) pero la idea general es que los espacios de trabajo Ser una base liviana para tareas de repositorio.

¿Qué piensas?

Creo que el problema que está abordando esta solicitud de función es el mismo que en el n. ° 4521. Un comando para hacer esencialmente lo que describe @ johannes-scharlach ciertamente sería más factible que un archivo de bloqueo por espacio de trabajo.

También hay un RFC abierto en este momento para espacios de trabajo anidados , que suena similar a esta solicitud de función, aunque creo que está resolviendo un problema diferente.

¿Qué pensaría de que los espacios de trabajo de Yarn sean solo un núcleo diminuto, una declaración de paquetes sin ninguna funcionalidad específica?

Los espacios de trabajo no cambiarán drásticamente, creo que estamos satisfechos con su interfaz actual.

Si quisiera ejecutar algún script en sus espacios de trabajo, haría yarn workspaces run <script>

Eso ya es posible (v1.10, # 6244).

Si quisiera migrar sus archivos de bloqueo actuales a una estructura elevada, ejecutaría yarn workspaces hoist-dependencies .

Dado que no cambiaremos la interfaz del espacio de trabajo, sería lo opuesto ( dehoistDependencies ).

Lo que no me gusta de esto es que toma un comportamiento técnico (izar) e intenta convertirlo en un comportamiento semántico. Debe centrarse en la historia del usuario y luego averiguar la implementación en lugar de lo contrario.

En este caso, creo que su caso de uso ("Instalar dependencias en un paquete específico solamente") se resolvería mejor extendiendo yarn --focus .

Supongo que la pregunta principal es si la elevación y un solo archivo yarn.lock son estrictamente necesarios para los espacios de trabajo. Quiero decir, ¿es lo que realmente los define o es "solo" la primera característica que obtuvieron históricamente?

Por ejemplo, en nuestro caso de uso, el mejor comportamiento hipotético de los espacios de trabajo sería:

  • Eleve node_modules en tiempo de desarrollo para mayor eficiencia.
  • Mantenga los archivos yarn.lock locales para la compilación (construimos paquetes específicos en Docker, algo que otras personas también mencionaron en este hilo) y también para que los paquetes puedan bloquear sus versiones específicas. Vea también # 6563.
  • Ejecute scripts a través de yarn workspaces run <script> incluso si no necesita (o debe evitar) el izado.

La elevación se puede deshabilitar con nohoist , run se puede "deshabilitar" simplemente sin usar el comando, pero no es posible "deshabilitar" el archivo único yarn.lock , y yo ' No estoy seguro de si es una característica tan fundamental que no se puede deshabilitar o si aún no se ha solicitado lo suficiente :)

Creo que la mejor manera de resolver esto sería tener yarn install --app-mode package@version

De esa manera, simplemente puede copiar los archivos de bloqueo del espacio de trabajo al publicar su aplicación en una versión determinada, e instalar en app-mode respetará el archivo de bloqueo incluido.

Yarn no tiene que instalar todo el archivo de bloqueo; debería poder extraer fácilmente solo la parte del gráfico de dependencia relevante para ese paquete.

De hecho, esto podría ser bastante fácil de hacer manualmente incluso ahora:

  • descargue el paquete zip desde el registro directamente (yarn no tiene equivalente, npm sí: npm pack package@version )
  • extrae el gzip en node_modules / package
  • cd en node_modules / paquete
  • ejecutar yarn install --production desde allí (respetará el archivo de bloqueo incluido)

editar: desafortunadamente, todo esto está mal, ya que el archivo de bloqueo del espacio de trabajo no incluye las versiones de los paquetes dentro del espacio de trabajo, que pueden ser dependencias del paquete de la aplicación. Debería haber algo más complicado que copiar al crear archivos de bloqueo de aplicaciones a partir de archivos de bloqueo del espacio de trabajo.

No estoy exactamente seguro de si los archivos de bloqueo separados son la respuesta, pero tengo un problema similar. Tengo un monorepo configurado con una CLI y un backend. La CLI requiere algunos paquetes que son específicos de la plataforma y solo funcionan en máquinas de escritorio con una configuración particular. Por otro lado, necesito poder construir mi api en una imagen de Docker, lo cual es fundamentalmente imposible en la implementación actual de espacios de trabajo.

¡Caso de uso muy similar al de @samuela aquí! ¡Este sería de gran ayuda!

Mi caso de uso puede parecer ridículo en comparación con los otros "reales". Pero tengo un monorepo para algunas utilidades - en este caso react hooks - dentro de packages/* .

Tengo un segundo espacio de trabajo al lado de packages/* , y ese es local/* . En realidad, esto está en gitignore, y la idea es que los desarrolladores de la empresa puedan hacer lo que quieran allí, por ejemplo, poner aplicaciones create-react-app allí y probar los ganchos durante el desarrollo.

Ahora, aunque los paquetes local/* están en gitignore, la raíz yarn.lock simplemente está hinchada y contaminada, y registrada en git, debido a los espacios de trabajo locales.

Lo que desearía es una forma de especificar que algunos espacios de trabajo usarán algunos archivos de bloqueo específicos, por ejemplo, algún mapeo como este:

  "workspaces": {
    "packages": [
      "packages/*",
      "local/*"
    ],
    "lockfiles": {
      "local/*": "./local.yarn.lock"
    }
  }

O incluso una forma de especificar "no ponga nada de _este_ espacio de trabajo en el archivo de bloqueo".

Pero sí, el mío no es un caso de uso serio en primer lugar :)

No estoy exactamente seguro de si los archivos de bloqueo separados son la respuesta, pero tengo un problema similar. Tengo un monorepo configurado con una CLI y un backend. La CLI requiere algunos paquetes que son específicos de la plataforma y solo funcionan en máquinas de escritorio con una configuración particular. Por otro lado, necesito poder construir mi api en una imagen de Docker, lo cual es fundamentalmente imposible en la implementación actual de espacios de trabajo.

Lo acertó: como yo lo veo, uno de los beneficios principales del archivo yarn.lock es crear compilaciones de producción de depósito congelado. ¿Los creadores de Yarn lo olvidaron?

Otro argumento para resolver el problema del archivo de bloqueo único es la propiedad del código. Si tiene un monorepo que está usando algo como la función GitHub CODEOWNERS, no es posible otorgar la propiedad completa sobre un paquete a un grupo de desarrolladores. Eso es porque si instalan algo en su propio espacio de trabajo, invariablemente cambiarán el archivo de bloqueo de nivel raíz. Este cambio deberá ser aprobado por los propietarios del código del archivo de bloqueo, que, dado un monorepo de escala suficiente, será diferente de los propietarios del espacio de trabajo original.

Otra razón más para tener la opción de generar archivos de bloqueo por espacio de trabajo: Google App Engine se niega a lanzar un servicio de nodo sin un archivo de bloqueo (NPM / Yarn). Este es un excelente DevOps de su parte, pero un dolor para nosotros. Hasta ahora las opciones que tenemos son:

  • Implemente todo con env vars indicando a qué servicio nos referimos y modifique nuestro yarn start (el único punto de entrada admitido) para bifurcar en función de env vars
  • Haga que el script de compilación copie el archivo de bloqueo principal en cada espacio de trabajo e implemente solo el servicio que nos interesa (gracias @ johannes-scharlach)

En última instancia, creo que un comando yarn install --workspace-lockfile que genere archivos de bloqueo por espacio de trabajo sería la mejor solución.

Tener una opción para archivos de bloqueo a nivel de paquete también nos ayudaría. Nuestro caso de uso es un poco diferente, estamos probando una nueva forma de administrar las dependencias locales.

Así que ya tenemos algunos repositorios mono y tenemos algunos repositorios que solo contienen un paquete. Todos estos están publicados y, por lo tanto, se pueden usar juntos, sin embargo, hay muchas ocasiones en las que tenerlos localmente y enlazados simbólicamente es muy útil.

Pero algunos desarrolladores tienen dificultades para administrar enlaces simbólicos, etc., por lo que estamos probando un repositorio mono de espacio de trabajo de hilo vacío estándar que todos clonamos en nuestras máquinas, luego clonamos repositorios de paquetes en ese repositorio mono local. Algunos de nosotros pueden tener un solo paquete de clones, otros pueden tener 5. Esto es muy conveniente y hace que el desarrollo local, de repositorios cruzados y de dependencias cruzadas sea muy sencillo.

Pero nos hemos encontrado con un problema que no podemos resolver, la edición de dependencias no actualiza el archivo de bloqueo de hilo local, siempre actualiza la raíz para el repositorio mono vacío en el que no actualizamos las dependencias de actualización (tiene todo en /packages gitignored.

Tener una opción para no levantar las escrituras de archivos de bloqueo en la raíz del repositorio mono sería genial y hacer que se escriban a nivel de paquete.

Como nota, también me he encontrado con problemas de implementación y compilación en torno a Docker que otros han mencionado y esto también resolvería eso.

Esta característica también sería muy valiosa para mí. En mi caso tengo un monorepo con algunos paquetes desplegados en distintas plataformas. Una es una aplicación Next.js implementada con Now.sh y la otra es un montón de funciones en la nube implementadas en Firebase.

En ambas implementaciones, el código fuente se incluye y se carga para su instalación y compilación en la nube. No tener un archivo yarn.lock que vaya junto con la fuente significa que las dependencias se instalan usando las versiones en package.json y ninguna versión está bloqueada.

También me encantaría poder habilitar archivos yarn.lock en cada espacio de trabajo.

Me doy cuenta de que los espacios de trabajo de hilo están destinados principalmente a monorepos, pero nuestro caso de uso es bastante similar al de @LeeCheneler .

Básicamente, hemos creado una biblioteca de componentes de React que usamos como dependencia en diferentes proyectos (que todos tienen sus propios repositorios). Al usar los espacios de trabajo de hilo, podemos hacer referencia fácilmente a la versión local de la biblioteca de componentes y hacer que los cambios se propaguen rápidamente a la versión local de nuestros otros proyectos. Tampoco necesitamos modificar el package.json cuando pasamos a producción porque la dependencia library: "*" funciona sin ningún cambio. Nuestro único problema es que, sin los archivos de bloqueos de hilo, las versiones de producción de cada proyecto podrían terminar usando diferentes versiones de paquetes.

Tengo que imaginar que este problema sería común entre los desarrolladores de paquetes que usan espacios de trabajo de hilo.

Otro problema crítico con un archivo de bloqueo de nivel superior es que rompe el almacenamiento en caché de la capa de Docker. Normalmente, uno podría optimizar el almacenamiento en caché de Docker copiando primero el package.json y yarn.lock. Si Docker no ve cambios en esos archivos, usará una capa anterior. Sin embargo, si ese archivo de bloqueo es uno solo para todo el monorepo, cualquier cambio en cualquier paquete invalida la caché. Para nosotros, esto se traduce en pipelines de CI / CD ridículamente lentos en los que cada paquete se crea sin el caché. Existen otras herramientas, como Lerna, que comprueban los cambios en los paquetes para ejecutar ciertos scripts. Esto también se rompe ya que un cambio de dependencia en el archivo de bloqueo podría no ser detectado por estar en el nivel superior.

Lamento mencionar este problema (un poco antiguo), pero también tengo un caso de uso en el que esto sería útil. Tengo aproximadamente 10 microservicios que están alojados y desarrollados de forma independiente, pero sería bueno tener un repositorio de espacio de trabajo central donde pueda escribir yarn install para instalar dependencias en todas las carpetas y luego ejecutar yarn start para ejecutar un script que iniciará todos los microservicios. Esto es algo que se podría hacer con un script relativamente simple, pero también parecía factible con los espacios de trabajo de hilo, pero no pude hacer que funcionara respetando los bloqueos de hilo en cada micorservicio.

@nahtnam Creo que la idea de Yarn 1.x monorepo es un poco diferente. No se trata de proyectos independientes bajo un mismo techo, se trata más de un gran proyecto singular que tiene algunos de sus componentes expuestos (llamados espacios de trabajo). Estos componentes no son completamente independientes, pero pueden usarse así: el compilador de Babel como una entidad mayor y preset-env como submódulo. Además, son homogéneos en el sentido de que sus dependencias están unificadas: si algunos paquetes dependen de core-js , debería ser la misma versión core-js en cada uno de ellos, porque no se puede bloquear diferentes versiones con un solo archivo de bloqueo raíz, ni tiene sentido que las partes del proyecto dependan de diferentes versiones. Y debido a que es un proyecto, todos sus componentes se vinculan automáticamente a la raíz node_modules , lo cual es extraño para proyectos completamente independientes.

Entonces, si está desarrollando un paquete de microservicios (que son independientes, y algunos de ellos no se tocarán en años, mientras que otros serán creados / actualizados, tal vez desarrollados por diferentes equipos), entonces deberían tener archivos de bloqueo personales sin root. lock (el problema de Docker también va aquí). La única pregunta es qué herramienta ayudará con la ejecución de scripts. Lerna podría ser una respuesta, ya que no está ligada al hilo.

La única pregunta es qué herramienta ayudará con la ejecución de scripts. Lerna podría ser una respuesta, ya que no está ligada al hilo.

@ the-spyke No solo eso. yarn workspaces también resuelve, vinculando módulos para el desarrollo de la misma manera que lo hace npm link , que es la razón principal por la que lo estamos usando. npm link no funciona bien en algunos casos.

@ the-spyke ¡Gracias! Por alguna razón, pensé que estaba volteado (espacios de trabajo de lerna vs yarn). Miré a lerna y parece que resuelve mi problema.

Terminé usando espacios de trabajo para cada proyecto en el que estoy trabajando, debido a lo fácil que es tener un paquete utilities separado que pueda actualizar cuando sea necesario (aunque esté publicado), y el hecho de que No tengo que reinstalar dependencias si quiero bifurcar algún paquete (esencialmente permitiéndome trabajar en dos ramas al mismo tiempo, lo que para mí era inaudito), y siempre que quiera usar un paquete de mi utilities (que incluye la mayoría de las cosas que suelo usar), puedo usarlo de inmediato sin agregarlo a package.json del segundo paquete (pero obviamente es una buena idea en caso de una instalación separada, y es necesaria para las importaciones automáticas de IDE); todo simplemente funciona. @ the-spyke tiene un buen punto, tal vez independent projects under one roof no es el propósito de los espacios de trabajo, y sin embargo, eso es más o menos lo que parece estar haciendo aquí: tengo un solo repositorio monorepo-base , que excluye la carpeta packages , mientras que cada carpeta debajo de packages es un repositorio de git independiente y separado.
image
Por supuesto que eso me lleva al tema de este hilo; ya que no confirmo todos los paquetes como un repositorio, el nivel raíz yarn.lock tiene sentido. He estado usando --no-lockfile para instalar todo, y recientemente encontré un problema con versiones en conflicto de class-validator . Por ahora, bloquearé todos los departamentos en versiones específicas (honestamente, ese nivel de control tiene más sentido para mí) y veré cómo funciona. Volveré a leer toda la banda de rodadura, tal vez haya algunos consejos que pueda usar para mi caso de uso.

PD.
yarn why no funciona sin el archivo de bloqueo para uno, y he notado que algunas personas mencionan problemas con App Engine. Supongo que en caso de que cada paquete sea un repositorio separado, el archivo de bloqueo podría generarse cada vez que se instala (sin agregarlo a VCS). No estoy seguro de ese caso específico.

Desafortunadamente, la solución sugerida por @ johannes-scharlach se vuelve muy complicada si su imagen construida requiere módulos de nodo en tiempo de ejecución que no están incluidos en alguna carpeta de compilación, porque tendrá que averiguar exactamente qué módulos se requieren para ejecutar y copiarlos minuciosamente a la etapa de construcción final.

(un poco fuera del tema) @GrayStrider también puede utilizar el campo "resoluciones" en package.json: es la única forma de forzar una versión en una dependencia anidada, por ejemplo, si desea que todos los lodashes sean la versión exacta, no importa cuán profundamente anidado. Sin embargo, eso puede introducir errores muy sutiles que serán difíciles de detectar.

A continuación, presentamos una solución a la que llegamos, con un impacto mínimo en el flujo de trabajo de Docker existente.

  1. ln project/yarn.lock packages/package1/yarn.lock : crea un enlace simbólico duro desde la raíz yarn.lock en cada paquete.
  2. Agregue COPY yarn.lock . a cada packages/package1/Dockerfile
  3. yarn install dentro de Docker

Ventajas :

  • No tienes que copiar todo tu monorepo en una capa de imagen
  • No tiene que fusionar los Dockerfiles de nivel de paquete en un solo Dockerfile en la raíz
  • Básicamente satisface el requisito de los archivos de bloqueo del espacio de trabajo

Desventajas :

  • --frozen-lockfile no funciona. Como los paquetes de espacios de trabajo no están incluidos en el yarn.lock , por lo tanto, yarn ve un paquete que ha "agregado" al package.json que no existe en el yarn.lock

De todos modos, esta es una desventaja menor, ya que puede evitarla realizando un yarn --frozen-lockfile como primer paso en su canalización de CI / CD

Editar:
Aparte, creo que los documentos de hilo sobre la instalación podrían ser un poco más claros sobre cómo se usa el archivo de bloqueo en el proceso de resolución del paquete.

Editar:
Entonces, resulta que git no admite enlaces físicos, solo admite enlaces simbólicos blandos, por lo que esta estrategia no funcionará.

Otra alternativa es simplemente usar un githook precommit para copiar el yarn.lock en cada uno de sus espacios de trabajo ... no es ideal ya que aún permite problemas al implementar desde su máquina local.

@ dan-cooke muchas gracias por sus ideas, ¡muy apreciado!

@ dan-cooke, esto rompe el almacenamiento en caché de la capa de Docker ya que una nueva dependencia en cualquier espacio de trabajo invalidaría la capa de instalación para todos los Dockerfiles.

@migueloller Si la capa de instalación no debe cambiar, entonces no debería usar un archivo de bloqueo para todos los paquetes. Aplastar y elevar dependencias en una sola lista gigante es el propósito de los espacios de trabajo. No es que "rompa" la caché de Docker, la caché se invalida porque las dependencias reales no se especifican en package.json , por lo que el código final de su paquete depende del contenido de yarn.lock . Como resultado, debe reconstruir todos los paquetes en cada cambio en yarn.lock y Docker hace todo bien. Y no es como where every package is built without the cache porque todas las compilaciones podrían reutilizar la capa con yarn install (probablemente necesitará configurar esto).

@migueloller
Correcto. Afortunadamente, no agregamos nuevas dependencias a menudo, por lo que esto solo será un problema una vez que se realice un sprint.

E incluso entonces, es un pequeño precio a pagar (para nosotros) por dependencias reproducibles en Docker

@ the-spyke, de hecho. Es por eso que este problema se trata de tener un archivo de bloqueo individual por paquete. De esta manera, la capa en caché solo se invalida cuando las dependencias del paquete cambian y es independiente de otros paquetes.

Quizás también valga la pena trasladar esta discusión al propio npm, que también admite espacios de trabajo a partir de la v7.0 ..

He estado investigando temas relacionados y me gustaría agregar alguna aclaración a mi comentario anterior (en su mayoría dirigido a desarrolladores con menos experiencia, supongo, ya que los problemas que enfrentaba se debían en cierta medida a mi falta de comprensión de la importancia de lockfile ).

"bloquear todas las dependencias a una versión específica" se llama anclar ; por desgracia, no va a evitar que las cosas potencialmente romperse si las actualizaciones sub -dependency (párrafos finales del artículo aquí ), que no he considerado. Eso es exactamente lo que pretendía evitar que suceda con el archivo de bloqueo.

He perdido más que suficientes horas en actualizaciones de última hora en el pasado en múltiples ocasiones; Experimentaré usando lockfile en monorepo, ya que prefiero lidiar con conflictos de fusión y dolores de cabeza organizacionales, que con errores invisibles causados ​​por actualizaciones menores.

Dicho anteriormente, estoy ansioso por ver cualquier progreso en este tema.

@migueloller Los archivos de bloqueo individuales significan Yarn Monorepos individuales. No puede tener un archivo de bloqueo para un espacio de trabajo porque rompe la uniformidad de los departamentos en Monorepo. Si quieres hacerlo, te estás alejando de la idea original de Yarn Monorepo: se trata de unificar, elevar y reducir deps en una lista plana en la raíz en lugar de tener diferentes versiones (e incluso subárboles completos) en diferentes espacios de trabajo. .

@ the-spyke, pero el problema original es exactamente lo contrario. Un archivo de bloqueo por espacio de trabajo.

No entiendo cómo no puede tener un archivo de bloqueo por espacio de trabajo.

¿Se rompe la uniformidad deps en el Monorepo? Seguro para el desarrollo. Pero todo el propósito de las dependencias compartidas se pierde cuando debe implementar microservicios ligeros desde cada uno de sus espacios de trabajo.

Los departamentos compartidos y elevados solo tienen sentido en el desarrollo.

Para que un espacio de trabajo viva en Docker, se requiere un archivo de bloqueo.

@ dan-cooke Como puede ver , también tuve este problema en 2018, pero ahora tengo una opinión diferente.

Estás diciendo Docker y microservicios. Pero, ¿qué pasa si desarrollo un paquete npm regular? No tengo un subárbol de dependencias production para anclar, porque el usuario final las proporcionará de acuerdo con mi especificación dependencies . Entonces, lo que quiero es maximizar mi experiencia de desarrollo, y eso es lo que hacen perfectamente Monorepos y Yarn Workspaces.

Al mismo tiempo, si está desarrollando microservicios (MS), existen 2 situaciones posibles:

  1. Proyectos independientes. Algunas EM están en desarrollo, otras no se tocaron en años. En este caso son completamente independientes. Es posible tener UserService usando [email protected] y MessagesService usando [email protected] . Ese no es un mundo tan fácil en el que simplemente vincula carpetas desde Workspaces a la raíz node_modules . Por lo tanto, no tiene sentido tener un archivo de bloqueo de root. Cree archivos separados (raíces) y adminístrelos de forma independiente. Eso se llama Multirepo en los documentos de Yarn. Pero ahora lo que está diciendo es "Quiero ejecutar tareas en carpetas diferentes de la carpeta raíz para mayor comodidad". Y ese es un tema completamente diferente.

  2. Proyectos con dependencias unificadas como Jest / Babel / etc. Para esto se crearon los espacios de trabajo, pero en los MS existen requisitos adicionales. Durante las etapas de CI, como el borrado y las pruebas, todo funciona bien porque funciona igual que en una máquina de desarrollo: deps instalados por Yarn en la raíz node_modules y aplanados. Solo con la adición de que probablemente guarde en caché la fase yarn install para acelerar las compilaciones simultáneas.

    En producción es completamente diferente: a partir de eso, solo necesita deps para un espacio de trabajo y termina con ¿cómo instalar ese paquete utils ? ¿Debería estar vinculado o descargarse como tarball? Entonces, lo que realmente necesita es no tener archivos de bloqueo por espacio de trabajo, sino tener un comando como yarn install --prod <workspace> que puede ejecutar especificando un espacio de trabajo e instalará solo los departamentos de producción y, al mismo tiempo, ignorará otros espacios de trabajo no referenciados. Como si mi data WS depende de utils WS, pero no de logging WS, entonces logging sí mismo y sus departamentos no deberían aparecer en node_modules . Un resultado similar, pero un enfoque completamente diferente a un "archivo de bloqueo por espacio de trabajo".

    Si está publicando paquetes de compilación en un repositorio (npm, Arifactory, GutHub), puede obtener un comportamiento similar simplemente copiando el archivo de bloqueo en un área de trabajo y haciendo yarn install --prod aquí. Debería advertir sobre archivos obsoletos, pero en lugar de volver a crearlo desde cero con versiones nuevas, debería eliminar el exceso de contenido (solo lo probé y parece legítimo). Debería ser aún mejor y robusto con el uso de Offline Mirror.

    Y al final, ha implementado Focused Workspaces exactamente para Multirepos.

Entonces, lo que estaba diciendo es que tal vez el problema no se parece a lo que es.

@ el-espía
Veo lo que dices. Creo que definitivamente se reduce a cómo se logra este resultado. Quizás un archivo de bloqueo por espacio de trabajo no sea realmente la mejor manera de lograr el resultado deseado. Tienes buenos argumentos.

Definitivamente no es una solución de "talla única" de todos modos

@ the-spyke, traes buenos puntos. Quizás sea necesario pensar más en qué problemas se diseñaron para resolver los espacios de trabajo de Yarn y si su uso para administrar monorepos grandes está alineado con ese diseño.

Tengo curiosidad por saber cómo resolvería un escenario como este:

.
└── packages
    ├── app1
    ├── app2
    ├── lib1
    ├── lib2
    └── lib3

lib3 es una biblioteca compartida y depende de app1 y app2 . lib1 solo lo usan app1 y lib2 solo lo usan app2 . Según sus sugerencias, lib1 y app1 deberían estar en su propio espacio de trabajo con su propio archivo de bloqueo y lo mismo con lib2 y app2 . Ahora, la pregunta es ¿qué hacer con lib3 si _ambos_ app1 y app2 dependen de ello? ¿Quizás uno podría hacer que ambos espacios de trabajo ( app1 y app2 ) agreguen lib3 a su espacio de trabajo y luego ejecuten yarn install en cada aplicación? ¿Yarn lo permite? ¿Habría algún conflicto si uno quisiera ejecutar tanto app1 como app2 en desarrollo localmente (quizás app1 es una aplicación React y app2 una API GraphQL)? ? Parece que podría funcionar.

La siguiente pregunta sería "¿Cómo se obtienen los beneficios de izar con este método?" Por ejemplo, si app1 y app2 comparten una gran cantidad de dependencias comunes, sería bueno incorporarlas. Sin embargo, puedo ver cómo esto podría estar fuera de alcance y es un problema que Yarn PnP debe resolver (no copia archivos a node_modules y, en cambio, tiene un caché compartido).

Voy a darle una oportunidad a esto e informaré. Si esto funciona, entonces quizás hemos estado usando mal los espacios de trabajo de Yarn todo el tiempo ...

EDITAR: Lo probé y funciona.

He cambiado mi postura ahora y me doy cuenta de que, si bien tener un archivo de bloqueo individual por espacio de trabajo puede ser lo primero que me viene a la mente al administrar un monorepo completo con espacios de trabajo de Yarn, puede que no sea la pregunta correcta. Una mejor pregunta podría ser "¿Los espacios de trabajo de Yarn están diseñados para administrar un monorepo?". La respuesta, como de costumbre, es "depende".

Si eres Babel y tienes un solo equipo trabajando en el monorepo y todo está destinado a cambiar al mismo tiempo, entonces sí, para esto se diseñaron los espacios de trabajo de Yarn. Pero si es una organización con varios equipos y está utilizando un monorepo, es probable que no desee administrar todo el monorepo con una sola raíz del espacio de trabajo de Yarn. Probablemente solo desee utilizar el comportamiento predeterminado de Yarn o varias raíces del espacio de trabajo de Yarn dentro del monorepo. Esto estará determinado por las aplicaciones que esté creando, cuántos equipos hay, etc.

Para nosotros, quedó claro que para cada entidad implementable (en nuestro caso hay un Dockerfile), queremos tener un yarn install por separado para cada una (ya sea una raíz de worksapce o no). Esto proporciona claridad sobre la propiedad del código, permite implementaciones aisladas que ocurren en diferentes cadencias, resuelve problemas de almacenamiento en caché con lockfiles y Docker, etc. Sin embargo, hay algunas desventajas en esto:

  • ¿Qué pasa con los paquetes de node_modules duplicados? Esta es una clase común de problemas con monorepos y aunque los espacios de trabajo de Yarn ayudan con el izado, no es una solución general de monorepos. Sin embargo, existen otras soluciones. Por ejemplo, Yarn PnP se encarga de esto. También puede usar Lerna sin Yarn y usar la opción --hoist .
  • ¿Qué pasa con la utilidad de ejecutar comandos en espacios de trabajo durante el desarrollo? Nuevamente, los espacios de trabajo de Yarn te permiten hacer esto, pero eso no significa que debas convertir todo el monorepo en una raíz del espacio de trabajo de Yarn. La construcción de las herramientas y los scripts necesarios será diferente para cada equipo y depende de su monorepo. Los espacios de trabajo de Yarn probablemente no fueron diseñados como un corredor de tareas de monorepo. Uno podría intentar doblar un poco los espacios de trabajo de Yarn para hacer este trabajo (es decir, ejecutar scripts NPM en el monorepo usando yarn workspace ... ) pero es importante tener en cuenta que una única raíz del espacio de trabajo para todo el monorepo probablemente no lo hará. darle lo que necesita a menos que sea como Babel, Jest, React, etc.

Hay toda una serie de problemas relacionados con la ejecución de un monorepo. Por ejemplo, ¿qué pasa con el seguimiento de las dependencias y solo reconstruir las cosas que cambiaron para ahorrar tiempo en CI? Los espacios de trabajo de Yarn podrían ayudar al permitirle consultar el gráfico de dependencia. Lerna hace esto, por ejemplo, para permitir la clasificación topológica de los comandos que se ejecutan. Yarn v2 también te permite consultar el gráfico de dependencia. El administrador de paquetes PNPM también hace eso. Pero yo diría que dependiendo de la complejidad del monorepo, uno podría querer probar herramientas creadas para eso (no administradores de paquetes) como Bazel , Pants , Buck , etc.

@migueloller Por sus requisitos, veo que no necesita paquetes estrictamente independientes u otras cosas exóticas, también desea instalaciones de desarrollador más delgadas. En tal caso, debe comenzar con Yarn Monorepo normal: raíz única y todos los paquetes como espacios de trabajo. Tendrá tiempos de instalación más rápidos, menor uso del disco y app1 usará lib1 y lib3 locales vinculados. El único inconveniente será con mayor frecuencia la invalidación de la caché de CI porque la adición de un devDep a lib1 actualizará el yarn.lock compartido. Pero normalmente no actualiza dependencias que a menudo se preocupen mucho por esta compensación.

lib1 puede depender de lodash@^4.5.0" and lib2 may depend on lodash@^4.10.0 ". En el caso de Monorepo, desea que se utilice una única versión de lodash , por lo que Yarn instalará algo más reciente compatible con ambos especificadores como ` [email protected] "

También hay situaciones en las que equipos independientes desarrollan proyectos independientes. En este caso, proj1 puede querer quedarse en [email protected] con proj2 teniendo [email protected] y actualizar eso con sus propias cadencias. Por supuesto, puede lograr esto utilizando especificadores más estrictos como lodash@~4.5.0 , pero esto aún puede actualizar la dependencia de segundo nivel demasiado pronto. Entonces, en general, esos proyectos pueden no estar relacionados en absoluto, simplemente se encuentran dentro de un repositorio de git. En este caso, no hay razón para vincularlos como Yarn Monorepo y compensar la independencia por un archivo de bloqueo compartido. Trátelos como lo que son: proyectos separados con su vida independiente. Y eso se llama Multirepo. En Unix, todos sus directorios están por debajo de / , pero eso no significa que todos los proyectos JS posibles en su PC deban ser un Monorepo :-)

La creación de una imagen de Docker mínima posible para uso en producción no está relacionada en absoluto con Yarn, pero puede obligar a Yarn a reutilizar un artefacto de desarrollo llamado yarn.lock y ayudarlo con esta tarea también.

@ the-spyke, en este ejemplo solo usé 3 espacios de trabajo, pero en nuestro repositorio real tenemos más de 20 espacios de trabajo con una combinación de bibliotecas y cargas de trabajo implementadas tanto para el front-end como para el back-end. La forma en que lo veo ahora es que tenemos un monorepo (o lo que usted llama multirepo) donde probablemente tenga sentido tener múltiples raíces de espacio de trabajo de Yarn con lockfiles independientes. Estamos pensando en usar una unidad desplegable como unidad de separación, se alinea muy bien con los archivos de bloqueo.

Creo que para mí, lo que hace que esto funcione muy bien es el hecho de que los espacios de trabajo de Yarn admiten rutas fuera de la raíz del espacio de trabajo, aunque la publicación inicial del blog dice lo contrario. Por ejemplo, puede tener esto:

{
  "workspaces": [
    "../lib1",
    "../lib3"
  ]
}

Tenemos el mismo caso de uso que @migueloller y una posible idea es que Yarn admita múltiples conjuntos de espacios de trabajo, como este:

{
  "workspaces": {
    "frontend-app": ["frontend", "common"],
    "backend-app": ["backend", "common"]
  }
}

Yarn mantendría dos archivos de bloqueo adicionales (imagino que el yarn.lock aún existiría):

.
└── monorepo/
    ├── yarn.frontend-app.lock
    ├── yarn.backend-app.lock
    └── packages/
        ├── frontend
        ├── backend
        └── common

Al construir una imagen de Docker, por ejemplo, para frontend, crearíamos un contexto (por ejemplo, a través de tar ) que incluye esto:

.
└── <Docker build context>/
    ├── yarn.frontend-app.lock
    └── packages/
        ├── frontend
        └── common

Lo que no pensé profundamente es si es posible instalar (enlace en node_modules ) las versiones correctas de las dependencias si el frontend y el backend bloquean versiones diferentes. Pero puramente desde la vista de alto nivel, probablemente lo que buscamos sean espacios de trabajo de Yarn bidimensionales.

(Algo similar también se publicó aquí ).

Parece que no necesita un archivo de bloqueo por espacio de trabajo, sino que necesita node_modules por espacio de trabajo para la implementación

@gfortaine , si lees la discusión te darás cuenta de que en realidad ese no es el caso. La razón para tener un archivo de bloqueo separado no tiene nada que ver con la instalación, sino con un archivo de bloqueo que solo cambia cuando cambia un paquete específico. Un archivo de bloqueo de nivel superior cambiará con _ cada_ cambio de dependencia del espacio de trabajo, pero un archivo de bloqueo en el ámbito de un solo paquete solo cambiará cuando cambien las dependencias de ese paquete.

Vale la pena mencionar que esto se puede hacer en la tierra de los usuarios. Usando el paquete @yarnpkg/lockfile uno puede analizar el archivo de bloqueo de nivel superior, y luego usando yarn workspaces info uno puede determinar las dependencias del espacio de trabajo. Con esta información, junto con el package.json de cada espacio de trabajo, se puede generar un archivo de bloqueo por espacio de trabajo. Luego, uno podría configurarlo como un script postinstall en el repositorio para mantener esos archivos de bloqueo individuales sincronizados con el de nivel superior.

Podría intentar construir esto e informar con mis hallazgos.

Parece que acabo de encontrar esta implementación, aunque para pnpm: @ pnpm / make -ded-lockfile

Espero que esto ayude 👍

Mi necesidad de este comportamiento (control de versiones por espacio de trabajo, pero todavía tengo archivos de bloqueo en cada paquete) es que tengo un monorepo anidado, donde un subárbol se exporta a otro repositorio por completo, por lo que debe permanecer independiente. En este momento estoy atascado con lerna / npm y algo de lógica personalizada para intentar igualar las versiones. Sería bueno si yarn (supongo que v2, dado que es donde se encuentra el soporte anidado) pudiera administrarlos todos a la vez, pero dejar el subconjunto correcto de la fijación global en cada uno.

Una secuencia de comandos posterior a la instalación podría intentar administrar los archivos de bloqueo directamente, supongo. Suena complicado, pero sería bueno de cualquier manera.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

chiedo picture chiedo  ·  3Comentarios

ocolot picture ocolot  ·  3Comentarios

mnpenner picture mnpenner  ·  3Comentarios

sebmck picture sebmck  ·  3Comentarios

MunifTanjim picture MunifTanjim  ·  3Comentarios