diff --git a/crates/turborepo-lib/src/engine/mod.rs b/crates/turborepo-lib/src/engine/mod.rs index 645c9a9b2b1d6..f87f669ab4375 100644 --- a/crates/turborepo-lib/src/engine/mod.rs +++ b/crates/turborepo-lib/src/engine/mod.rs @@ -421,6 +421,7 @@ impl Engine { package_graph: &PackageGraph, concurrency: u32, ui_mode: UIMode, + will_execute_tasks: bool, ) -> Result<(), Vec> { // TODO(olszewski) once this is hooked up to a real run, we should // see if using rayon to parallelize would provide a speedup @@ -510,14 +511,16 @@ impl Engine { // there must always be at least one concurrency 'slot' available for // non-persistent tasks otherwise we get race conditions - if persistent_count >= concurrency { + if will_execute_tasks && persistent_count >= concurrency { validation_errors.push(ValidateError::PersistentTasksExceedConcurrency { persistent_count, concurrency, }) } - validation_errors.extend(self.validate_interactive(ui_mode)); + if will_execute_tasks { + validation_errors.extend(self.validate_interactive(ui_mode)); + } validation_errors.sort(); @@ -706,20 +709,91 @@ mod test { // if our limit is less than, it should fail engine - .validate(&graph, 1, UIMode::Stream) + .validate(&graph, 1, UIMode::Stream, true) .expect_err("not enough"); // if our limit is less than, it should fail engine - .validate(&graph, 2, UIMode::Stream) + .validate(&graph, 2, UIMode::Stream, true) .expect_err("not enough"); // we have two persistent tasks, and a slot for all other tasks, so this should // pass - engine.validate(&graph, 3, UIMode::Stream).expect("ok"); + engine + .validate(&graph, 3, UIMode::Stream, true) + .expect("ok"); // if our limit is greater, then it should pass - engine.validate(&graph, 4, UIMode::Stream).expect("ok"); + engine + .validate(&graph, 4, UIMode::Stream, true) + .expect("ok"); + } + + #[tokio::test] + async fn test_interactive_validation() { + let tmp = tempfile::TempDir::new().unwrap(); + + let mut engine = Engine::new(); + + // add two packages with a persistent build task + for package in ["a", "b"] { + let task_id = TaskId::new(package, "build"); + engine.get_index(&task_id); + engine.add_definition( + task_id, + TaskDefinition { + persistent: true, + interactive: true, + ..Default::default() + }, + ); + } + + let engine = engine.seal(); + + let graph_builder = PackageGraph::builder( + AbsoluteSystemPath::from_std_path(tmp.path()).unwrap(), + PackageJson::default(), + ) + .with_package_discovery(DummyDiscovery(&tmp)); + + let graph = graph_builder.build().await.unwrap(); + + assert!(engine.validate(&graph, 3, UIMode::Stream, false).is_ok()); + assert!(engine.validate(&graph, 3, UIMode::Stream, true).is_err()); + } + + #[tokio::test] + async fn test_dry_run_skips_concurrency_validation() { + let tmp = tempfile::TempDir::new().unwrap(); + + let mut engine = Engine::new(); + + // add two packages with a persistent build task + for package in ["a", "b"] { + let task_id = TaskId::new(package, "build"); + engine.get_index(&task_id); + engine.add_definition( + task_id, + TaskDefinition { + persistent: true, + ..Default::default() + }, + ); + } + + let engine = engine.seal(); + + let graph_builder = PackageGraph::builder( + AbsoluteSystemPath::from_std_path(tmp.path()).unwrap(), + PackageJson::default(), + ) + .with_package_discovery(DummyDiscovery(&tmp)); + + let graph = graph_builder.build().await.unwrap(); + + assert!(engine.validate(&graph, 1, UIMode::Stream, false).is_ok()); + assert!(engine.validate(&graph, 1, UIMode::Stream, true).is_err()); } #[tokio::test] diff --git a/crates/turborepo-lib/src/run/builder.rs b/crates/turborepo-lib/src/run/builder.rs index 08bf7309780fc..f41edb772eedf 100644 --- a/crates/turborepo-lib/src/run/builder.rs +++ b/crates/turborepo-lib/src/run/builder.rs @@ -138,6 +138,10 @@ impl RunBuilder { }); } + fn will_execute_tasks(&self) -> bool { + self.opts.run_opts.dry_run.is_none() && self.opts.run_opts.graph.is_none() + } + pub fn with_analytics_sender(mut self, analytics_sender: Option) -> Self { self.analytics_sender = analytics_sender; self @@ -464,9 +468,9 @@ impl RunBuilder { self.opts.run_opts.dry_run.is_some(), )); - let should_print_prelude = self.should_print_prelude_override.unwrap_or_else(|| { - self.opts.run_opts.dry_run.is_none() && self.opts.run_opts.graph.is_none() - }); + let should_print_prelude = self + .should_print_prelude_override + .unwrap_or_else(|| self.will_execute_tasks()); Ok(Run { version: self.version, @@ -538,6 +542,7 @@ impl RunBuilder { pkg_dep_graph, self.opts.run_opts.concurrency, self.opts.run_opts.ui_mode, + self.will_execute_tasks(), ) .map_err(Error::EngineValidation)?; }