@@ -11,14 +11,14 @@ use clap::ValueEnum;
11
11
use miette:: { NamedSource , SourceSpan } ;
12
12
use serde:: { Deserialize , Serialize } ;
13
13
use struct_iterable:: Iterable ;
14
- use turbopath:: AbsoluteSystemPath ;
14
+ use turbopath:: { AbsoluteSystemPath , AnchoredSystemPath , AnchoredSystemPathBuf } ;
15
15
use turborepo_errors:: Spanned ;
16
16
use turborepo_repository:: package_graph:: ROOT_PKG_NAME ;
17
17
use turborepo_unescape:: UnescapedString ;
18
18
19
19
use crate :: {
20
20
cli:: { EnvMode , OutputLogsMode } ,
21
- config:: { ConfigurationOptions , Error , InvalidEnvPrefixError } ,
21
+ config:: { ConfigurationOptions , Error , InvalidEnvPrefixError , RootSyntaxInGlobalDepsError } ,
22
22
run:: {
23
23
task_access:: TaskAccessTraceFile ,
24
24
task_id:: { TaskId , TaskName } ,
@@ -33,6 +33,8 @@ pub use loader::TurboJsonLoader;
33
33
34
34
use crate :: { boundaries:: BoundariesConfig , config:: UnnecessaryPackageTaskSyntaxError } ;
35
35
36
+ const TURBO_ROOT : & str = "$TURBO_ROOT$" ;
37
+
36
38
#[ derive( Serialize , Deserialize , Debug , Default , PartialEq , Clone , Deserializable ) ]
37
39
#[ serde( rename_all = "camelCase" ) ]
38
40
pub struct SpacesJson {
@@ -537,10 +539,11 @@ impl RawTurboJson {
537
539
}
538
540
}
539
541
540
- impl TryFrom < RawTurboJson > for TurboJson {
541
- type Error = Error ;
542
-
543
- fn try_from ( raw_turbo : RawTurboJson ) -> Result < Self , Error > {
542
+ impl TurboJson {
543
+ fn from_raw (
544
+ raw_turbo : RawTurboJson ,
545
+ path_to_repo_root : & AnchoredSystemPath ,
546
+ ) -> Result < Self , Error > {
544
547
if let Some ( pipeline) = raw_turbo. pipeline {
545
548
let ( span, text) = pipeline. span_and_text ( "turbo.json" ) ;
546
549
return Err ( Error :: PipelineField { span, text } ) ;
@@ -608,6 +611,8 @@ impl TryFrom<RawTurboJson> for TurboJson {
608
611
// Remote Cache config is handled through layered config
609
612
} )
610
613
}
614
+
615
+ // fn task
611
616
}
612
617
613
618
impl TurboJson {
@@ -629,7 +634,12 @@ impl TurboJson {
629
634
path : & AbsoluteSystemPath ,
630
635
) -> Result < TurboJson , Error > {
631
636
let raw_turbo_json = RawTurboJson :: read ( repo_root, path) ?;
632
- raw_turbo_json. try_into ( )
637
+ // pass in repo_root
638
+ // This needs the relative path to root
639
+ let pkg_path = path. parent ( ) . expect ( "turbo.json is not root" ) ;
640
+ // relative pkg_path -> repo_root
641
+ let path_to_root = AnchoredSystemPathBuf :: relative_path_between ( pkg_path, repo_root) ;
642
+ TurboJson :: from_raw ( raw_turbo_json, & path_to_root)
633
643
}
634
644
635
645
pub fn task ( & self , task_id : & TaskId , task_name : & TaskName ) -> Option < RawTaskDefinition > {
@@ -739,6 +749,30 @@ pub fn validate_extends(turbo_json: &TurboJson) -> Vec<Error> {
739
749
}
740
750
}
741
751
752
+ pub fn validate_no_root_syntax_in_global_deps ( turbo_json : & TurboJson ) -> Vec < Error > {
753
+ let mut errors = Vec :: new ( ) ;
754
+
755
+ for dep in & turbo_json. global_deps {
756
+ if dep. starts_with ( "$TURBO_ROOT$" ) {
757
+ errors. push ( Error :: RootSyntaxInGlobalDeps ( Box :: new (
758
+ RootSyntaxInGlobalDepsError {
759
+ span : turbo_json. text . as_ref ( ) . map ( |text| {
760
+ // Find the position of the $TURBO_ROOT$ in the text
761
+ let pos = text. find ( "$TURBO_ROOT$" ) . unwrap_or ( 0 ) ;
762
+ ( pos, pos + 12 ) . into ( ) // 12 is length of "$TURBO_ROOT$"
763
+ } ) ,
764
+ text : NamedSource :: new (
765
+ turbo_json. path . as_deref ( ) . unwrap_or ( "turbo.json" ) ,
766
+ turbo_json. text . as_deref ( ) . unwrap_or ( "" ) . to_string ( ) ,
767
+ ) ,
768
+ } ,
769
+ ) ) ) ;
770
+ }
771
+ }
772
+
773
+ errors
774
+ }
775
+
742
776
fn gather_env_vars (
743
777
vars : Vec < Spanned < impl Into < String > > > ,
744
778
key : & str ,
@@ -765,6 +799,21 @@ fn gather_env_vars(
765
799
Ok ( ( ) )
766
800
}
767
801
802
+ // $TURBO_ROOT$/something
803
+ fn replace_turbo_root_token (
804
+ task_definition : & mut TaskDefinition ,
805
+ path_to_repo_root : & AnchoredSystemPath ,
806
+ ) {
807
+ for input in task_definition. inputs . iter_mut ( ) {
808
+ let swapped = input. replacen ( TURBO_ROOT , path_to_repo_root. as_str ( ) , 1 ) ;
809
+ }
810
+ /* need to go through incl & exclu individually
811
+ for output in task_definition.outputs.iter_mut() {
812
+ let swapped = output.replacen(TURBO_ROOT, path_to_repo_root.as_str(), 1);
813
+ }
814
+ */
815
+ }
816
+
768
817
#[ cfg( test) ]
769
818
mod tests {
770
819
use anyhow:: Result ;
@@ -775,13 +824,15 @@ mod tests {
775
824
use test_case:: test_case;
776
825
use turborepo_unescape:: UnescapedString ;
777
826
778
- use super :: { RawTurboJson , SpacesJson , Spanned , TurboJson , UIMode } ;
827
+ use super :: {
828
+ validate_no_root_syntax_in_global_deps, RawTaskDefinition , RawTurboJson , SpacesJson ,
829
+ Spanned , TaskName , TurboJson , UIMode ,
830
+ } ;
779
831
use crate :: {
780
832
boundaries:: BoundariesConfig ,
781
833
cli:: OutputLogsMode ,
782
- run :: task_id :: TaskName ,
834
+ config :: Error ,
783
835
task_graph:: { TaskDefinition , TaskOutputs } ,
784
- turbo_json:: RawTaskDefinition ,
785
836
} ;
786
837
787
838
#[ test_case( "{}" , "empty boundaries" ) ]
@@ -1191,4 +1242,32 @@ mod tests {
1191
1242
& [ Spanned :: new( UnescapedString :: from( "api#server" ) ) ]
1192
1243
) ;
1193
1244
}
1245
+
1246
+ #[ test]
1247
+ fn test_validate_no_root_syntax_in_global_deps ( ) {
1248
+ let json_with_root = r#"{
1249
+ "globalDependencies": ["$TURBO_ROOT$/some/path"]
1250
+ }"# ;
1251
+
1252
+ let json_without_root = r#"{
1253
+ "globalDependencies": ["some/path"]
1254
+ }"# ;
1255
+
1256
+ let turbo_json_with_root = RawTurboJson :: parse ( json_with_root, "turbo.json" ) . unwrap ( ) ;
1257
+ let turbo_json = TurboJson :: try_from ( turbo_json_with_root) . unwrap ( ) ;
1258
+ let errors = validate_no_root_syntax_in_global_deps ( & turbo_json) ;
1259
+ assert_eq ! ( errors. len( ) , 1 ) ;
1260
+ match & errors[ 0 ] {
1261
+ Error :: RootSyntaxInGlobalDeps ( error) => {
1262
+ assert ! ( error. span. is_some( ) ) ;
1263
+ assert_eq ! ( error. text. name( ) , "turbo.json" ) ;
1264
+ }
1265
+ _ => panic ! ( "Expected RootSyntaxInGlobalDeps error" ) ,
1266
+ }
1267
+
1268
+ let turbo_json_without_root = RawTurboJson :: parse ( json_without_root, "turbo.json" ) . unwrap ( ) ;
1269
+ let turbo_json = TurboJson :: try_from ( turbo_json_without_root) . unwrap ( ) ;
1270
+ let errors = validate_no_root_syntax_in_global_deps ( & turbo_json) ;
1271
+ assert ! ( errors. is_empty( ) ) ;
1272
+ }
1194
1273
}
0 commit comments