From 7a92d0be042cfa33f7253272a6b29a7e2ccd4ad2 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Thu, 2 Jan 2025 14:10:43 -0700 Subject: [PATCH] docs(errors): improve error message for recursive calls (#9650) Closes #9224 ### Description This is a state that you can get into two ways: - Genuinely configuring their way into a loop with misconfigured scripts and tasks - Accidentally misconfiguring their package manager Workspace - In some misconfigurations, the Workspace will look like a single-package Workspace, but the previous iteration of our error message didn't give any hints about this. Either way, we should be providing an informative error message, so this PR provides a message that caters to both situations. We'll need a more verbose explanation, so an error link has been added here, as well. ### Testing Instructions Delete `pnpm-workspace.yaml` from `create-turbo`, remove `turbo` from `devDependencies`, re-run package manager installation, and try to run `turbo build`. You should be greeted by this new and improved error message. --------- Co-authored-by: Chris Olszewski --- .../src/task_graph/visitor/mod.rs | 17 ++++++-- .../messages/recursive-turbo-invocations.mdx | 40 +++++++++++++++++++ .../integration/tests/recursive-turbo.t | 10 +++-- .../integration/tests/run/missing-tasks.t | 4 ++ 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 docs/repo-docs/messages/recursive-turbo-invocations.mdx diff --git a/crates/turborepo-lib/src/task_graph/visitor/mod.rs b/crates/turborepo-lib/src/task_graph/visitor/mod.rs index 8bb69aad75f74..2797d693d3de6 100644 --- a/crates/turborepo-lib/src/task_graph/visitor/mod.rs +++ b/crates/turborepo-lib/src/task_graph/visitor/mod.rs @@ -2,7 +2,6 @@ mod command; mod error; mod exec; mod output; - use std::{ borrow::Cow, collections::HashSet, @@ -11,6 +10,7 @@ use std::{ }; use console::{Style, StyledObject}; +use convert_case::{Case, Casing}; use error::{TaskError, TaskWarning}; use exec::ExecContextFactory; use futures::{stream::FuturesUnordered, StreamExt}; @@ -23,6 +23,7 @@ use tracing::{debug, error, warn, Span}; use turbopath::{AbsoluteSystemPath, AnchoredSystemPath}; use turborepo_ci::{Vendor, VendorBehavior}; use turborepo_env::{platform::PlatformEnv, EnvironmentVariableMap}; +use turborepo_errors::TURBO_SITE; use turborepo_repository::package_graph::{PackageGraph, PackageName, ROOT_PKG_NAME}; use turborepo_telemetry::events::{ generic::GenericEventBuilder, task::PackageTaskEventBuilder, EventBuilder, TrackedErrors, @@ -77,12 +78,21 @@ pub enum Error { task_id: TaskId<'static>, }, #[error( - "root task {task_name} ({command}) looks like it invokes turbo and might cause a loop" + "Your `package.json` script looks like it invokes a Root Task ({task_name}), creating a \ + loop of `turbo` invocations. You likely have misconfigured your scripts and tasks or \ + your package manager's Workspace structure." + )] + #[diagnostic( + code(recursive_turbo_invocations), + url( + "{}/messages/{}", + TURBO_SITE, self.code().unwrap().to_string().to_case(Case::Kebab) + ) )] RecursiveTurbo { task_name: String, command: String, - #[label("task found here")] + #[label("This script calls `turbo`, which calls the script, which calls `turbo`...")] span: Option, #[source_code] text: NamedSource, @@ -215,6 +225,7 @@ impl<'a> Visitor<'a> { Some(cmd) if info.package() == ROOT_PKG_NAME && turbo_regex().is_match(cmd) => { package_task_event.track_error(TrackedErrors::RecursiveError); let (span, text) = cmd.span_and_text("package.json"); + return Err(Error::RecursiveTurbo { task_name: info.to_string(), command: cmd.to_string(), diff --git a/docs/repo-docs/messages/recursive-turbo-invocations.mdx b/docs/repo-docs/messages/recursive-turbo-invocations.mdx new file mode 100644 index 0000000000000..0510e824db6c3 --- /dev/null +++ b/docs/repo-docs/messages/recursive-turbo-invocations.mdx @@ -0,0 +1,40 @@ +--- +title: Recursive `turbo` invocations +description: Learn more about errors with recursive sccripts and tasks in Turborepo. +--- + +## Why this error occurred + +When a cycle of `turbo` invocations is detected in a [single-package workspace](https://turbo.build/repo/docs/guides/single-package-workspaces), Turborepo will error to prevent the recursive calls to itself. Typically, this situation occurs for one of two reasons: + +### Recursion in scripts and tasks + +In a single-package workspace, a script in `package.json` that calls a Turborepo task with the same name causes a loop. + +```json title="./package.json" +{ + "scripts": { + "build": "turbo run build" + } +} +``` + +Calling the `build` script calls `turbo run build`. `turbo run build` then calls the `build` script, initiating the loop of recursive calls. + +To resolve this, ensure that the name of the script in `package.json` is not the same as the Turborepo task. For example, to fix the snippet above, renaming the script would break the cycle: + +```json title="./package.json" +{ + "scripts": { + "build:app": "turbo run build" + } +} +``` + +### Package manager Workspace misconfiguration + +A misconfigured workspace can make it appear that a [multi-package workspace](https://vercel.com/docs/vercel-platform/glossary#multi-package-workspace) is a single-package workspace. This causes Turborepo to infer that the repository is of the wrong type, causing it to see the script in `package.json` to be recursive. + +Your repo can end up in this state in a few ways, with the most common being that the [packages are not defined according to your package manager](https://turbo.build/repo/docs/crafting-your-repository/structuring-a-repository#specifying-packages-in-a-monorepo). An npm workspace that is missing the `workspaces` field in `package.json` or a pnpm workspace that is missing a `pnpm-workspace.yaml` file can result in this error message. + +Check that your repository is complying with standards for multi-package workspaces and correct any issues. diff --git a/turborepo-tests/integration/tests/recursive-turbo.t b/turborepo-tests/integration/tests/recursive-turbo.t index 910276dffda71..965472c7a3de0 100644 --- a/turborepo-tests/integration/tests/recursive-turbo.t +++ b/turborepo-tests/integration/tests/recursive-turbo.t @@ -6,13 +6,17 @@ sed replaces the square brackets with parentheses so prysk can parse the file pa \xe2\x80\xa2 Packages in scope: //, another, my-app, util (esc) \xe2\x80\xa2 Running something in 4 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - x root task //#something (turbo run build) looks like it invokes turbo and - | might cause a loop + recursive_turbo_invocations (https://turbo.build/messages/recursive-turbo-invocations) + + x Your `package.json` script looks like it invokes a Root Task (// + | #something), creating a loop of `turbo` invocations. You likely have + | misconfigured your scripts and tasks or your package manager's Workspace + | structure. ,-\(.*package.json:3:1\) (re) 3 | "scripts": { 4 | "something": "turbo run build" : ^^^^^^^^|^^^^^^^^ - : `-- task found here + : `-- This script calls `turbo`, which calls the script, which calls `turbo`... 5 | }, `---- diff --git a/turborepo-tests/integration/tests/run/missing-tasks.t b/turborepo-tests/integration/tests/run/missing-tasks.t index 60b61977b43dc..7222666eb77a8 100644 --- a/turborepo-tests/integration/tests/run/missing-tasks.t +++ b/turborepo-tests/integration/tests/run/missing-tasks.t @@ -30,11 +30,15 @@ Setup $ ${TURBO} run something --dry > OUTPUT 2>&1 [1] $ grep --quiet -E "root task (//#)?something \(turbo run build\) looks like it invokes turbo and" OUTPUT + [1] $ grep --quiet -E "might cause a loop" OUTPUT + [1] # Bad command $ ${TURBO} run something > OUTPUT2 2>&1 [1] $ grep --quiet -E "root task (//#)?something \(turbo run build\) looks like it invokes turbo and" OUTPUT + [1] $ grep --quiet -E "might cause a loop" OUTPUT + [1]