Skip to content

Commit

Permalink
new: Support glob based targets. (#1776)
Browse files Browse the repository at this point in the history
* Update locator.

* Update graph.

* Update ci.

* Add locator tests.

* Add tests.
  • Loading branch information
milesj authored Jan 4, 2025
1 parent 57829fc commit 8863f42
Show file tree
Hide file tree
Showing 9 changed files with 467 additions and 32 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
- Deprecated the top-level `platform` setting from `moon.yml`, use `toolchain.default` instead.
- Additionally, the toolchain can now be inferred from the top-level `language` setting and any
config files in the project/workspace root. This pattern is preferred when possible.
- Added the ability to run targets in `moon run` and `moon ci` using a glob-like syntax.
- For example: `:build-*`, `app-*:build`, `#tag-{foo,bar}:build`, etc.
- Updated task option `runInCI` to support the values "always" (always run) and "affected" (only run
if affected, same as `true`).
- Updated the `extends` setting in `.moon/workspace.yml`, `toolchain.yml`, and `tasks.yml`, to
Expand Down
77 changes: 70 additions & 7 deletions crates/action-graph/src/action_graph_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,13 +540,76 @@ impl<'app> ActionGraphBuilder<'app> {

// Track the qualified as an initial target
for locator in reqs.target_locators.clone() {
initial_targets.push(match locator {
TargetLocator::Qualified(target) => target,
TargetLocator::TaskFromWorkingDir(task_id) => Target::new(
&self.workspace_graph.get_project_from_path(None)?.id,
task_id,
)?,
});
match locator {
TargetLocator::GlobMatch {
project_glob,
task_glob,
scope,
..
} => {
let mut is_all = false;
let mut do_query = false;
let mut projects = vec![];

// Query for all applicable projects first since we can't
// query projects + tasks at the same time
if let Some(glob) = project_glob {
let query = if let Some(tag_glob) = glob.strip_prefix('#') {
format!("tag~{tag_glob}")
} else {
format!("project~{glob}")
};

projects = self.workspace_graph.query_projects(build_query(&query)?)?;
do_query = !projects.is_empty();
} else {
match scope {
Some(TargetScope::All) => {
is_all = true;
do_query = true;
}
_ => {
// Don't query for the other scopes,
// since they're not valid from the run context
}
};
}

// Then query for all tasks within the queried projects
if do_query {
let mut query = format!("task~{task_glob}");

if !is_all {
query = format!(
"project=[{}] && {query}",
projects
.into_iter()
.map(|project| project.id.to_string())
.collect::<Vec<_>>()
.join(",")
);
}

let tasks = self.workspace_graph.query_tasks(build_query(&query)?)?;

initial_targets.extend(
tasks
.into_iter()
.map(|task| task.target.clone())
.collect::<Vec<_>>(),
);
}
}
TargetLocator::Qualified(target) => {
initial_targets.push(target);
}
TargetLocator::TaskFromWorkingDir(task_id) => {
initial_targets.push(Target::new(
&self.workspace_graph.get_project_from_path(None)?.id,
task_id,
)?);
}
};
}

// Determine affected tasks before building
Expand Down
83 changes: 83 additions & 0 deletions crates/action-graph/tests/action_graph_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,89 @@ mod action_graph {
assert_snapshot!(graph.to_dot());
}

#[tokio::test]
async fn runs_by_task_glob() {
let sandbox = create_sandbox("tasks");
let container = ActionGraphContainer::new(sandbox.path()).await;
let mut builder = container.create_builder();

builder
.run_from_requirements(RunRequirements {
target_locators: FxHashSet::from_iter([
TargetLocator::parse(":*-dependency").unwrap(),
TargetLocator::parse(":{a,c}").unwrap(),
]),
..Default::default()
})
.unwrap();

let graph = builder.build();

assert_snapshot!(graph.to_dot());
}

#[tokio::test]
async fn runs_by_tag_glob() {
let sandbox = create_sandbox("tasks");
let container = ActionGraphContainer::new(sandbox.path()).await;
let mut builder = container.create_builder();

builder
.run_from_requirements(RunRequirements {
target_locators: FxHashSet::from_iter([
TargetLocator::parse("#front*:build").unwrap()
]),
..Default::default()
})
.unwrap();

let graph = builder.build();

assert_snapshot!(graph.to_dot());
}

#[tokio::test]
async fn runs_by_project_glob() {
let sandbox = create_sandbox("tasks");
let container = ActionGraphContainer::new(sandbox.path()).await;
let mut builder = container.create_builder();

builder
.run_from_requirements(RunRequirements {
target_locators: FxHashSet::from_iter([TargetLocator::parse(
"c{lient,ommon}:test",
)
.unwrap()]),
..Default::default()
})
.unwrap();

let graph = builder.build();

assert_snapshot!(graph.to_dot());
}

#[tokio::test]
async fn returns_empty_result_for_no_glob_match() {
let sandbox = create_sandbox("tasks");
let container = ActionGraphContainer::new(sandbox.path()).await;
let mut builder = container.create_builder();

builder
.run_from_requirements(RunRequirements {
target_locators: FxHashSet::from_iter([TargetLocator::parse(
"{foo,bar}:task-*",
)
.unwrap()]),
..Default::default()
})
.unwrap();

let graph = builder.build();

assert!(graph.is_empty());
}

#[tokio::test]
async fn computes_context() {
let sandbox = create_sandbox("tasks");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: crates/action-graph/tests/action_graph_test.rs
expression: graph.to_dot()
---
digraph {
0 [ label="SyncWorkspace" ]
1 [ label="SetupToolchain(system)" ]
2 [ label="SyncProject(system, client)" ]
3 [ label="SyncProject(system, server)" ]
4 [ label="SyncProject(system, common)" ]
5 [ label="SyncProject(system, base)" ]
6 [ label="RunTask(client:test)" ]
1 -> 0 [ ]
3 -> 1 [ ]
5 -> 1 [ ]
4 -> 1 [ ]
4 -> 5 [ ]
2 -> 1 [ ]
2 -> 3 [ ]
2 -> 4 [ ]
6 -> 2 [ ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
source: crates/action-graph/tests/action_graph_test.rs
expression: graph.to_dot()
---
digraph {
0 [ label="SyncWorkspace" ]
1 [ label="SetupToolchain(system)" ]
2 [ label="SyncProject(system, client)" ]
3 [ label="SyncProject(system, server)" ]
4 [ label="SyncProject(system, common)" ]
5 [ label="SyncProject(system, base)" ]
6 [ label="RunTask(client:build)" ]
7 [ label="RunTask(server:build)" ]
8 [ label="RunTask(common:build)" ]
1 -> 0 [ ]
3 -> 1 [ ]
5 -> 1 [ ]
4 -> 1 [ ]
4 -> 5 [ ]
2 -> 1 [ ]
2 -> 3 [ ]
2 -> 4 [ ]
7 -> 3 [ ]
8 -> 4 [ ]
6 -> 2 [ ]
6 -> 7 [ ]
6 -> 8 [ ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
source: crates/action-graph/tests/action_graph_test.rs
expression: graph.to_dot()
---
digraph {
0 [ label="SyncWorkspace" ]
1 [ label="SetupToolchain(system)" ]
2 [ label="SyncProject(system, ci)" ]
3 [ label="RunTask(ci:ci2-dependency)" ]
4 [ label="RunTask(ci:ci3-dependency)" ]
5 [ label="RunTask(ci:ci4-dependency)" ]
6 [ label="SyncProject(system, deps-affected)" ]
7 [ label="RunTask(deps-affected:a)" ]
8 [ label="RunTask(deps-affected:b)" ]
9 [ label="RunTask(deps-affected:c)" ]
10 [ label="SyncProject(system, deps)" ]
11 [ label="RunTask(deps:a)" ]
12 [ label="RunTask(deps:c)" ]
1 -> 0 [ ]
2 -> 1 [ ]
3 -> 2 [ ]
4 -> 2 [ ]
5 -> 2 [ ]
6 -> 1 [ ]
9 -> 6 [ ]
8 -> 6 [ ]
8 -> 9 [ ]
7 -> 6 [ ]
7 -> 8 [ ]
10 -> 1 [ ]
11 -> 10 [ ]
12 -> 10 [ ]
}
30 changes: 12 additions & 18 deletions crates/app/src/commands/ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ use moon_action_graph::{ActionGraph, RunRequirements};
use moon_affected::{DownstreamScope, UpstreamScope};
use moon_common::path::WorkspaceRelativePathBuf;
use moon_console::Console;
use moon_task::{Target, TargetLocator};
use moon_task::TargetLocator;
use moon_workspace_graph::WorkspaceGraph;
use rustc_hash::FxHashSet;
use starbase::AppResult;
use starbase_styles::color;
use std::sync::Arc;
use tracing::instrument;

type TargetList = Vec<Target>;
type TargetList = Vec<TargetLocator>;

const HEADING_PARALLELISM: &str = "Parallelism and distribution";

#[derive(Args, Clone, Debug)]
pub struct CiArgs {
#[arg(help = "List of targets (scope:task) to run")]
targets: Vec<Target>,
#[arg(help = "List of targets to run")]
targets: Vec<TargetLocator>,

#[arg(long, help = "Base branch, commit, or revision to compare against")]
base: Option<String>,
Expand Down Expand Up @@ -70,16 +70,14 @@ impl CiConsole {
}

pub fn print_targets(&self, targets: &TargetList) -> miette::Result<()> {
let mut targets_to_print = targets.clone();
let mut targets_to_print = targets
.iter()
.map(|t| format!(" {}", color::label(t.as_str())))
.collect::<Vec<_>>();

targets_to_print.sort();

self.write_line(
targets_to_print
.iter()
.map(|t| format!(" {}", color::label(&t.id)))
.collect::<Vec<_>>()
.join("\n"),
)
self.write_line(targets_to_print.join("\n"))
}
}

Expand Down Expand Up @@ -151,7 +149,7 @@ async fn gather_potential_targets(

if args.targets.is_empty() {
for task in workspace_graph.get_tasks()? {
targets.push(task.target.clone());
targets.push(TargetLocator::Qualified(task.target.clone()));
}
} else {
targets.extend(args.targets.clone());
Expand Down Expand Up @@ -218,11 +216,7 @@ async fn generate_action_graph(
ci: true,
ci_check: true,
dependents: true,
target_locators: FxHashSet::from_iter(
targets
.iter()
.map(|target| TargetLocator::Qualified(target.to_owned())),
),
target_locators: FxHashSet::from_iter(targets.clone()),
..Default::default()
})?;

Expand Down
Loading

0 comments on commit 8863f42

Please sign in to comment.