Yarn: 工作区:每个工作区锁定文件

创建于 2018-02-28  ·  54评论  ·  资料来源: yarnpkg/yarn

您要请求功能还是报告错误
特征

目前的行为是什么?
将纱线工作区用于包含顶级节点模块的 monorepo 仅在 monorepo 的根部创建一个yarn.lock ,没有特定于顶级节点模块的yarn.lock

什么是预期行为?
我想使用纱线工作区来管理包含应用程序(顶级节点模块)和库的 monorepo。 但是,在 monorepo 的根目录中只有一个yarn.lock文件会阻止我将我的应用程序打包到 docker 映像中。 我希望有一种方法可以为需要拥有自己的工作区的选定工作区获取yarn.lock文件,因为该工作区以后可能会在 monorepo 之外使用。

一个例子:
如果我有一个带有 2 个工作区的 monorepo: workspace-aworkspace-bworkspace-a使用了一些从workspace-b导出的模块。 如果我想将workspace-a打包到一个 docker 镜像中(或任何其他打包工作空间的方式,而不是整个 monorepo),我就没有yarn.lock了。 这意味着当我将workspace-a的文件移动到除了 monorepo 之外的不同环境时,我将丢失一个yarn.lock文件,并且在安装依赖项时,我将丢失所有锁定文件的优点(知道我正在安装开发中使用的相同依赖项)。

我很惊讶我找不到关于这个的问题。 我是唯一一个想以这种方式与 monorepos 合作的人吗? 也许我错过了什么? 我目前的解决方法是使用lerna根本没有提升,所以我每个包都有一个锁定文件。
最近发布的nohoist功能似乎也没有帮助(尽管我希望),因为它不会yarn.lock每个工作区创建不同的
这个问题与这个关于另一个问题的

请提及您的 node.js、yarn 和操作系统版本。
节点 8.9.3,纱线 1.5.1,OSX 10.13.3

cat-feature triaged

最有用的评论

所以对我们来说,我们不想将整个 monorepo 打包到生成的 docker 容器中。 我们在生产中使用 docker,这些图像应该尽可能轻。 我们的 mono 存储库非常大,包含多个微服务,这些微服务使用库包在它们之间共享代码(一些库与某些微服务相关,但不是全部)。 因此,当我们打包微服务时,我们希望镜像包含该微服务的文件和任何其他依赖项作为适当的依赖项 - 从我们的私有注册表下载,并为 docker 镜像构建。

所以我认为这里的主要考虑是让我们的 docker 镜像尽可能轻,打包整个 monorepo 不符合我们的需求。 此外,当我们在微服务的镜像中运行“yarn”时,我们不想在那里有符号链接,只是一个普通的依赖。

这里的解决方案不必为每个工作区创建一个yarn.lock 文件,它也可以是一个yarn 命令,有助于打包给定的工作区、按需为工作区生成yarn.lock 文件等..

希望它有助于澄清用例..🍻

所有54条评论

基于纱线博客

当您发布包含 yarn.lock 的包时,该库的任何用户都不会受到它的影响。 当您在应用程序或库中安装依赖项时,只会考虑您自己的 yarn.lock 文件。 依赖项中的锁文件将被忽略。

发布单个包时似乎没有必要捆绑 yarn.lock ……它更像是整个 repo 的开发工件,因此,无需将其放在每个包中。

@connectdotz库或已发布的包可能不需要它,但是对于构建 docker 容器,您将希望将其部署在肯定会出现的地方。

当然……但是开发 docker 容器不会只有整个 repo 和 yarn.lock 吗? 我可以看到我们使用 docker 容器来测试我们针对不同操作系统或平台的 monorepo 项目,在这种情况下,我们只需部署整个 repo 及其 yarn.lock。 你能给我一个例子用例吗,你需要在开发周期中将monorepo项目中的单个包部署到docker容器中,这样我才能得到更具体的理解......

所以对我们来说,我们不想将整个 monorepo 打包到生成的 docker 容器中。 我们在生产中使用 docker,这些图像应该尽可能轻。 我们的 mono 存储库非常大,包含多个微服务,这些微服务使用库包在它们之间共享代码(一些库与某些微服务相关,但不是全部)。 因此,当我们打包微服务时,我们希望镜像包含该微服务的文件和任何其他依赖项作为适当的依赖项 - 从我们的私有注册表下载,并为 docker 镜像构建。

所以我认为这里的主要考虑是让我们的 docker 镜像尽可能轻,打包整个 monorepo 不符合我们的需求。 此外,当我们在微服务的镜像中运行“yarn”时,我们不想在那里有符号链接,只是一个普通的依赖。

这里的解决方案不必为每个工作区创建一个yarn.lock 文件,它也可以是一个yarn 命令,有助于打包给定的工作区、按需为工作区生成yarn.lock 文件等..

希望它有助于澄清用例..🍻

