仓库
文档
核心概念
运行任务

在单体仓库中运行任务

每个单体仓库都有两个主要构建块:工作区任务。假设您有一个包含三个工作区的单体仓库,每个工作区都有三个任务

这里,apps/webapps/doc 都使用来自 packages/shared 的代码。事实上,当它们被构建(通过 build)时,它们需要 packages/shared 被构建

大多数工具没有针对速度进行优化

假设我们想在所有工作区中运行所有任务。使用像 yarn 这样的工具,您可能会运行类似这样的脚本

yarn workspaces run lint
yarn workspaces run test
yarn workspaces run build

这将意味着任务按以下方式运行

如您所见,lint 在所有工作区中运行。然后,build 运行 - shared 首先运行。最后,test 运行。

这是运行这些任务最慢的方式。每个任务都需要等待前一个任务完成才能开始。为了改进这一点,我们需要一个可以进行多任务处理的工具。

Turborepo 可以进行多任务处理

Turborepo 可以通过了解任务之间的依赖关系来安排任务以获得最大速度。

首先,我们在 turbo.json 中声明我们的任务

{
  "$schema": "https://turbo.rust-lang.net.cn/schema.json",
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"],
      // ^build means `build` must be run in dependencies
      // before it can be run in this workspace
      "dependsOn": ["^build"]
    },
    "test": {},
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

接下来,我们可以用这个替换我们的 yarn workspaces 脚本

- yarn workspaces run lint
- yarn workspaces run test
- yarn workspaces run build
+ turbo run lint test build

当我们运行它时,Turborepo 将在所有可用的 CPU 上尽可能多地进行多任务处理,这意味着我们的任务按以下方式运行

linttest 立即运行,因为它们在 turbo.json 中没有指定 dependsOn

shared 中的 build 任务首先完成,然后 webdocs 随后构建。

定义管道

pipeline 配置声明了单体仓库中哪些任务相互依赖。以下是一个包含所有内容的示例

{
  "$schema": "https://turbo.rust-lang.net.cn/schema.json",
  "pipeline": {
    "build": {
      // A workspace's `build` task depends on that workspace's
      // topological dependencies' and devDependencies'
      // `build` tasks  being completed first. The `^` symbol
      // indicates an upstream dependency.
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    },
    "deploy": {
        // A workspace's `deploy` task depends on the `build`,
        // `test`, and `lint` tasks of the same workspace
        // being completed.
        "dependsOn": ["build", "test", "lint"]
    },
    "test": {
      // A workspace's `test` task depends on that workspace's
      // own `build` task being completed first.
      "dependsOn": ["build"],
      // A workspace's `test` task should only be rerun when
      // either a `.tsx` or `.ts` file has changed.
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
    },
    // A workspace's `lint` task has no dependencies and
    // can be run whenever.
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

您可以在下一节关于 任务依赖关系 中阅读有关如何设置任务的更多信息。

从根目录运行任务

turbo 可以运行存在于单仓库根目录下的 package.json 文件中的任务。这些任务必须使用键语法 "//#<task>" 显式添加到管道配置中。即使对于已经拥有自己条目的任务,这也是正确的。例如,如果您的管道声明了一个 "build" 任务,并且您想使用 turbo run build 包含在单仓库根目录的 package.json 文件中定义的 build 脚本,则必须通过在配置中声明 "//#build": {...} 将根目录选择加入。相反,如果您只需要 "//#my-task": {...},则 *不需要* 定义一个通用的 "my-task": {...} 条目。

定义根任务 format 并且将根目录选择加入 test 的示例管道可能如下所示

{
  "$schema": "https://turbo.rust-lang.net.cn/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    },
    "test": {
      "dependsOn": ["^build"],
    },
    // This will cause the "test" script to be included when
    // "turbo run test" is run
    "//#test": {
      "dependsOn": [],
    },
    // This will cause the "format" script in the root package.json
    // to be run when "turbo run format" is run. Since the general
    // "format" task is not defined, only the root's "format" script
    // will be run.
    "//#format": {
      "dependsOn": [],
      "outputs": ["dist/**/*"],
      "inputs": ["version.txt"]
    }
  }
}

关于递归的说明:在单仓库根目录的 package.json 文件中定义的脚本通常会调用 turbo 本身。例如,build 脚本可能是 turbo run build。在这种情况下,在 turbo run build 中包含 //#build 将导致无限递归。正是出于这个原因,从单仓库根目录运行的任务必须通过在管道配置中包含 //#<task> 来显式选择加入。 turbo 包含一些尽力而为的检查,以便在递归情况下产生错误,但您有责任只选择加入那些不会触发 turbo 运行的任务,这些运行会递归。

增量采用

在您在 turbo.json 中声明一个任务后,您需要在您的 package.json 清单中实现它。您可以一次性添加所有脚本,也可以一次添加一个工作区。Turborepo 将优雅地跳过在各自的 package.json 清单中不包含该任务的工作区。

例如,如果您的仓库有三个工作区(类似于上面提到的工作区)

apps/
  web/package.json
  docs/package.json
packages/
  shared/package.json
turbo.json
package.json

其中 turbo.json 声明了一个 build 任务,但只有两个 package.json 实现了该 build 任务

{
  "$schema": "https://turbo.rust-lang.net.cn/schema.json",
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    }
  }
}
turbo run build

turbo 构建只会对 webdocs 工作区执行 build 脚本。 shared 包仍然将是任务图的一部分,但将被优雅地跳过。

Turborepo 的管道 API 设计和此文档页面受到 Microsoft 的 Lage 项目 (在新标签页中打开) 的启发。感谢 Kenneth Chau (在新标签页中打开) 为以如此简洁优雅的方式扩展任务的想法。