Turborepo 0.4.0
我很高兴地宣布 Turborepo v0.4.0 版本的发布!
- 速度提升 10 倍:
turbo
已使用 Go 从头重写,使其速度更快 - 更智能的哈希:改进的哈希算法现在考虑已解析的依赖项,而不仅仅是整个根 lockfile 的内容
- 部分 lockfile / 稀疏安装:生成根 lockfile 和 monorepo 的精简子集,其中仅包含给定目标所需的必要包
- 细粒度调度:通过
pipeline
配置改进了任务编排和选项 - 更好的缓存控制:现在您可以按任务指定缓存输出
使用 Go 重写
虽然我最初在 TypeScript 中原型化了 turbo
,但很明显,路线图上的某些项目需要更好的性能。经过大约一个多月的工作,我很高兴最终发布 Go 版本的 turbo
CLI。它不仅可以在毫秒内启动,而且新的 Go 实现的哈希速度比 Node.js 实现快 10 到 100 倍。凭借这个新的基础(以及您即将阅读到的一些功能),Turborepo 现在可以扩展到星系级大小的项目,同时保持闪电般的速度,这一切都归功于 Go 出色的并发控制。
更好的哈希
v0.4.0 中的哈希不仅更快,而且更智能。
主要的改变是 turbo
不再在其哈希器(负责确定给定任务是否存在于缓存中或是否需要执行的算法)中包含根 lockfile 内容的哈希。相反,turbo
现在基于根 lockfile 哈希包的 dependencies
和 devDependencies
的已解析版本集。
旧的行为会在根 lockfile 以任何方式更改时使缓存失效。使用这种新行为,更改 lockfile 只会使受添加/更改/删除的依赖项影响的包的缓存失效。虽然这听起来很复杂,但再次强调,这仅仅意味着当您从 npm 安装/删除/更新依赖项时,只有那些实际受更改影响的包才需要重建。
实验性功能:精简的工作区
我们最大的客户痛点/请求之一是改进使用大型 Yarn 工作区(或任何工作区实现)时的 Docker 构建时间。核心问题是工作区最好的功能——将您的 monorepo 减少到单个 lockfile——在 Docker 层缓存方面也是最糟糕的。
为了帮助阐明问题以及 turbo
现在如何解决它,让我们看一个例子。
假设我们有一个使用 Yarn 工作区的 monorepo,其中包含一组名为 frontend
、admin
、ui
和 backend
的包。我们还假设 frontend
和 admin
都是 Next.js 应用程序,它们都依赖于同一个内部 React 组件库包 ui
。现在我们还假设 backend
包含一个 Express TypeScript REST API,它实际上不与我们 monorepo 的任何其他部分共享太多代码。
以下是 frontend
Next.js 应用程序的 Dockerfile 可能的样子
虽然这可行,但有些地方可以做得更好
- 您手动
COPY
进入构建目标应用程序所需的内部包和文件,并且需要记住哪些需要先构建。 - 您将根
yarn.lock
lockfileCOPY
到 Dockerfile 中非常早期的正确位置,但此 lockfile 是整个 monorepo 的 lockfile。
当您的 monorepo 变得越来越大时,最后一个问题尤其令人痛苦,因为对该 lockfile 的任何更改都会触发几乎完全的重建,而不管应用程序是否实际受到新的/更改的依赖项的影响。
...直到现在。
借助全新的 turbo prune
命令,您现在可以通过确定性地为目标包生成带有精简 lockfile 的稀疏/部分 monorepo 来解决这个难题——而无需安装您的 node_modules
。
让我们看看如何在 Docker 中使用 turbo prune
。
那么 turbo prune
的输出到底是什么?一个名为 out
的文件夹,其中包含以下内容
- 一个名为
json
的文件夹,其中包含精简的工作区的 package.jsons - 一个名为
full
的文件夹,其中包含精简的工作区的完整源代码,但仅包括构建目标所需的内部包 - 一个新的精简 lockfile,其中仅包含原始根 lockfile 的精简子集,以及精简工作区中包实际使用的依赖项。
由于上述原因,现在可以将 Docker 设置为仅在有真正理由这样做时才重建每个应用程序。因此,只有当 frontend
的源代码或依赖项(内部或来自 npm)实际发生更改时,它才会重建。admin
和 backend
也是如此。对 ui
的更改,无论是对其源代码还是依赖项的更改,都将触发 frontend
和 admin
的重建,但不会触发 backend
的重建。
虽然这个例子看起来很简单,但试想一下,如果每个应用程序都需要长达 20 分钟才能构建和部署。这些节省确实开始迅速累积,尤其是在大型团队中。
管道 (Pipelines)
为了让您更好地控制 Turborepo,我们在 turbo
的配置中添加了 pipeline
。pipeline
中的这个新字段允许您指定 monorepo 中的 npm 脚本如何相互关联,以及一些额外的按任务选项。然后 turbo
使用此信息来最佳地调度 monorepo 中的任务,从而消除原本会存在的水瀑效应。
以下是它的工作原理
上述配置随后将被 turbo
解释为最佳地调度执行。
这实际上意味着什么?在过去(如 Lerna 和 Nx),turbo
只能按拓扑顺序运行任务。通过添加管道,turbo
现在除了实际的依赖关系图之外,还构建了一个拓扑“动作”图,它使用该图来确定任务应以最大并发执行的顺序。最终结果是您不再浪费空闲的 CPU 时间等待东西完成(即不再有水瀑效应)。
改进的缓存控制
由于 pipeline
,我们现在有了一个很好的位置来按任务打开 turbo
的缓存行为。
在上面示例的基础上,您现在可以像这样在整个 monorepo 中设置缓存输出约定
注意:目前,pipeline
存在于项目级别,但在以后的版本中,这些将在每个包的基础上可覆盖。
下一步是什么?
我知道这很多,但还有更多内容即将推出。以下是 Turborepo 路线图上的下一步。
- 一个着陆页!
- 使用
@turborepo/server
的远程缓存 - 构建扫描、遥测和指标以及依赖关系和任务图可视化
- 桌面控制台 UI
- 智能
watch
模式 - TypeScript、React、Jest、Node.js、Docker、Kubernetes 等的官方构建规则
鸣谢
- Iheanyi Ekechukwu,感谢他在 Go 生态系统中指导我
- Miguel Oller 和 Makeswift 团队,感谢他们在新的
prune
命令上进行迭代