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'
Let's walk through some common patterns you'll want to get to know before diving in to turbo.json
.
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": {}
}
}
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.
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/**"]
}
}
}
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.
- 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 - Package-tasks do not inherit cache configuration. You must redeclare
outputs
at the moment. <workspace>
must match thename
key in the workspace'spackage.json
or the task will be ignored.
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:
- All tests run in parallel to keep things speedy
- 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.