Skip to content

Commit bb00e64

Browse files
authored
new: Add more task input types. (#2128)
* Add new input type. * Implement affected. * Fix tests. * Add affected tests. * Update docs. * Added ^ support. * Rename enum. * Start on group input. * Rename file variant. * Rename file variant. * Fixes. * Polish. * Add expander tests.
1 parent f30f78f commit bb00e64

40 files changed

+1491
-434
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
## Unreleased
44

5+
#### 🚀 Updates
6+
7+
- Added a new task input type that allows you to depend on changes to a project's files directly,
8+
instead of depending on a project's task.
9+
- Added URI support: `project://<id>`
10+
- Added object support: `project: '<id>'`
11+
- Can filter with globs or by file group.
12+
- Can reference all project dependencies with `^`.
13+
- Added a new task input type for referencing file groups within the owning project.
14+
- Added URI support: `group://<name>`
15+
- Added object support: `group: '<name>'`
16+
- This is similar to the `@files`, `@globs`, etc, token functions.
17+
518
#### ⚙️ Internal
619

720
- Updated proto to [v0.53.0](https://github.com/moonrepo/proto/releases/tag/v0.53.0) (from 0.52.3).

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ starbase_archive = { version = "0.11.6", default-features = false, features = [
7171
] }
7272
starbase_console = { version = "0.6.13", features = ["miette"] }
7373
starbase_events = "0.7.3"
74-
starbase_id = { version = "0.2.2", features = ["miette", "schematic"] }
74+
starbase_id = { version = "0.2.3", features = ["miette", "schematic"] }
7575
starbase_sandbox = "0.9.7"
7676
starbase_shell = "0.10.2"
7777
starbase_styles = { version = "0.6.1", features = ["relative-path"] }

crates/action-graph/src/action_graph_builder.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ use moon_common::{Id, color};
1414
use moon_config::{PipelineActionSwitch, TaskDependencyConfig};
1515
use moon_pdk_api::{DefineRequirementsInput, LocateDependenciesRootInput};
1616
use moon_platform::{PlatformManager, Runtime, ToolchainSpec};
17-
use moon_project::Project;
17+
use moon_project::{Project, ProjectError};
1818
use moon_query::{Criteria, build_query};
1919
use moon_task::{Target, TargetError, TargetLocator, TargetScope, Task};
2020
use moon_task_args::parse_task_args;
21-
use moon_workspace_graph::{GraphConnections, WorkspaceGraph, tasks::TaskGraphError};
21+
use moon_workspace_graph::{GraphConnections, WorkspaceGraph};
2222
use petgraph::prelude::*;
2323
use rustc_hash::{FxHashMap, FxHashSet};
2424
use std::mem;
@@ -627,7 +627,11 @@ impl<'query> ActionGraphBuilder<'query> {
627627

628628
// Don't allow internal tasks to be ran
629629
if !allow_internal && task.is_internal() {
630-
return Err(TaskGraphError::UnconfiguredTarget(task.target.clone()).into());
630+
return Err(ProjectError::UnknownTask {
631+
task_id: task.id.to_string(),
632+
project_id: project_id.to_string(),
633+
}
634+
.into());
631635
}
632636

633637
tasks.push(task);

crates/affected/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ publish = false
1010

1111
[dependencies]
1212
moon_common = { path = "../common" }
13+
moon_config = { path = "../config" }
1314
moon_env_var = { path = "../env-var" }
1415
moon_project = { path = "../project" }
1516
moon_target = { path = "../target" }

crates/affected/src/affected.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ pub struct AffectedTaskState {
120120
#[serde(skip_serializing_if = "FxHashSet::is_empty")]
121121
pub files: FxHashSet<WorkspaceRelativePathBuf>,
122122

123+
#[serde(skip_serializing_if = "FxHashSet::is_empty")]
124+
pub projects: FxHashSet<Id>,
125+
123126
#[serde(skip_serializing_if = "FxHashSet::is_empty")]
124127
pub upstream: FxHashSet<Target>,
125128

@@ -147,6 +150,9 @@ impl AffectedTaskState {
147150
AffectedBy::UpstreamTask(target) => {
148151
state.upstream.insert(target);
149152
}
153+
AffectedBy::UpstreamProject(id) => {
154+
state.projects.insert(id);
155+
}
150156
_ => {
151157
state.other = true;
152158
}

crates/affected/src/affected_tracker.rs

Lines changed: 118 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use crate::affected::*;
22
use moon_common::path::WorkspaceRelativePathBuf;
33
use moon_common::{Id, color};
4+
use moon_config::Input;
45
use moon_env_var::GlobalEnvBag;
56
use moon_project::Project;
67
use moon_task::{Target, Task, TaskOptionRunInCI};
78
use moon_workspace_graph::{GraphConnections, WorkspaceGraph};
89
use rustc_hash::{FxHashMap, FxHashSet};
910
use starbase_utils::fs;
11+
use starbase_utils::glob::GlobSet;
1012
use std::fmt;
1113
use std::sync::Arc;
1214
use tracing::{debug, trace};
@@ -74,10 +76,11 @@ impl AffectedTracker {
7476
debug!(
7577
env = ?state.env.iter().collect::<Vec<_>>(),
7678
files = ?state.files.iter().collect::<Vec<_>>(),
79+
projects = ?state.projects.iter().map(|id| id.as_str()).collect::<Vec<_>>(),
7780
upstream = ?state.upstream.iter().map(|target| target.as_str()).collect::<Vec<_>>(),
7881
downstream = ?state.downstream.iter().map(|target| target.as_str()).collect::<Vec<_>>(),
7982
other = state.other,
80-
"Task {} is affected by", color::label(&target),
83+
"Task {} is affected by", color::id(&target),
8184
);
8285

8386
affected.tasks.insert(target, state);
@@ -161,6 +164,59 @@ impl AffectedTracker {
161164
}
162165
}
163166

167+
pub fn is_project_affected_using_file_group(
168+
&self,
169+
project: &Project,
170+
file_group_id: &Id,
171+
) -> miette::Result<Option<AffectedBy>> {
172+
let group = project.get_file_group(file_group_id)?;
173+
174+
if !group.files.is_empty() {
175+
for file in &group.files {
176+
if self.touched_files.contains(file) {
177+
return Ok(Some(AffectedBy::TouchedFile(file.to_owned())));
178+
}
179+
}
180+
}
181+
182+
if !group.globs.is_empty() {
183+
return self.is_project_affected_using_globs(project, &group.globs);
184+
}
185+
186+
Ok(None)
187+
}
188+
189+
pub fn is_project_affected_using_globs<G: AsRef<str>>(
190+
&self,
191+
project: &Project,
192+
base_globs: &[G],
193+
) -> miette::Result<Option<AffectedBy>> {
194+
let mut globs = vec![];
195+
196+
// Ensure they are relative from the project root
197+
for glob in base_globs {
198+
let glob = glob.as_ref();
199+
200+
if glob.starts_with(project.source.as_str()) {
201+
globs.push(glob.to_owned());
202+
} else {
203+
globs.push(format!("{}/{glob}", project.source));
204+
}
205+
}
206+
207+
if !globs.is_empty() {
208+
let globset = GlobSet::new(&globs)?;
209+
210+
for file in &self.touched_files {
211+
if globset.matches(file.as_str()) {
212+
return Ok(Some(AffectedBy::TouchedFile(file.to_owned())));
213+
}
214+
}
215+
}
216+
217+
Ok(None)
218+
}
219+
164220
pub fn is_project_marked(&self, project: &Project) -> bool {
165221
self.projects.contains_key(&project.id)
166222
}
@@ -332,6 +388,7 @@ impl AffectedTracker {
332388
return Ok(Some(AffectedBy::AlreadyMarked));
333389
}
334390

391+
// Special CI handling
335392
if self.ci {
336393
match &task.options.run_in_ci {
337394
TaskOptionRunInCI::Always => {
@@ -342,11 +399,12 @@ impl AffectedTracker {
342399
};
343400
}
344401

345-
// inputs: []
402+
// Never affected
346403
if task.state.empty_inputs {
347404
return Ok(None);
348405
}
349406

407+
// By env vars
350408
if !task.input_env.is_empty() {
351409
let bag = GlobalEnvBag::instance();
352410

@@ -359,32 +417,77 @@ impl AffectedTracker {
359417
}
360418
}
361419

420+
// By files
362421
let globset = task.create_globset()?;
363422

364423
for file in self.touched_files.iter() {
365-
let mut affected = false;
366-
367-
if let Some(params) = task.input_files.get(file) {
368-
affected = match &params.content {
424+
let affected = if let Some(params) = task.input_files.get(file) {
425+
match &params.content {
369426
Some(matcher) => {
370-
let content =
371-
fs::read_file(file.to_logical_path(&self.workspace_graph.root))?;
427+
let abs_file = file.to_logical_path(&self.workspace_graph.root);
372428

373-
matcher.is_match(&content)
429+
if abs_file.exists() {
430+
matcher.is_match(&fs::read_file(abs_file)?)
431+
} else {
432+
false
433+
}
374434
}
375435
None => true,
376-
};
377-
}
378-
379-
if !affected {
380-
affected = globset.matches(file.as_str());
381-
}
436+
}
437+
} else {
438+
globset.matches(file.as_str())
439+
};
382440

383441
if affected {
384442
return Ok(Some(AffectedBy::TouchedFile(file.to_owned())));
385443
}
386444
}
387445

446+
// By other inputs
447+
let mut has_all_project_sources = false;
448+
449+
for input in &task.inputs {
450+
match input {
451+
Input::Project(inner) => {
452+
if has_all_project_sources {
453+
continue;
454+
}
455+
456+
let projects = if inner.project == "^" {
457+
has_all_project_sources = true;
458+
459+
let parent = self
460+
.workspace_graph
461+
.get_project(task.target.get_project_id()?)?;
462+
463+
self.workspace_graph
464+
.get_projects_by_id(parent.dependencies.iter().map(|dep| &dep.id))?
465+
} else {
466+
vec![self.workspace_graph.get_project(&inner.project)?]
467+
};
468+
469+
for project in projects {
470+
let affected = if let Some(group_id) = &inner.group {
471+
self.is_project_affected_using_file_group(&project, group_id)?
472+
.is_some()
473+
} else if !inner.filter.is_empty() {
474+
self.is_project_affected_using_globs(&project, &inner.filter)?
475+
.is_some()
476+
} else {
477+
self.is_project_affected(&project).is_some()
478+
};
479+
480+
if affected {
481+
return Ok(Some(AffectedBy::UpstreamProject(project.id.clone())));
482+
}
483+
}
484+
}
485+
_ => {
486+
// Skip
487+
}
488+
};
489+
}
490+
388491
Ok(None)
389492
}
390493

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fileGroups:
2+
tests:
3+
- 'tests/**/*'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fileGroups:
2+
sources:
3+
- 'src/**/*'
4+
tests:
5+
- 'tests/**/*'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
dependsOn:
2+
- 'dep'
3+
- 'dep-test'
4+
5+
tasks:
6+
by-any:
7+
inputs:
8+
- 'project://dep'
9+
by-deps:
10+
inputs:
11+
- project: '^'
12+
group: 'tests'
13+
by-group:
14+
inputs:
15+
- project: 'dep'
16+
group: 'sources'
17+
by-filter:
18+
inputs:
19+
- 'project://dep?filter=tests/**/*'

0 commit comments

Comments
 (0)