任務相依性
當您表達任務之間的關聯性時,Turborepo 最為強大。我們將這些關係稱為「依賴關係」,但它們與您從 package.json
檔案安裝的套件依賴關係不同。雖然 Turborepo 確實了解您的工作區,但它不會自動繪製它們的任務之間的任何關係,除非您在 turbo.json
中透過 dependsOn
設定表達它們。
讓我們逐步了解一些常見模式,說明如何讓任務依賴於其他任務。
來自同一個工作區
可能有些任務需要在其他任務之前執行。例如,build
可能需要在 deploy
之前執行。
如果兩個任務都在同一個工作區中,您可以這樣指定關係
{
"$schema": "https://turbo.dev.org.tw/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
任務完成後執行。
這可能會令人困惑,因為它同時指的是工作區依賴關係和任務依賴關係,而這兩個概念不同。工作區依賴關係是 dependencies
和 devDependencies
在 package.json
中,而任務依賴關係是 dependsOn
鍵在 turbo.json
中。
符號 ^
(稱為「插入符號」)明確宣告任務取決於工作區中它所依賴的任務。
{
"$schema": "https://turbo.dev.org.tw/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
時,表達應用程式或微服務之間的任務順序。
在這些情況下,您可以使用 <workspace>#<task>
語法在 pipeline
設定中表達這些關係。以下範例說明取決於 backend
的 deploy
和 health-check
指令碼,以及 ui
工作區的 test
指令碼的 frontend
應用程式的 deploy
指令碼:
{
"$schema": "https://turbo.dev.org.tw/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>
必須與工作區的name
鍵相符,否則任務將會被忽略。
沒有依賴關係
空的依賴關係清單(dependsOn
未定義或為 []
)表示在這個任務之前不需要執行任何動作!畢竟,它沒有依賴關係。
{
"$schema": "https://turbo.dev.org.tw/schema.json",
"pipeline": {
// A workspace's `lint` command has no dependencies and can be run any time.
"lint": {}
}
}
任務外部的依賴關係
假設您有一個共用的 ui
套件,您在兩個應用程式 docs
和 web
中使用它。
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
來檢查您的類型。這裡有兩個需求:
- 所有類型檢查並行執行,以保持速度:因為類型檢查的結果彼此無關,所以您可以並行執行所有檢查。
- 依賴關係中的變更應導致快取遺漏:如果
ui
套件變更,docs
或web
中的類型檢查任務應知道遺漏快取。
為達成此目的,您將在圖形中建立一個假的遞迴任務並依賴它。
{
"$schema": "https://turbo.dev.org.tw/schema.json",
"pipeline": {
"topo": {
"dependsOn": ["^topo"]
},
"typecheck": {
"dependsOn": ["topo"]
}
}
}
由於 topo
任務在您的腳本中不存在,Turborepo 將「立即」完成任務,然後查看依賴於該工作區的任何工作區。因此,您的任務將並行執行,同時仍了解它們與任務圖形中其他工作區的關係。
這裡的 topo
名稱並非特殊名稱。它是「拓撲」的簡稱,有助於說明其存在的目的,但您可以將此任務命名為任何您想要的內容。
為什麼這麼做有效?
我們可以透過檢視幾乎符合我們需求的管線,更深入了解為什麼這麼做有效。
您可以透過從任務定義中省略 dependsOn
來達成任務的平行處理,如下所示
{
"$schema": "https://turbo.dev.org.tw/schema.json",
"pipeline": {
"typecheck": {} // Uh oh, not quite!
}
}
您的 typecheck
任務將會成功平行執行 - 但它們不會知道其工作區的相依性!
我們可以使用以下步驟來示範
- 執行
turbo typecheck
- 變更
ui
套件中的一些原始碼 - 執行
turbo typecheck --filter=web
如果您執行此操作,您將會在步驟 3 命中快取 - 但您不應該這麼做!您可能在 web
工作區中建立一個類型錯誤,而這個錯誤來自於 ui
套件程式碼中的變更。步驟 3 中的快取命中將會是不正確的,並會向您隱藏類型錯誤。
若要解決此問題,您可以選擇直接依賴您的拓撲相依圖,就像您對 build
任務所做的一樣
{
"$schema": "https://turbo.dev.org.tw/schema.json",
"pipeline": {
"typecheck": {
"dependsOn": ["^typecheck"] // Uh oh, not quite!
}
}
}
現在您擁有正確的快取行為:當 ui
程式碼變更時,web
將會錯失快取。這很棒 - 但我們剛剛失去了讓我們的管線執行得如此快速的平行處理。在 ui
工作區中的 typecheck
任務現在必須在 web
中的任務開始之前結束。
如果我們可以依賴於在 ui
中「立即完成」的任務,以便在依賴的工作空間中盡快啟動 typecheck
指令,那會如何?
這正是「假的」topo
任務發揮作用的地方
{
"$schema": "https://turbo.dev.org.tw/schema.json",
"pipeline": {
"topo": {
"dependsOn": ["^topo"]
},
"typecheck": {
"dependsOn": ["topo"]
}
}
}
在此管線中,我們宣告一個稱為 topo
的「合成」任務。由於我們在任何 package.json
檔案中都沒有 topo
指令碼,turbo typecheck
管線將直接執行所有 typecheck
指令碼,以符合我們的首要需求。
但這個 topo
任務也建立了一個「合成」工作空間任務相依性,從 web
到 ui
,以及從 docs
到 ui
。這表示當你在 ui
中變更程式碼時,你也會在 web
和 docs
中的工作空間取得快取遺漏,符合第二個需求。
管線宣告 typecheck
相依於 topo
任務,而 topo
相依於 ^topo
。以英文來說,這表示 相同 工作空間的 topo
任務必須在所有 typecheck
任務之前執行,而所有 套件相依性 的 topo
任務必須在 topo
任務本身之前執行。
你可能會問,為什麼 typecheck
沒有直接相依於 ^topo
?因為我們希望我們的 Workspace 能透過合成任務以 遞迴 方式連接套件相依性。如果