@netanelgilad感谢您提供详细信息,它确实有助于澄清您的用例更多地是关于将用于生产或开发的单个包发布到 docker 容器。 请加入#4521 中的讨论,以便我们开始整合它们。

虽然我可以使用单独的锁定文件,但它们不是必需的。 如果您从 repo 的根目录运行 docker 并且-f标志指向单个文件,您将拥有整个 repo 作为上下文,并且可以从根目录复制 package.json 和 yarn.lock 。

您只需要 package.json 用于您将在映像中构建的包,而 yarn 只会为您复制的那些 package.json 文件安装包,即使 yarn.lock 包含更多内容。

编辑:话虽如此。 它会导致 docker 缓存不用于任何包中的包更改,即使它未包含在构建中

4206 是相关的/重复的,并且描述的用例正是我们面临的问题:

假设我们有十个不同的包。 我们希望所有这些都存在于自己的存储库中,以便人们可以根据需要独立处理它们,但我们也希望能够在需要时将它们链接在一起。 为此,我们有一个大型存储库,每个包都有一个子模块,还有一个 package.json 将这些子模块中的每一个作为工作区引用。

我对工作区有类似的问题。 我的项目是一个web-app ,它依赖于许多本地包:

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

工作区包controlsutils未发布并由路径使用。 问题是我需要发布controls包( yarn pack ),我不想自己构建/测试它。 这意味着我想在yarn install里面做web-app/packages/constols/ 。 对于工作区,它将使用顶级web-app/yarn.lock文件和顶级web-app/node-modules/ 。 因此,它安装所有软件包而不是web-app/packages/controls/package.json指定的子集。 但是我需要检查我的包是否在它自己的package.json具有所有必需的依赖项,并且无法通过填充其他工作区缺少的 deps 来工作。

有两种可能的解决方案:

  1. 如果不是 root,请使用 root 的yarn.lock ,但只安装本地package.json指定的包。
  2. 不要搜索顶级配置,而是.yarnrc/.npmrc

也在为此苦苦挣扎。 我们的 API 旁边有一个 Angular CLI 项目,因此它们位于同一个存储库中,并尝试将前端推送到 Heroku。

我们正在使用一个 buildpack,它告诉 Heroku 首先跳转到前端存储库: https :

问题是,在nohoist包中没有yarn.lock ,所以 Heroku 只是使用 npm 安装,我们最终得到所有新包而不是锁定的包

您可以将全局 yarn.lock 文件与各个包一起使用。 我最近像这样处理我的 Dockerfile:

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

这将只安装我复制的两个包实际使用的依赖项,而不是其他任何人。

我有一个构建过程,首先我想从一个包创建一个发布工件,然后没有安装任何依赖项。 使用 Docker 的多阶段构建,这相当容易

  1. 仅将 yarn.lock、package.json 和 UI 包添加到 docker
  2. 运行yarn install --frozen-lockfile
  3. 运行构建过程
  4. 开始一个新阶段并添加 yarn.lock、package.json 和必要的运行时包/工作区文件夹
  5. 为构建的工件做一个COPY --from=<stage>
  6. 运行yarn install --frozen-lockfile并公开RUN命令。

你最终会得到一个小容器,它只包含在你的 yarn.lock 文件中指定的并且在生产中需要的依赖项。

@约翰内斯-沙拉赫
我最终使用了与您所描述的几乎相同的方法。 多阶段构建是一个很好的提示:)。

@connectdotz我认为从我的角度来看,这个问题可以关闭,我们可以继续处理问题 #4521。 由于主 yarn.lock 文件可以与包的子集一起使用,因此每个工作区的 yarn.lock 似乎不是必需的(尽管我仍然认为这可能是一个更好的开发工作流程😉)。 但是#4521 中的问题仍然很重要,因为在我们到这里的解决方案中,我们需要提及 Dockerfile 中的每个依赖工作区,即使 yarn 应该知道相互依赖关系以及如何“供应商”给定的工作区。

在过去的几天里,我试图将我们的 monorepo 转换为第一个 Lerna 和 Yarn 工作区。 Yarn 通常更可靠地工作,它非常接近我们的需要,尤其是最近引入了yarn workspaces run <script>和其他像wsrun这样的好东西。

