11use std:: {
22 collections:: hash_map:: Entry ,
33 env:: { join_paths, split_paths} ,
4- ffi:: OsStr ,
4+ ffi:: { OsStr , OsString } ,
55 path:: PathBuf ,
66 process:: { ExitStatus , Stdio } ,
77 sync:: { Arc , LazyLock , Mutex } ,
@@ -123,7 +123,10 @@ pub struct TaskEnvs {
123123 pub envs_without_pass_through : HashMap < Str , Str > ,
124124}
125125
126- fn resolve_envs_with_patterns ( patterns : & [ & str ] ) -> Result < HashMap < Str , Arc < OsStr > > , Error > {
126+ fn resolve_envs_with_patterns (
127+ env_vars : impl Iterator < Item = ( OsString , OsString ) > ,
128+ patterns : & [ & str ] ,
129+ ) -> Result < HashMap < Str , Arc < OsStr > > , Error > {
127130 let patterns = GlobPatternSet :: new ( patterns. iter ( ) . filter ( |pattern| {
128131 if pattern. starts_with ( '!' ) {
129132 // FIXME: use better way to print warning log
@@ -137,7 +140,7 @@ fn resolve_envs_with_patterns(patterns: &[&str]) -> Result<HashMap<Str, Arc<OsSt
137140 true
138141 }
139142 } ) ) ?;
140- let envs: HashMap < Str , Arc < OsStr > > = std :: env :: vars_os ( )
143+ let envs: HashMap < Str , Arc < OsStr > > = env_vars
141144 . filter_map ( |( name, value) | {
142145 let Some ( name) = name. to_str ( ) else {
143146 return None ;
@@ -241,15 +244,19 @@ const SENSITIVE_PATTERNS: &[&str] = &[
241244] ;
242245
243246impl TaskEnvs {
244- pub fn resolve ( base_dir : & AbsolutePath , task : & ResolvedTaskConfig ) -> Result < Self , Error > {
247+ pub fn resolve (
248+ current_envs : impl Iterator < Item = ( OsString , OsString ) > + Clone ,
249+ base_dir : & AbsolutePath ,
250+ task : & ResolvedTaskConfig ,
251+ ) -> Result < Self , Error > {
245252 // All envs that are passed to the task
246253 let all_patterns: Vec < & str > = DEFAULT_PASSTHROUGH_ENVS
247254 . iter ( )
248255 . copied ( )
249256 . chain ( task. config . pass_through_envs . iter ( ) . map ( std:: convert:: AsRef :: as_ref) )
250257 . chain ( task. config . envs . iter ( ) . map ( std:: convert:: AsRef :: as_ref) )
251258 . collect ( ) ;
252- let mut all_envs = resolve_envs_with_patterns ( & all_patterns) ?;
259+ let mut all_envs = resolve_envs_with_patterns ( current_envs , & all_patterns) ?;
253260
254261 // envs need to calculate fingerprint
255262 let mut envs_without_pass_through = HashMap :: < Str , Str > :: new ( ) ;
@@ -562,17 +569,16 @@ mod tests {
562569 let resolved_task_config =
563570 ResolvedTaskConfig { config_dir : RelativePathBuf :: default ( ) , config : task_config } ;
564571
565- // Test when FORCE_COLOR is not already set
566- unsafe {
567- std:: env:: remove_var ( "FORCE_COLOR" ) ;
568- }
569-
570572 let base_dir = if cfg ! ( windows) {
571573 AbsolutePath :: new ( "C:\\ workspace" ) . unwrap ( )
572574 } else {
573575 AbsolutePath :: new ( "/workspace" ) . unwrap ( )
574576 } ;
575- let result = TaskEnvs :: resolve ( base_dir, & resolved_task_config) . unwrap ( ) ;
577+
578+ // Test when FORCE_COLOR is not already set
579+ let mock_envs = vec ! [ ( "PATH" . into( ) , "/usr/bin" . into( ) ) ] ;
580+ let result =
581+ TaskEnvs :: resolve ( mock_envs. into_iter ( ) , base_dir, & resolved_task_config) . unwrap ( ) ;
576582
577583 // FORCE_COLOR should be automatically added if color is supported
578584 // Note: This test might vary based on the test environment
@@ -585,11 +591,10 @@ mod tests {
585591 }
586592
587593 // Test when FORCE_COLOR is already set - should not be overridden
588- unsafe {
589- std:: env:: set_var ( "FORCE_COLOR" , "2" ) ;
590- }
591-
592- let result2 = TaskEnvs :: resolve ( base_dir, & resolved_task_config) . unwrap ( ) ;
594+ let mock_envs =
595+ vec ! [ ( "PATH" . into( ) , "/usr/bin" . into( ) ) , ( "FORCE_COLOR" . into( ) , "2" . into( ) ) ] ;
596+ let result2 =
597+ TaskEnvs :: resolve ( mock_envs. into_iter ( ) , base_dir, & resolved_task_config) . unwrap ( ) ;
593598
594599 // Should contain the original FORCE_COLOR value
595600 assert ! ( result2. all_envs. contains_key( "FORCE_COLOR" ) ) ;
@@ -599,27 +604,15 @@ mod tests {
599604 // FORCE_COLOR should not be in envs_without_pass_through since it's a passthrough env
600605 assert ! ( !result2. envs_without_pass_through. contains_key( "FORCE_COLOR" ) ) ;
601606
602- // Clean up
603- unsafe {
604- std:: env:: remove_var ( "FORCE_COLOR" ) ;
605- }
606-
607- // Test when NO_COLOR is already set - should not be overridden
608- unsafe {
609- std:: env:: set_var ( "NO_COLOR" , "1" ) ;
610- }
611-
612- let result3 = TaskEnvs :: resolve ( base_dir, & resolved_task_config) . unwrap ( ) ;
607+ // Test when NO_COLOR is already set - FORCE_COLOR should not be automatically added
608+ let mock_envs = vec ! [ ( "PATH" . into( ) , "/usr/bin" . into( ) ) , ( "NO_COLOR" . into( ) , "1" . into( ) ) ] ;
609+ let result3 =
610+ TaskEnvs :: resolve ( mock_envs. into_iter ( ) , base_dir, & resolved_task_config) . unwrap ( ) ;
613611 assert ! ( result3. all_envs. contains_key( "NO_COLOR" ) ) ;
614612 let no_color_value = result3. all_envs . get ( "NO_COLOR" ) . unwrap ( ) ;
615613 assert_eq ! ( no_color_value. to_str( ) . unwrap( ) , "1" ) ;
616614 // FORCE_COLOR should not be automatically added since NO_COLOR is set
617615 assert ! ( !result3. all_envs. contains_key( "FORCE_COLOR" ) ) ;
618-
619- // Clean up
620- unsafe {
621- std:: env:: remove_var ( "NO_COLOR" ) ;
622- }
623616 }
624617
625618 #[ test]
@@ -654,31 +647,35 @@ mod tests {
654647 let resolved_task_config =
655648 ResolvedTaskConfig { config_dir : RelativePathBuf :: default ( ) , config : task_config } ;
656649
657- let base_dir = if cfg ! ( windows) {
658- AbsolutePath :: new ( "C:\\ workspace" ) . unwrap ( )
659- } else {
660- AbsolutePath :: new ( "/workspace" ) . unwrap ( )
661- } ;
662-
663- // Set up environment variables
664- unsafe {
665- std:: env:: set_var ( "ZEBRA_VAR" , "zebra_value" ) ;
666- std:: env:: set_var ( "ALPHA_VAR" , "alpha_value" ) ;
667- std:: env:: set_var ( "MIDDLE_VAR" , "middle_value" ) ;
668- std:: env:: set_var ( "BETA_VAR" , "beta_value" ) ;
669- std:: env:: set_var ( "VSCODE_VAR" , "vscode_value" ) ;
670- std:: env:: set_var ( "APP1_TOKEN" , "app1_token" ) ;
671- std:: env:: set_var ( "APP2_TOKEN" , "app2_token" ) ;
672- std:: env:: set_var ( "APP1_NAME" , "app1_value" ) ;
673- std:: env:: set_var ( "APP2_NAME" , "app2_value" ) ;
674- std:: env:: set_var ( "APP1_PASSWORD" , "app1_password" ) ;
675- std:: env:: set_var ( "OXLINT_TSGOLINT_PATH" , "/path/to/oxlint_tsgolint" ) ;
676- }
650+ let base_dir = AbsolutePath :: new ( "/workspace" ) . unwrap ( ) ;
651+
652+ // Create mock environment variables
653+ let mock_envs = vec ! [
654+ ( "ZEBRA_VAR" . into( ) , "zebra_value" . into( ) ) ,
655+ ( "ALPHA_VAR" . into( ) , "alpha_value" . into( ) ) ,
656+ ( "MIDDLE_VAR" . into( ) , "middle_value" . into( ) ) ,
657+ ( "BETA_VAR" . into( ) , "beta_value" . into( ) ) ,
658+ ( "VSCODE_VAR" . into( ) , "vscode_value" . into( ) ) ,
659+ ( "APP1_TOKEN" . into( ) , "app1_token" . into( ) ) ,
660+ ( "APP2_TOKEN" . into( ) , "app2_token" . into( ) ) ,
661+ ( "APP1_NAME" . into( ) , "app1_value" . into( ) ) ,
662+ ( "APP2_NAME" . into( ) , "app2_value" . into( ) ) ,
663+ ( "APP1_PASSWORD" . into( ) , "app1_password" . into( ) ) ,
664+ ( "OXLINT_TSGOLINT_PATH" . into( ) , "/path/to/oxlint_tsgolint" . into( ) ) ,
665+ ( "PATH" . into( ) , "/usr/bin" . into( ) ) ,
666+ ( "HOME" . into( ) , "/home/user" . into( ) ) ,
667+ ] ;
677668
678669 // Resolve envs multiple times
679- let result1 = TaskEnvs :: resolve ( base_dir, & resolved_task_config) . unwrap ( ) ;
680- let result2 = TaskEnvs :: resolve ( base_dir, & resolved_task_config) . unwrap ( ) ;
681- let result3 = TaskEnvs :: resolve ( base_dir, & resolved_task_config) . unwrap ( ) ;
670+ let result1 =
671+ TaskEnvs :: resolve ( mock_envs. clone ( ) . into_iter ( ) , base_dir, & resolved_task_config)
672+ . unwrap ( ) ;
673+ let result2 =
674+ TaskEnvs :: resolve ( mock_envs. clone ( ) . into_iter ( ) , base_dir, & resolved_task_config)
675+ . unwrap ( ) ;
676+ let result3 =
677+ TaskEnvs :: resolve ( mock_envs. clone ( ) . into_iter ( ) , base_dir, & resolved_task_config)
678+ . unwrap ( ) ;
682679
683680 // Convert to sorted vecs for comparison
684681 let mut envs1: Vec < _ > = result1. envs_without_pass_through . iter ( ) . collect ( ) ;
@@ -730,21 +727,6 @@ mod tests {
730727 assert_eq ! ( env_value. to_str( ) . unwrap( ) , "1" ) ;
731728 // VITE_TASK_EXECUTION_ENV should not be in envs_without_pass_through since it's not declared
732729 assert ! ( !result1. envs_without_pass_through. contains_key( "VITE_TASK_EXECUTION_ENV" ) ) ;
733-
734- // Clean up
735- unsafe {
736- std:: env:: remove_var ( "ZEBRA_VAR" ) ;
737- std:: env:: remove_var ( "ALPHA_VAR" ) ;
738- std:: env:: remove_var ( "MIDDLE_VAR" ) ;
739- std:: env:: remove_var ( "BETA_VAR" ) ;
740- std:: env:: remove_var ( "VSCODE_VAR" ) ;
741- std:: env:: remove_var ( "APP1_NAME" ) ;
742- std:: env:: remove_var ( "APP2_NAME" ) ;
743- std:: env:: remove_var ( "APP1_PASSWORD" ) ;
744- std:: env:: remove_var ( "APP1_TOKEN" ) ;
745- std:: env:: remove_var ( "APP2_TOKEN" ) ;
746- std:: env:: remove_var ( "OXLINT_TSGOLINT_PATH" ) ;
747- }
748730 }
749731
750732 #[ test]
@@ -777,16 +759,20 @@ mod tests {
777759 let resolved_task_config =
778760 ResolvedTaskConfig { config_dir : RelativePathBuf :: default ( ) , config : task_config } ;
779761
780- // Set up environment variables with different cases
781- unsafe {
782- std :: env :: set_var ( "TEST_VAR" , "uppercase" ) ;
783- std :: env :: set_var ( "test_var" , "lowercase" ) ;
784- std :: env :: set_var ( "Test_Var" , "mixed" ) ;
785- }
762+ // Create mock environment variables with different cases
763+ let mock_envs = vec ! [
764+ ( "TEST_VAR" . into ( ) , "uppercase" . into ( ) ) ,
765+ ( "test_var" . into ( ) , "lowercase" . into ( ) ) ,
766+ ( "Test_Var" . into ( ) , "mixed" . into ( ) ) ,
767+ ] ;
786768
787769 // Resolve envs
788- let result =
789- TaskEnvs :: resolve ( AbsolutePath :: new ( "/tmp" ) . unwrap ( ) , & resolved_task_config) . unwrap ( ) ;
770+ let result = TaskEnvs :: resolve (
771+ mock_envs. into_iter ( ) ,
772+ AbsolutePath :: new ( "/tmp" ) . unwrap ( ) ,
773+ & resolved_task_config,
774+ )
775+ . unwrap ( ) ;
790776 let envs_without_pass_through = result. envs_without_pass_through ;
791777
792778 // On Unix, all three should be treated as separate variables
@@ -808,13 +794,6 @@ mod tests {
808794 envs_without_pass_through. get( "Test_Var" ) . map( vite_str:: Str :: as_str) ,
809795 Some ( "mixed" )
810796 ) ;
811-
812- // Clean up
813- unsafe {
814- std:: env:: remove_var ( "TEST_VAR" ) ;
815- std:: env:: remove_var ( "test_var" ) ;
816- std:: env:: remove_var ( "Test_Var" ) ;
817- }
818797 }
819798
820799 #[ test]
@@ -847,28 +826,37 @@ mod tests {
847826 let resolved_task_config =
848827 ResolvedTaskConfig { config_dir : RelativePathBuf :: default ( ) , config : task_config } ;
849828
850- // Set up environment variables
851- unsafe {
852- std :: env :: set_var ( "ZEBRA_VAR" , "zebra_value" ) ;
853- std :: env :: set_var ( "ALPHA_VAR" , "alpha_value" ) ;
854- std :: env :: set_var ( "MIDDLE_VAR" , "middle_value" ) ;
855- std :: env :: set_var ( "BETA_VAR" , "beta_value" ) ;
856- // VSCode specific
857- std :: env :: set_var ( "VSCODE_VAR" , "vscode_value" ) ;
858- std :: env :: set_var ( "app1_name" , "app1_value" ) ;
859- std :: env :: set_var ( "app2_name" , "app2_value" ) ;
860- }
829+ // Create mock environment variables
830+ let mock_envs = vec ! [
831+ ( "ZEBRA_VAR" . into ( ) , "zebra_value" . into ( ) ) ,
832+ ( "ALPHA_VAR" . into ( ) , "alpha_value" . into ( ) ) ,
833+ ( "MIDDLE_VAR" . into ( ) , "middle_value" . into ( ) ) ,
834+ ( "BETA_VAR" . into ( ) , "beta_value" . into ( ) ) ,
835+ ( "VSCODE_VAR" . into ( ) , "vscode_value" . into ( ) ) ,
836+ ( "app1_name" . into ( ) , "app1_value" . into ( ) ) ,
837+ ( "app2_name" . into ( ) , "app2_value" . into ( ) ) ,
838+ ( "Path" . into ( ) , "C: \\ Windows \\ System32" . into ( ) ) ,
839+ ] ;
861840
862841 // Resolve envs multiple times
863- let result1 =
864- TaskEnvs :: resolve ( AbsolutePath :: new ( "C:\\ tmp" ) . unwrap ( ) , & resolved_task_config)
865- . unwrap ( ) ;
866- let result2 =
867- TaskEnvs :: resolve ( AbsolutePath :: new ( "C:\\ tmp" ) . unwrap ( ) , & resolved_task_config)
868- . unwrap ( ) ;
869- let result3 =
870- TaskEnvs :: resolve ( AbsolutePath :: new ( "C:\\ tmp" ) . unwrap ( ) , & resolved_task_config)
871- . unwrap ( ) ;
842+ let result1 = TaskEnvs :: resolve (
843+ mock_envs. clone ( ) . into_iter ( ) ,
844+ AbsolutePath :: new ( "C:\\ tmp" ) . unwrap ( ) ,
845+ & resolved_task_config,
846+ )
847+ . unwrap ( ) ;
848+ let result2 = TaskEnvs :: resolve (
849+ mock_envs. clone ( ) . into_iter ( ) ,
850+ AbsolutePath :: new ( "C:\\ tmp" ) . unwrap ( ) ,
851+ & resolved_task_config,
852+ )
853+ . unwrap ( ) ;
854+ let result3 = TaskEnvs :: resolve (
855+ mock_envs. clone ( ) . into_iter ( ) ,
856+ AbsolutePath :: new ( "C:\\ tmp" ) . unwrap ( ) ,
857+ & resolved_task_config,
858+ )
859+ . unwrap ( ) ;
872860
873861 // Convert to sorted vecs for comparison
874862 let mut envs1: Vec < _ > = result1. envs_without_pass_through . iter ( ) . collect ( ) ;
@@ -897,17 +885,6 @@ mod tests {
897885 assert ! ( all_envs. contains_key( "VSCODE_VAR" ) ) ;
898886 assert ! ( all_envs. contains_key( "Path" ) || all_envs. contains_key( "PATH" ) ) ;
899887 assert ! ( all_envs. contains_key( "app1_name" ) ) ;
900- assert ! ( all_envs. contains_key( "app1_name" ) ) ;
901-
902- // Clean up
903- unsafe {
904- std:: env:: remove_var ( "ZEBRA_VAR" ) ;
905- std:: env:: remove_var ( "ALPHA_VAR" ) ;
906- std:: env:: remove_var ( "MIDDLE_VAR" ) ;
907- std:: env:: remove_var ( "BETA_VAR" ) ;
908- std:: env:: remove_var ( "VSCODE_VAR" ) ;
909- std:: env:: remove_var ( "app1_name" ) ;
910- std:: env:: remove_var ( "app1_name" ) ;
911- }
888+ assert ! ( all_envs. contains_key( "app2_name" ) ) ;
912889 }
913890}
0 commit comments