Turborepo 0.4.0
我很高兴地宣布 Turborepo v0.4.0 版本的发布!
- 速度提升 10 倍:
turbo
已从头开始用 Go 语言重写,使其速度更快。 - 更智能的哈希:改进的哈希算法现在考虑已解析的依赖项,而不仅仅是整个根 lockfile 的内容。
- 部分 lockfiles / 稀疏安装:生成根 lockfile 的精简子集和 monorepo,其中仅包含给定目标所需的必要包。
- 细粒度的调度:通过
pipeline
配置改进了任务编排和选项。 - 更好的缓存控制:您现在可以为每个任务指定缓存输出。
用 Go 重写
虽然我最初用 TypeScript 原型化了 turbo
,但很明显,路线图上的某些项目需要更好的性能。经过大约一个月左右的工作,我很高兴终于发布了 turbo
CLI 的 Go 版本。它不仅在毫秒内启动,而且新的 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
目标应用程序所需的内部包和文件,并且需要记住哪些需要先构建。 - 您在 Dockerfile 的早期位置
COPY
根yarn.lock
lockfile,但此 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 分钟的时间来构建和部署。这些节省确实开始迅速积累起来,尤其是在大型团队中。
管道
为了让您更好地控制 Turborepo,我们在 turbo
的配置中添加了 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
命令