diff --git a/crates/cli/src/commands/check.rs b/crates/cli/src/commands/check.rs index 72ad1152600..171044356e2 100644 --- a/crates/cli/src/commands/check.rs +++ b/crates/cli/src/commands/check.rs @@ -4,6 +4,7 @@ use clap::Args; use moon::generate_project_graph; use moon_common::Id; use moon_project::Project; +use moon_target::TargetLocator; use moon_workspace::Workspace; use starbase::system; use std::env; @@ -67,7 +68,7 @@ pub async fn check( for project in projects { for task in project.get_tasks()? { if task.is_build_type() || task.is_test_type() { - targets.push(task.target.id.clone()); + targets.push(TargetLocator::Target(task.target.clone())); } } } diff --git a/crates/cli/src/commands/graph/dep.rs b/crates/cli/src/commands/graph/dep.rs index 78c82c44fdd..7d1921c0bbd 100644 --- a/crates/cli/src/commands/graph/dep.rs +++ b/crates/cli/src/commands/graph/dep.rs @@ -2,14 +2,14 @@ use crate::commands::graph::utils::{dep_graph_repr, respond_to_request, setup_se use clap::Args; use miette::IntoDiagnostic; use moon::{build_dep_graph, generate_project_graph}; -use moon_target::Target; +use moon_target::TargetLocator; use moon_workspace::Workspace; use starbase::system; #[derive(Args, Clone, Debug)] pub struct DepGraphArgs { #[arg(help = "Target to *only* graph")] - target: Option, + target: Option, #[arg(long, help = "Print the graph in DOT format")] dot: bool, @@ -24,11 +24,10 @@ pub async fn dep_graph(args: ArgsRef, workspace: ResourceMut, + #[arg(required = true, help = "List of targets to run")] + pub targets: Vec, #[arg( long, @@ -103,7 +104,7 @@ pub fn is_local(args: &RunArgs) -> bool { } pub async fn run_target( - target_ids: &[String], + target_locators: &[TargetLocator], args: &RunArgs, concurrency: Option, workspace: &Workspace, @@ -142,8 +143,8 @@ pub async fn run_target( } // Run targets, optionally based on affected files - let primary_targets = dep_builder.run_targets_by_id( - target_ids, + let primary_targets = dep_builder.run_targets_by_locator( + target_locators, if should_run_affected { Some(&touched_files) } else { @@ -152,7 +153,7 @@ pub async fn run_target( )?; if primary_targets.is_empty() { - let targets_list = map_list(target_ids, |id| color::label(id)); + let targets_list = map_list(target_locators, |id| color::label(id)); if should_run_affected { let status_list = if args.status.is_empty() { @@ -192,7 +193,7 @@ pub async fn run_target( // Process all tasks in the graph let context = ActionContext { affected_only: should_run_affected, - initial_targets: FxHashSet::from_iter(target_ids.to_owned()), + initial_targets: FxHashSet::from_iter(target_locators.to_owned()), interactive: args.interactive, passthrough_args: args.passthrough.to_owned(), primary_targets: FxHashSet::from_iter(primary_targets), diff --git a/crates/core/action-context/src/lib.rs b/crates/core/action-context/src/lib.rs index e4f76c2e635..6e54e544080 100644 --- a/crates/core/action-context/src/lib.rs +++ b/crates/core/action-context/src/lib.rs @@ -1,6 +1,6 @@ use clap::ValueEnum; use moon_common::path::WorkspaceRelativePathBuf; -use moon_target::Target; +use moon_target::{Target, TargetLocator}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -31,7 +31,7 @@ impl TargetState { pub struct ActionContext { pub affected_only: bool, - pub initial_targets: FxHashSet, + pub initial_targets: FxHashSet, pub interactive: bool, diff --git a/crates/core/dep-graph/src/dep_builder.rs b/crates/core/dep-graph/src/dep_builder.rs index 384a2dd072f..0770f050740 100644 --- a/crates/core/dep-graph/src/dep_builder.rs +++ b/crates/core/dep-graph/src/dep_builder.rs @@ -7,7 +7,7 @@ use moon_platform::{PlatformManager, Runtime}; use moon_project::Project; use moon_project_graph::ProjectGraph; use moon_query::{build_query, Criteria}; -use moon_target::{Target, TargetError, TargetScope}; +use moon_target::{Target, TargetError, TargetLocator, TargetScope}; use moon_task::Task; use petgraph::graph::NodeIndex; use petgraph::Graph; @@ -386,35 +386,31 @@ impl<'ws> DepGraphBuilder<'ws> { Ok(indexes) } - pub fn run_targets_by_id( + pub fn run_targets_by_locator( &mut self, - target_ids: &[String], + target_locators: &[TargetLocator], touched_files: Option<&TouchedFilePaths>, ) -> miette::Result> { let mut qualified_targets = vec![]; - let mut project_targets = vec![]; - - for target_id in target_ids { - // Target (with possible scope) provided - if target_id.contains(':') { - qualified_targets - .extend(self.run_target(Target::parse(target_id)?, touched_files)?.0); - // Task name provided, find closest project - } else { - project_targets.push(target_id); - } - } + let mut project = None; + let cwd = std::env::current_dir().expect("Could not determine current working directory!"); + + for locator in target_locators { + let result = match locator { + TargetLocator::Target(target) => self.run_target(target, touched_files)?, + TargetLocator::Path(task_id) => { + if project.is_none() { + project = Some(self.project_graph.get_from_path(&cwd)?); + } - if !project_targets.is_empty() { - let cwd = std::env::current_dir().unwrap(); - let project = self.project_graph.get_from_path(&cwd)?; + self.run_target( + Target::new(&project.as_ref().unwrap().id, task_id)?, + touched_files, + )? + } + }; - for target_id in project_targets { - qualified_targets.extend( - self.run_target(Target::new(&project.id, target_id)?, touched_files)? - .0, - ); - } + qualified_targets.extend(result.0); } Ok(qualified_targets) diff --git a/nextgen/target/src/target_locator.rs b/nextgen/target/src/target_locator.rs index e69de29bb2d..9e7fd84d4a6 100644 --- a/nextgen/target/src/target_locator.rs +++ b/nextgen/target/src/target_locator.rs @@ -0,0 +1,63 @@ +use crate::target::Target; +use moon_common::Id; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use std::str::FromStr; + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum TargetLocator { + Target(Target), + Path(Id), +} + +impl TargetLocator { + pub fn as_str(&self) -> &str { + self.as_ref() + } +} + +impl AsRef for TargetLocator { + fn as_ref(&self) -> &TargetLocator { + self + } +} + +impl AsRef for TargetLocator { + fn as_ref(&self) -> &str { + match self { + Self::Target(target) => target.as_str(), + Self::Path(id) => id.as_str(), + } + } +} + +impl FromStr for TargetLocator { + type Err = miette::Report; + + fn from_str(value: &str) -> Result { + Ok(if value.contains(':') { + TargetLocator::Target(Target::parse(&value)?) + } else { + TargetLocator::Path(Id::new(&value)?) + }) + } +} + +impl<'de> Deserialize<'de> for TargetLocator { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + + TargetLocator::from_str(&value).map_err(de::Error::custom) + } +} + +impl Serialize for TargetLocator { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.as_str()) + } +}