タスクの依存関係
Turborepo は、タスク同士の関係を表現することで最も強力になります。これらの関係を「依存関係」と呼びますが、これは package.json ファイルからインストールするパッケージの依存関係とは異なります。Turborepo はワークスペースを理解しますが、turbo.json で dependsOn 設定で明示的に表現しない限り、タスク間の関係を自動的に描画しません。
タスクを他のタスクに依存させる一般的なパターンについて見ていきましょう。
同じワークスペースから
他のタスクの前に実行する必要があるタスクがあるかもしれません。たとえば、build は deploy の前に実行する必要があるかもしれません。
両方のタスクが同じワークスペースにある場合は、次のように関係を指定できます。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
"build": {},
"deploy": {
// A workspace's `deploy` task depends on the `build` task of the same workspace.
"dependsOn": ["build"]
}
}
}これは、turbo deploy が実行されるたびに、同じワークスペース内で build も実行されることを意味します。
依存するワークスペースから
モノレポの一般的なパターンは、ワークスペースの build タスクは、その依存しているワークスペースすべての build タスクが完了した後にのみ実行するように宣言することです。
これは、ワークスペースの依存関係とタスクの依存関係の両方を指しているため、混乱を招く可能性があります。これらは異なる概念です。ワークスペースの依存関係は、package.jsonのdependenciesとdevDependenciesであり、タスクの依存関係はturbo.jsonのdependsOnキーです。
^記号(「キャレット」と呼ばれる)は、タスクが依存するワークスペース内のタスクに依存することを明示的に宣言します。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
"build": {
// A workspace's `build` command depends on its dependencies'
// and devDependencies' `build` commands being completed first
"dependsOn": ["^build"],
}
}
}上記の設定では、アプリが別のワークスペースからパッケージをインストールする場合、パッケージのbuildスクリプトは、常にアプリのbuildスクリプトより前に実行されます。
任意のワークスペースから
ワークスペースのタスクが別のワークスペースのタスクに依存するようにしたい場合があります。これは、lernaやrushから移行するリポジトリで特に役立ちます。これらのツールでは、タスクはデフォルトで別々のフェーズで実行されます。上記のように、これらの構成は、単純なpipeline構成では表現できない前提を設けている場合があります。または、CI/CDでturboを使用する際に、アプリケーションやマイクロサービス間のタスクの順序を表現したい場合もあります。
このような場合、pipeline構成で<workspace>#<task>構文を使用してこれらの関係を表現できます。以下の例では、backendのdeployスクリプトとhealth-checkスクリプト、およびuiワークスペースのtestスクリプトに依存するfrontendアプリケーションのdeployスクリプトについて説明しています。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
// Explicit workspace-task to workspace-task dependency
"frontend#deploy": {
"dependsOn": ["ui#test", "backend#deploy", "backend#health-check"]
}
}
}frontend#deployに対するこの明示的な構成は、testおよびdeployタスクの構成と矛盾するように見えるかもしれませんが、そうではありません。testとdeployは他のワークスペース(例:^<task>)に依存関係がないため、ワークスペースのbuildスクリプトとtestスクリプトが完了した後であれば、いつでも実行できます。
注記
- この
<workspace>#<task>構文は便利なエスケープハッチですが、一般的には、ビルド時の依存関係ではなく、ヘルスチェックなどのデプロイメントオーケストレーションタスクに使用することをお勧めします。これにより、Turborepoはこれらのタスクをより効率的に最適化できます。 - ワークスペースのタスクはキャッシュ構成を継承しません。現時点では
outputsを再宣言する必要があります。 <workspace>は、ワークスペースのpackage.jsonのnameキーと一致する必要があります。そうでない場合、タスクは無視されます。
依存関係なし
空の依存関係リスト(dependsOnが未定義または[])は、このタスクの前に何も実行する必要がないことを意味します。結局のところ、依存関係がないのですから。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
// A workspace's `lint` command has no dependencies and can be run any time.
"lint": {}
}
}タスク外の依存関係
2つのアプリ、docsとwebで使用している共通のuiパッケージがあるとしましょう。
apps/
docs/package.json # Depends on ui
web/package.json # Depends on ui
packages/
ui/package.json # No workspace dependencies
turbo.json
package.jsonワークスペースでTypeScriptを記述し、型をチェックするためにtscを実行する時が来ました。ここでは2つの要件があります。
- すべてが高速に実行されるように、すべての型チェックを並行して実行する:型チェックの結果は互いに依存しないため、すべてを並行して実行できます。
- 依存関係の変更はキャッシュミスになるはずである:
uiパッケージが変更された場合、docsまたはwebの型チェックタスクはキャッシュミスになることを知っておく必要があります。
これを実現するには、グラフ内に偽の再帰的なタスクを作成し、それに依存します。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
"topo": {
"dependsOn": ["^topo"]
},
"typecheck": {
"dependsOn": ["topo"]
}
}
}topoタスクはスクリプト内に存在しないため、Turborepoはタスクを「即座に」完了し、そのワークスペースに依存していたすべてのワークスペースを探します。このため、タスクはタスクグラフ内の他のワークスペースとの関係を理解しながら、並行して実行されます。
ここでのtopoという名前は特別な名前ではありません。「トポロジカル」の略であり、それが存在する理由を示すのに役立ちますが、このタスクは任意の名前を付けることができます。
なぜこれが機能するのですか?
なぜこれが機能するのかをより深く理解するために、要件をほぼ満たすパイプラインを見てみましょう。
以下のようにタスク定義からdependsOnを省略することで、タスクを並行して実行できます。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
"typecheck": {} // Uh oh, not quite!
}
}typecheckタスクは並行して正常に実行されますが、ワークスペースの依存関係については認識しません!
次の手順でこれを実証できます。
turbo typecheckを実行しますuiパッケージのソースコードを変更します。turbo typecheck --filter=webを実行します
もしこれを実行すると、ステップ3でキャッシュヒットしますが、それは誤りです! webワークスペースで、uiパッケージのコード変更によって型エラーが発生している可能性があります。ステップ3でのキャッシュヒットは誤ったもので、型エラーを隠蔽してしまいます。
この問題を解決するには、buildタスクと同様に、トポロジカル依存関係グラフに直接依存するように選択できます。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
"typecheck": {
"dependsOn": ["^typecheck"] // Uh oh, not quite!
}
}
}これで、正しいキャッシュ動作になります。uiのコードが変更された場合、webはキャッシュミスになります。これは素晴らしいことです。しかし、パイプラインを高速に実行していた並列処理が失われてしまいました。uiワークスペースのtypecheckタスクが完了するまで、webのタスクは開始できません。
uiのタスクで「即座に完了する」ものに依存し、依存するワークスペースでのtypecheckコマンドをより早く開始できるようにしたらどうでしょうか?
ここで「偽の」topoタスクが登場します。
{
"$schema": "https://turbo.dokyumento.jp/schema.json",
"pipeline": {
"topo": {
"dependsOn": ["^topo"]
},
"typecheck": {
"dependsOn": ["topo"]
}
}
}このパイプラインでは、topoという「合成」タスクを宣言します。package.jsonファイルにtopoスクリプトがないため、turbo typecheckパイプラインは、すべてのtypecheckスクリプトを並行して実行します。これにより、最初の要件が満たされます。
しかし、このtopoタスクは、webからuiへ、またdocsからuiへの「合成」ワークスペース-タスク依存関係も作成します。これは、uiのコードを変更すると、webとdocsのワークスペースでもキャッシュミスになることを意味し、2番目の要件を満たします。
パイプラインは、typecheckがtopoタスクに依存し、topoが^topoに依存すると宣言します。これは、同じワークスペースのtopoタスクが、すべてのtypecheckタスクよりも前に実行され、すべてのパッケージ依存関係のtopoタスクが、topoタスク自体よりも前に実行される必要があることを意味します。

なぜtypecheckが直接^topoに依存しないのか?と疑問に思うかもしれません。それは、ワークスペースが合成タスクを介してパッケージの依存関係を再帰的に結びつけるようにしたいからです。typecheckが^topoに依存する場合、turboは最初のレベルの依存関係の後にグラフへの追加を停止します。