1
1
use crate :: affected:: * ;
2
2
use moon_common:: path:: WorkspaceRelativePathBuf ;
3
3
use moon_common:: { Id , color} ;
4
+ use moon_config:: Input ;
4
5
use moon_env_var:: GlobalEnvBag ;
5
6
use moon_project:: Project ;
6
7
use moon_task:: { Target , Task , TaskOptionRunInCI } ;
7
8
use moon_workspace_graph:: { GraphConnections , WorkspaceGraph } ;
8
9
use rustc_hash:: { FxHashMap , FxHashSet } ;
9
10
use starbase_utils:: fs;
11
+ use starbase_utils:: glob:: GlobSet ;
10
12
use std:: fmt;
11
13
use std:: sync:: Arc ;
12
14
use tracing:: { debug, trace} ;
@@ -74,10 +76,11 @@ impl AffectedTracker {
74
76
debug ! (
75
77
env = ?state. env. iter( ) . collect:: <Vec <_>>( ) ,
76
78
files = ?state. files. iter( ) . collect:: <Vec <_>>( ) ,
79
+ projects = ?state. projects. iter( ) . map( |id| id. as_str( ) ) . collect:: <Vec <_>>( ) ,
77
80
upstream = ?state. upstream. iter( ) . map( |target| target. as_str( ) ) . collect:: <Vec <_>>( ) ,
78
81
downstream = ?state. downstream. iter( ) . map( |target| target. as_str( ) ) . collect:: <Vec <_>>( ) ,
79
82
other = state. other,
80
- "Task {} is affected by" , color:: label ( & target) ,
83
+ "Task {} is affected by" , color:: id ( & target) ,
81
84
) ;
82
85
83
86
affected. tasks . insert ( target, state) ;
@@ -161,6 +164,59 @@ impl AffectedTracker {
161
164
}
162
165
}
163
166
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
+
164
220
pub fn is_project_marked ( & self , project : & Project ) -> bool {
165
221
self . projects . contains_key ( & project. id )
166
222
}
@@ -332,6 +388,7 @@ impl AffectedTracker {
332
388
return Ok ( Some ( AffectedBy :: AlreadyMarked ) ) ;
333
389
}
334
390
391
+ // Special CI handling
335
392
if self . ci {
336
393
match & task. options . run_in_ci {
337
394
TaskOptionRunInCI :: Always => {
@@ -342,11 +399,12 @@ impl AffectedTracker {
342
399
} ;
343
400
}
344
401
345
- // inputs: []
402
+ // Never affected
346
403
if task. state . empty_inputs {
347
404
return Ok ( None ) ;
348
405
}
349
406
407
+ // By env vars
350
408
if !task. input_env . is_empty ( ) {
351
409
let bag = GlobalEnvBag :: instance ( ) ;
352
410
@@ -359,32 +417,77 @@ impl AffectedTracker {
359
417
}
360
418
}
361
419
420
+ // By files
362
421
let globset = task. create_globset ( ) ?;
363
422
364
423
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 {
369
426
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 ) ;
372
428
373
- matcher. is_match ( & content)
429
+ if abs_file. exists ( ) {
430
+ matcher. is_match ( & fs:: read_file ( abs_file) ?)
431
+ } else {
432
+ false
433
+ }
374
434
}
375
435
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
+ } ;
382
440
383
441
if affected {
384
442
return Ok ( Some ( AffectedBy :: TouchedFile ( file. to_owned ( ) ) ) ) ;
385
443
}
386
444
}
387
445
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
+
388
491
Ok ( None )
389
492
}
390
493
0 commit comments