Skip to content

Latest commit

 

History

History
174 lines (141 loc) · 5.84 KB

task-dependencies.mdx

File metadata and controls

174 lines (141 loc) · 5.84 KB
title description
Task Dependencies
Turborepo helps you specify task dependencies declaratively.

import Callout from "../../../../../components/Callout"; import HeartIcon from "@heroicons/react/solid/HeartIcon"; import { Tabs, Tab } from '../../../../../components/Tabs'

Task Dependencies

Let's walk through some common patterns you'll want to get to know before diving in to turbo.json.

No dependencies

An empty dependency list (dependsOn is either undefined or []) means that nothing needs to run before this task! After all, it has no dependencies.

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    // A workspace's `lint` command has no dependencies and can be run any time.
    "lint": {}
  }
}

In the same workspace

There might be tasks which need to run before other tasks. For instance, build might need to be run before deploy.

If both tasks are in the same workspace, you can specify the relationship like this:

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {},
    "deploy": {
      // A workspace's `deploy` task depends on the `build` task of the same workspace.
      "dependsOn": ["build"]
    }
  }
}

This means that whenever turbo run deploy is run, build will also be run inside the same workspace.

In a different workspace

A common pattern in monorepos is to declare that a workspace's build task should only run once the build tasks of all the workspaces it depends on are complete.

The ^ symbol explicitly declares that the task has a dependency on a task in a workspace it depends on.

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      // "A workspace's `build` command depends on its dependencies'
      // and devDependencies' `build` commands being completed first"
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    }
  }
}

Specific Tasks from a Workspace

Sometimes, you may want to create a workspace-task dependency on another workspace-task. This can be especially helpful for repos migrating from lerna or rush, where tasks are run in separate phases by default. Sometimes these configurations make assumptions that cannot be expressed in a simple pipeline configuration, as seen above. Or you may just want to express sequences of tasks between applications or microservices when using turbo in CI/CD.

For these cases, you can express these relationships in your pipeline configuration using the <workspace>#<task> syntax. The example below describes the deploy script of a frontend application that depends on the deploy and health-check scripts of backend, as well as the test script of a ui workspace:

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    // Explicit workspace-task to workspace-task dependency
    "frontend#deploy": {
      "dependsOn": ["ui#test", "backend#deploy", "backend#health-check"]
    }
  }
}

This explicit configuration for frontend#deploy may seem to conflict with the test and deploy task configurations, but it does not. Since test and deploy do not have dependencies on other workspaces (e.g. ^<task>), they can execute any time after their workspace's build and test scripts have finished.

Notes:
  1. Although this <workspace>#<task> syntax is a useful escape hatch, we generally recommend using it for deployment orchestration tasks such as health checks, rather than build-time dependencies, so that Turborepo can optimize these tasks more efficiently
  2. Package-tasks do not inherit cache configuration. You must redeclare outputs at the moment.
  3. <workspace> must match the name key in the workspace's package.json or the task will be ignored.

Dependencies outside of a task

When your task has topological dependencies that are outside of that given task, you'll still want to enjoy the parallelism of Turborepo and ensure that your caching behavior is correct according to your code changes.

To demonstrate how to do this, let's say you have a set of workspaces to do a little bit of math: add, subtract, and multiply. subtract is implemented by calling add with a negative number and your multiply works by calling add in a loop. So, add is a dependency of both subtract and multiply.

You've written tests in all three of these workspaces and it's time to run them. There are two requirements here:

  1. All tests run in parallel to keep things speedy
  2. A change in a dependency should result in a cache miss

To accomplish this, we can set up a pipeline like so:

{
   "$schema": "https://turbo.build/schema.json",
   "pipeline": {
     "topo": {
       "dependsOn": ["^topo"]
     },
     "test": {
       "dependsOn": ["^topo"]
     }
   }
 }

In this pipeline, we create an intermediary placeholder topo task. Since we don't have a topo command in our workspaces, the pipeline will go straight to running test scripts in parallel, meeting our first requirement. The second requirement will also be taken care of, falling back on Turborepo's default behavior of creating hashes for a workspace task and it's dependencies as a tree.

<Callout type="idea" icon={}

Turborepo's Pipeline API design and this page of documentation was inspired by Microsoft's Lage project. Shoutout to Kenneth Chau for the idea of fanning out tasks in such a concise and elegant way.