但是,单yarn.lock是一个痛点:

  • 我不确定如何将我们现有的锁文件正确迁移到单个锁文件,请参阅https://github.com/yarnpkg/yarn/issues/6563。 我们在那里有数以万计的行,并且添加现有的包作为工作区引入了许多微妙的版本控制问题。
  • 仅在特定包中安装依赖项(“de-hoisting”/“vendoring”) Dockerized 构建不受支持,请参见上文 (https://github.com/yarnpkg/yarn/issues/5428#issuecomment-403722271) 或https: //github.com/yarnpkg/yarn/issues/4521。

你会怎么看待Yarn 工作区只是一个很小的核心——一个没有任何特定功能的包的声明。 例如:

  • 如果您想在您的工作区中运行一些脚本,您可以执行yarn workspaces run <script>
  • 如果你想要一个单独的锁文件和提升(两者一定要绑定在一起吗?),这将是你的根package.json
    json "workspaces": { "packages": ["packages/*"], "hoistDependencies": true }
  • 如果您想将当前的锁文件迁移到提升结构,您可以运行yarn workspaces hoist-dependencies

等等。这些只是示例,在实践中,某些功能可能会选择退出而不是选择加入(例如,人们期望单个yarn.lock并且现在正在提升)但一般的想法是工作区将成为 repo-wide 任务的轻量级基础。

你怎么认为?

我相信此功能请求解决的问题与 #4521 中的问题相同。 基本上执行@johannes-scharlach 描述的命令肯定比每个工作区的锁文件更可行。

现在还有一个针对嵌套工作区

你如何看待 Yarn 工作区只是一个很小的核心——一个没有任何特定功能的包的声明

工作区不会发生巨大变化,我认为我们对他们当前的界面感到满意。

如果你想在你的工作区运行一些脚本,你会做yarn workspaces run <script>

这已经是可能的(v1.10,#6244)。

如果您想将当前的锁文件迁移到提升结构,您可以运行yarn workspaces hoist-dependencies

由于我们不会更改工作区界面,因此情况正好相反( dehoistDependencies )。

我不喜欢这个的地方是它需要一个技术行为(提升)并试图把它变成一个语义行为。 您应该关注用户故事,然后找出实现而不是相反。

在这种情况下,我认为通过扩展yarn --focus可以更好地解决您的用例(“仅在特定包中安装依赖项”)。

我想核心问题是工作空间是否严格需要提升和单个yarn.lock文件。 我的意思是,是什么真正定义了它们,还是“只是”它们历史上获得的第一个特征?

例如,在我们的用例中,工作区的最佳假设行为是:

  • 在开发时提升node_modules以提高效率。
  • 保留用于构建的本地yarn.lock文件(我们在 Docker 中构建特定的包,其他人在此线程中也提到了这一点),并且包可以锁定它们的特定版本。 另见#6563。
  • 即使您不需要(或必须避免)提升,也可以通过yarn workspaces run <script>运行脚本。

可以使用nohoist禁用提升, run可以通过不使用命令来“禁用”,但不可能“禁用”单个yarn.lock文件,我我不确定它是否是一个无法禁用的核心功能,或者它是否还没有被足够地请求:)

我认为解决这个问题的最好方法是让yarn install --app-mode package@version

这样,您可以在发布特定版本的应用程序时简单地复制工作区锁定文件,并在app-mode将尊重捆绑的锁定文件。

Yarn 不必安装整个锁文件; 它应该能够轻松地仅提取与该包相关的依赖关系图部分。

事实上,即使现在手动操作也很容易:

  • 直接从注册表下载压缩包(yarn 没有等价物,npm 有: npm pack package@version
  • 将 gzip 解压到 node_modules/package
  • cd 到 node_modules/package
  • 那里运行 yarn install --production (它将尊重捆绑的锁文件)

编辑:不幸的是,这都是错误的,因为工作区锁定文件不包括工作区中的包版本,这可能是应用程序包的依赖项。 从工作区锁定文件创建应用程序锁定文件时,需要比复制更多的内容。

我不确定单独的锁文件是否是答案,但我有类似的问题。 我有一个带有 CLI 和后端的 monorepo 设置。 CLI 需要一些特定于平台的软件包,并且只能在具有特定设置的台式机上运行。 另一方面,我需要能够将我的 api 构建到 docker 映像中,这在当前的工作区实现中从根本上是不可能的。

这里的用例与@samuela非常相似! 这将是非常有帮助的!

与其他“真实”用例相比,我的用例可能看起来很可笑。 但是我有一个用于某些实用程序的 monorepo - 在这种情况下是反应钩子 - 在packages/*

我在packages/*旁边有第二个工作区,那就是local/* 。 这实际上是在 gitignore 上,其想法是公司的开发人员可以在那里做任何他们喜欢的事情,例如将create-react-app应用程序放在那里并在开发过程中测试钩子。

现在,虽然local/*包在 gitignore 上,但根yarn.lock只是膨胀和污染 - 并签入 git - 因为本地工作区。

我想要的是一种指定某些工作区应使用某些特定锁定文件的方法,例如某些映射,如下所示:

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

或者甚至是一种指定“根本不要将 _this_ 工作区中的任何内容放入锁定文件”的方法。

但是,是的,我的首先不是一个严重的用例:)

我不确定单独的锁文件是否是答案,但我有类似的问题。 我有一个带有 CLI 和后端的 monorepo 设置。 CLI 需要一些特定于平台的软件包,并且只能在具有特定设置的台式机上运行。 另一方面,我需要能够将我的 api 构建到 docker 映像中,这在当前的工作区实现中从根本上是不可能的。

你搞定了——在我看来,yarn.lock 文件的核心好处之一是用于创建冻结的生产版本! Yarn 的创造者是否忘记了这一点?

解决单锁文件问题的另一个论据是代码所有权。 如果您有一个使用类似 GitHub CODEOWNERS 功能的 monorepo,则不可能将包的完全所有权授予一组开发人员。 那是因为如果他们在自己的工作区中安装了一些东西,他们总是会更改根级别的锁文件。 此更改需要得到锁文件的代码所有者的批准,如果有足够规模的 monorepo,它将与原始工作区的所有者不同。

可以选择生成每个工作区锁定文件的另一个原因是:Google App Engine 拒绝在没有锁定文件 (NPM/Yarn) 的情况下启动 Node 服务。 这对他们来说是优秀的 devops,但对我们来说是一种痛苦。 到目前为止,我们拥有的选项是:

  • 使用 env vars 部署所有服务,指示我们指的是哪个服务,并将我们的yarn start (唯一支持的入口点)修改为基于 env vars 的分支
  • 让构建脚本将主锁文件复制到每个工作区并仅部署我们感兴趣的服务。(感谢 @johannes-scharlach)

最终,我认为每个工作区锁定文件生成的yarn install --workspace-lockfile命令将是最好的解决方案。

选择包级锁定文件也会对我们有所帮助。 我们的用例有点不同,我们正在尝试一种管理本地依赖项的新方法。

所以我们已经有一些单一的存储库,我们有一些只包含一个包的存储库。 这些都是已发布的,因此可以一起使用,但是很多时候将它们放在本地并进行符号链接非常有用。

但是一些开发人员很难管理符号链接等,因此我们正在尝试一个标准的空纱线工作区单存储库,我们都将其克隆到我们的机器上,然后我们将包存储库克隆到该本地单存储库中。 我们有些人可能只是有一个包克隆,有一些可能有5这是超级方便,使本地,跨回购,交叉依赖性发展的绝对轻而易举。

但是我们遇到了一个我们无法解决的问题,编辑依赖项不会更新本地纱线锁文件,它总是更新我们不更新更新依赖项的空单存储库的根目录(它包含下面的所有内容) /packages gitignored。

可以选择不将锁定文件写入单存储库根目录会很棒,并让它们在包级别写出。

作为说明,我还遇到了其他人提到的围绕 Docker 的部署和构建问题,这也可以解决这个问题!

这个功能对我来说也很有价值。 就我而言,我有一个 monorepo,其中一些包部署到不同的平台。 一个是使用 Now.sh 部署的 Next.js 应用程序,另一个是部署到 Firebase 的一堆云功能。

在这两种部署中,源代码都被捆绑并上传,以便在云中安装和构建。 没有yarn.lock文件与源一起意味着依赖项是使用 package.json 中的版本安装的,并且没有任何版本被锁定。

我也希望能够在每个工作区中启用 yarn.lock 文件。

我意识到纱线工作区主要用于 monorepos,但我们的用例与@LeeCheneler的非常相似。

基本上,我们创建了一个 React 组件库,我们将其用作不同项目(都有自己的存储库)中的依赖项。 通过使用纱线工作区,我们可以轻松引用组件库的本地版本,并将更改快速传播到我们其他项目的本地版本。 当我们推送到生产时,我们也不需要修改 package.json 因为依赖项library: "*"无需任何更改即可工作。 我们唯一的问题是,如果没有纱线锁文件,每个项目的生产版本可能会使用不同的包版本。

我不得不想象这个问题在任何使用纱线工作区的包开发人员中都很常见。

顶级锁文件的另一个关键问题是它破坏了 Docker 的层缓存。 通常可以通过首先复制 package.json 和 yarn.lock 来优化 Docker 缓存。 如果 Docker 没有发现这些文件有任何变化,它将使用上一层。 但是,如果该锁文件是整个 monorepo 的单个锁文件,则任何包中的任何更改都会使缓存无效。 对我们来说,这会导致 CI​​/CD 管道极其缓慢,其中每个包都在没有缓存的情况下构建。 还有其他工具,如 Lerna,可以检查包更改以运行某些脚本。 这也会中断,因为锁文件中的依赖项更改可能不会因为处于顶层而被选中。

很抱歉提出这个(有点老)的问题,但我也有一个用例,这会有所帮助。 我有 10 个左右的独立托管和开发的微服务,但如果有一个中央工作区存储库会很好,您可以在其中键入yarn install以在所有文件夹中安装依赖项,然后运行yarn start运行将启动所有微服务的脚本。 这是可以用一个相对简单的脚本完成的事情,但它似乎也适用于纱线工作区,但我无法在尊重每个微服务中的 yarn.locks 的同时让它工作

@nahtnam我认为 Yarn 1.x monorepo 的想法有点不同。 它不是关于一个屋檐下的独立项目,而是关于一个单一的大项目,它的一些组件被暴露(称为工作区)。 这些组件并非完全独立,但可以像这样使用:Babel 编译器作为一个更大的实体, preset-env作为子模块。 此外,它们的依赖项是统一的,从某种意义上说,它们是同质的:如果某些包依赖于core-js ,那么它们中的每一个都应该是相同的core-js版本,因为你不能使用单个根锁定文件锁定不同的版本,项目的部分依赖于不同的版本也没有意义。 并且因为它是一个项目,所以它的所有组件都会自动链接到根node_modules ,这对于完全独立的项目来说很奇怪。

因此,如果您正在开发一组微服务(它们是独立的,其中一些多年不​​会被触及,而另一些将创建/更新,可能由不同的团队开发),那么它们应该有没有 root 的个人锁定文件锁(Docker 问题也出现在这里)。 唯一的问题是什么工具将有助于运行脚本。 Lerna 可能是一个答案,因为它与 Yarn 无关。

唯一的问题是什么工具将有助于运行脚本。 Lerna 可能是一个答案,因为它与 Yarn 无关。

@the-spyke 不仅如此。 yarn workspaces也可以解决,以与npm link相同的方式链接模块进行开发,这是我们使用它的主要原因。 npm link在某些情况下效果不佳。

@the-spyke 谢谢! 出于某种原因,我认为它被翻转了(lerna vs yarn workspaces)。 我查看了 lerna,看起来它解决了我的问题

我最终为我正在处理的每个项目使用了工作区,因为拥有一个单独的utilities包是多么容易,我可以在需要时更新它(即使它已发布),而且事实上如果我想 fork 一些包(基本上允许我同时在两个分支上工作,这对我来说是闻所未闻的),以及每当我想使用utilities的包时,我都不必重新安装依赖项package.json (但在单独安装的情况下这显然是一个好主意,并且是必需的)用于自动 IDE 导入); 一切正常。 @the-spyke 提出了一个很好的观点,也许independent projects under one roof不是工作区的目的,但这几乎就是我在这里所做的:我有一个monorepo-base存储库,其中不包括packages文件夹,而packages下的每个文件夹都是一个单独的独立 git repo。
image
当然,这让我想到了这个话题; 因为我没有将所有包作为一个 repo 提交,所以 root 级别的yarn.lock是没有意义的。 我一直在使用--no-lockfile来安装所有东西,最近遇到了class-validator版本冲突的问题。 现在我会将所有 deps 锁定到特定版本(老实说,这种级别的控制对我来说更有意义)并看看它是如何工作的。 我会再次阅读整个流程,也许有一些技巧可以用于我的用例。

附注。
yarn why没有锁文件就不能工作,我注意到有些人提到了 App Engine 的问题。 我想如果每个包都是一个单独的存储库,每次安装时都可以生成锁定文件(不将其添加到 VCS)? 不确定那个特定的情况。

不幸的是,如果您的构建映像需要运行时节点模块而不包含在某个构建文件夹中,那么@johannes-scharlach 建议的解决方案会变得非常混乱,因为您必须确切地弄清楚需要哪些模块才能运行,并煞费苦心地将它们复制到最后的构建阶段。

(有点跑题) @GrayStrider你也可以使用 package.json 中的“resolutions”字段——这是强制嵌套依赖的版本的唯一方法,例如,如果你希望所有的 lodashes 都是准确的版本,不管嵌套多深。 然而,这可能会引入非常微妙的错误,这些错误很难被发现。

这是我们找到的解决方案 - 对现有 Docker 工作流程的影响最小。

  1. ln project/yarn.lock packages/package1/yarn.lock - 从根yarn.lock到每个包创建一个硬符号链接。
  2. COPY yarn.lock .到每个packages/package1/Dockerfile
  3. yarn install在 Docker 中

优点

  • 不必将整个 monorepo 复制到图像层中
  • 不必将包级别的 Dockerfile 合并到根目录下的单个 Dockerfile 中
  • 基本满足工作空间锁文件的要求

缺点

  • --frozen-lockfile不起作用。 由于工作区包不包含在yarn.lock ,因此纱线看到您“添加”到package.json中的包,该包不存在于yarn.lock

无论如何,这都是一个小缺点,因为您可以通过执行yarn --frozen-lockfile作为 CI/CD 管道的第一步来解决它

编辑:
顺便说一句,我真的认为关于安装的纱线文档可能会更清楚地说明包解析过程如何使用锁文件。

编辑:
所以事实证明 git 实际上并不支持硬链接,它只支持软符号链接,所以这个策略是行不通的。

另一种选择是简单地使用预提交 githook 将yarn.lock复制到您的每个工作区......这并不理想,因为它仍然允许从本地机器部署时出现问题。

@dan-cooke 非常感谢您的见解,非常感谢!

@dan-cooke,这会破坏 Docker 的层缓存,因为任何工作区中的新依赖都会使所有 Dockerfile 的安装层无效。

@migueloller如果安装层不能改变,那么你不应该对所有包使用一个锁定文件。 将依赖项展平和提升到单个巨型列表中是 Workspaces 的全部目的。 它不像“破坏”Docker缓存,缓存无效,因为package.json中没有指定真正的依赖项,所以你包的最终代码取决于yarn.lock 。 因此,您需要在yarn.lock每次更改时重建所有包,而 Docker 会做正确的一切。 它不像where every package is built without the cache因为所有构建都可以使用yarn install重用该层(可能您需要设置它)。

@米格罗勒
正确的。 幸运的是,我们不会经常添加新的依赖项,因此这最多只会出现一次 sprint。

即便如此,(对我们来说)在 Docker 中为可复制的依赖项付出的代价很小

@the-spyke,确实如此。 这就是为什么这个问题是关于每个包有一个单独的锁文件。 这样,缓存层只有在包的依赖关系发生变化时才会失效,并且与其他包无关。

也许将这个讨论转移到 npm 本身也是值得的,它也支持从 v7.0 开始的工作区。

我一直在研究相关主题,并想对我上面的评论进行一些澄清(我想主要针对经验不足的开发人员,因为我面临的问题在某种程度上是由于我未能理解lockfile的重要性

“将所有依赖项锁定到特定版本”称为固定; 不幸的是,如果依赖项更新(这里是文章的最后一段),它不会阻止事情的潜在破坏,我没有考虑过。 这正是 lockfile 旨在防止发生的事情。

过去,我在多次中断更新上浪费了足够多的时间; 我将尝试在 monorepo 中使用lockfile ,因为我更愿意处理合并冲突和组织问题,而不是由小更新引起的不可见错误。

上面说了,我非常期待这个问题的任何进展

@migueloller个人锁定文件意味着个人 Yarn Monorepos。 您不能拥有工作区的锁定文件,因为它破坏了 Monorepo 中的 deps 一致性。 如果你想这样做,你就偏离了 Yarn Monorepo 的最初想法:它是关于将 deps 统一、提升和减少到根中的一个平面列表中,而不是在不同的工作区中拥有不同的版本(甚至整个子树) .

@the-spyke 但最初的问题恰恰相反。 每个工作区一个锁定文件。

我不明白每个工作区不能有一个锁文件。

它打破了 Monorepo 的一致性? 肯定有发展。 但是,当您必须从每个工作区部署轻量级微服务时,共享依赖项的全部目的就消失了

共享的、提升的 deps 只在开发中有意义。

要在 Docker 中运行工作区,它需要一个锁文件。

@dan-cooke如您所见,我在 2018 年也遇到过这个问题,但现在我有了不同的看法。

你说的是 Docker 和微服务。 但是如果我开发一个常规的npm包呢? 我没有要固定的production依赖子树,因为它们将由最终用户根据我的dependencies规范提供。 所以,我想要的是最大化我的开发经验,而 Monorepos 和 Yarn Workspaces 正是如此。

同时,如果您正在开发微服务 (MS),则有两种可能的情况:

  1. 独立项目。 有些 MS 正在开发中,有些已多年未动。 在这种情况下,它们是完全独立的。 UserService使用[email protected]MessagesService使用[email protected] 。 将文件夹从 Workspaces 链接到根node_modules并不是那么容易的世界。 因此,拥有根锁定文件毫无意义。 创建单独的文件(根)并独立管理它们。 这在 Yarn 文档中称为Multirepo 。 但是现在您要说的是“为了方便起见,我想在根文件夹的不同文件夹中运行任务”。 这是一个完全不同的话题。

  2. 具有统一依赖项的项目,例如 Jest/Babel/etc。 这就是工作区的用途,但在 MS 中有额外的要求。 在像 linting 和测试这样的 CI 阶段,一切正常,因为它的工作方式与您在开发人员机器上的工作方式相同:由 Yarn 安装到根 node_modules 中并扁平化的 deps。 另外,您可能会缓存yarn install阶段以加速并发构建。

    在生产中,情况完全不同:从您只需要一个工作区的 deps 开始,到如何安装utils包结束? 它应该作为 tarball 链接还是下载? 所以,你真正需要的不是每个工作区都有锁定文件,而是有一个像yarn install --prod <workspace>这样的命令,你可以运行指定一个工作区,它只会安装生产部门,同时忽略其他未引用的工作区。 就像如果我的data WS 依赖于utils WS,而不依赖于logging WS,那么logging本身及其 deps 不应该出现在node_modules 。 类似的结果,但“每个工作区的锁定文件”的方法完全不同。

    如果您将构建包发布到存储库(npm、Arifactory、GutHub)中,只需将锁​​定文件复制到工作区并在此处执行yarn install --prod即可获得类似的行为。 它应该警告过时的文件,但不是从头开始使用新版本重新创建它应该只是从中删除多余的 deps(刚刚尝试过并且看起来合法)。 使用离线镜像应该会更好更健壮。

    最后,您已经为 Multirepos 完全实现了 Focused Workspaces。

所以,我所说的也许问题看起来不像是什么。

@the-spyke
我明白你在说什么。 我认为这绝对归结为如何实现这一结果。 也许每个工作区的锁文件实际上并不是实现预期结果的最佳方式。 你提出了一些很好的观点。

无论如何,它绝对不是“一刀切”的解决方案

@the-spyke,你提出了好点。 也许需要更多思考 Yarn 工作区旨在解决哪些问题,以及使用它来管理大型 monorepos 是否符合该设计。

我很好奇你将如何解决这样的场景:

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

lib3是一个共享库,依赖于app1app2lib1仅由app1lib2仅由app2 。 根据您的建议, lib1app1应该在他们自己的工作区中,并拥有自己的锁文件,与lib2app2 。 现在的问题是做什么用的lib3如果_both_ app1app2依赖于它? 也许可以让两个工作区( app1app2 )将lib3到他们的工作区,然后在每个应用程序中运行yarn install ? Yarn 允许这样做吗? 如果想要在本地开发中同时运行app1app2会不会有任何冲突(也许app1是一个 React 应用程序,而app2是一个 GraphQL API) ? 这听起来像它可以工作。

下一个要问的问题是“如何从使用这种方法提升的好处中获益?” 例如,如果app1app2共享许多共同的依赖项,那么提升它们会很好。 不过,我可以看到这可能超出范围,并且是 Yarn PnP 需要解决的问题(它不会将文件复制到 node_modules 而是具有共享缓存)。

我要试一试,然后回来报告。 如果这最终奏效,那么也许我们一直在错误地使用 Yarn 工作区......

编辑:我试过了,它确实有效。

我现在改变了我的立场,并意识到虽然在使用 Yarn 工作区管理整个 monorepo 时,每个工作区都有一个单独的锁文件可能是我想到的第一件事,但这可能不是正确的问题。 一个更好的问题可能是“Yarn 工作区是否旨在管理 monorepo?”。 像往常一样,答案是“视情况而定”。

如果您是 Babel 并且您有一个团队在开发 monorepo,并且一切都旨在同步更改,那么是的,这就是 Yarn 工作区的设计目的。 但是,如果您是一个拥有多个团队的组织并且您使用的是 monorepo,则您可能不想使用单个 Yarn 工作区根来管理整个 monorepo。 您可能只想在 monorepo 中使用 Yarn 的默认行为或多个 Yarn 工作区根。 这将取决于您正在构建的应用程序、有多少团队等。

对我们来说,很明显,对于每个可部署实体(在我们的例子中,有一个 Dockerfile),我们希望为每个实体(无论它是否是工作空间根)单独完成yarn install 。 这提供了代码所有权的清晰性,允许以不同的节奏发生的隔离部署,解决了锁定文件和 Docker 等的缓存问题。 但是,这有一些缺点:

  • 重复的 node_modules 包怎么办? 这是 monorepo 的一类常见问题,虽然 Yarn 工作区有助于提升,但它不是一般的 monorepo 解决方案。 不过,还有其他解决方案。 例如,Yarn PnP 会处理这个问题。 你也可以在没有 Yarn 的情况下使用 Lerna 并使用--hoist选项。
  • 在开发过程中跨工作区运行命令的效用如何? 同样,Yarn 工作区允许您执行此操作,但这并不意味着应该将整个 monorepo 设为 Yarn 工作区根。 每个团队构建必要的工具和脚本会有所不同,并取决于他们的 monorepo。 Yarn 工作区可能不是作为 monorepo 任务运行器设计的。 人们可能会尝试稍微弯曲 Yarn 工作区来完成这项工作(即,使用yarn workspace ...跨 monorepo 运行 NPM 脚本)但重要的是要记住,整个 monorepo 的单个工作区根可能不会给你你需要的东西,除非你像 Babel、Jest、React 等。

运行 monorepo 还会带来许多其他问题。 例如,如何跟踪依赖项并仅重建更改以节省 CI 时间的内容? Yarn 工作区可以通过让您查询依赖关系图来提供帮助。 例如,Lerna 这样做是为了允许对正在运行的命令进行拓扑排序。 Yarn v2 实际上也允许您查询依赖关系图。 包管理器 PNPM 也会这样做。 但我认为,根据 monorepo 的复杂性,人们可能想尝试为此构建的工具(不是包管理器),如BazelPantsBuck等。

@migueloller根据您的要求,我看到您不需要严格独立的软件包或其他奇特的东西,您也确实需要更精简的开发人员安装。 在这种情况下,您应该从常规 Yarn Monorepo 开始:单个根和所有包作为工作区。 您将拥有更快的安装时间、更低的磁盘使用率,并且app1将使用本地链接的lib1lib3 。 唯一的缺点是 CI 缓存失效更频繁,因为将 devDep 添加到lib1将更新共享的yarn.lock 。 但通常您不会更新依赖项,因为通常会担心这种权衡。

lib1可能取决于lodash@^4.5.0" and lib2 may depend on lodash@^4.10.0"。在 Monorepo 的情况下,您希望使用lodash的单个版本,所以Yarn 将安装与这两个说明符最新兼容的东西,例如提升到 root node_modules 的 ` [email protected] "。 并且在更新的情况下,您正在更新单个统一版本,因此所有工作区始终保持在同一页面上。 这是所需的行为。

也有独立团队开发独立项目的情况。 在这种情况下, proj1可能希望保持在[email protected] ,而proj2具有[email protected]并用自己的节奏更新它。 当然,您可以通过使用更严格的说明符(如lodash@~4.5.0来实现这一点,但这可能仍然过早地更新第二级依赖项。 因此,总的来说,这些项目可能完全不相关,只是碰巧在一个 git 存储库中。 在这种情况下,没有理由将它们绑定为 Yarn Monorepo 和共享锁文件的权衡独立性。 把它们当作它们本来的样子:将项目与它们独立的生活分开。 这就是所谓的 Multirepo。 在 Unix 上,您的所有目录都在/ ,但这并不意味着您 PC 上所有可能的 JS 项目都应该是 Monorepo :-)

为生产使用构建尽可能少的 Docker 映像与 Yarn 完全无关,但您可以强制 Yarn 重用名为yarn.lock的开发工件,并帮助您完成此任务。

@the-spyke,在这个例子中,我只使用了 3 个工作区,但在我们的实际存储库中,我们有 20 多个工作区,其中包含库和前端和后端部署工作负载的组合。 我现在看到的方式是我们有一个 monorepo(或你称之为 multirepo 的东西),其中拥有多个具有独立锁文件的 Yarn 工作区根可能是有意义的。 我们正在考虑使用可部署单元作为分离单元,它与锁文件很好地对齐。

我认为对我来说,使这项工作非常出色的原因是 Yarn 工作区支持工作区根目录之外的路径,即使最初的博客文章另有说明。 例如,你可以有这个:

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

我们有与@migueloller相同的用例,一个可能的想法是让 Yarn 支持多工作区,如下所示:

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

Yarn 会维护两个额外的锁文件(我想主要的yarn.lock仍然存在):

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

在为前端构建 Docker 镜像时,我们会创建一个包含以下内容的上下文(例如,通过 tar ):

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

我没有深入思考的是,如果前端和后端锁定不同的版本,是否可以安装(链接在node_modules )正确版本的依赖项。 但纯粹从高层次的角度来看,二维 Yarn 工作区可能就是我们所追求的。

这里也发布

看起来您不需要每个工作区的锁定文件,而是需要每个工作区的 node_modules 进行部署

@gfortaine ,如果您阅读讨论,您会意识到事实并非如此。 有一个单独的锁文件的原因与安装无关,而是有一个只有在特定包更改时才会更改的锁文件。 顶级锁文件会随着 _every_ 工作区依赖项的变化而改变,但作用域为单个包的锁文件只会在 _那个_ 包的依赖项更改时发生变化。

值得一提的是,这可以在用户空间中完成。 使用@yarnpkg/lockfile包可以解析顶级锁文件,然后使用yarn workspaces info可以确定工作区依赖关系。 使用此信息,连同每个工作区的package.json ,可以为每个工作区生成一个锁文件。 然后,可以在 repo 中将其设置为postinstall脚本,以保持这些单独的锁文件与顶级锁文件同步。

我可能会尝试构建它并报告我的发现。

看起来我刚刚找到了这个实现,尽管是针对 pnpm 的: @pnpm/make-dedicated-lockfile

希望这有帮助👍

我对这种行为的需求(每个工作区的版本控制,但每个包中仍然有锁文件)是我有一个嵌套的 monorepo,其中一个子树完全导出到另一个 repo,因此必须保持独立。 现在我坚持使用 lerna/npm 和一些自定义逻辑来尝试平衡版本。 如果yarn(我猜是v2,因为嵌套支持就在那里?)可以一次管理所有这些,但在每个中保留全局固定的正确子集,那就太好了。

我想,安装后脚本可能会尝试直接管理锁定文件。 听起来很复杂,但无论如何都会很好。

此页面是否有帮助?
0 / 5 - 0 等级