@@ -9,24 +9,20 @@ use tracing::debug;
99
1010/// Builds a [`forge_domain::Environment`] from runtime context only.
1111///
12- /// Only the six fields that cannot be sourced from [`ForgeConfig`] are set
13- /// here: `os`, `pid`, `cwd`, `home`, `shell`, and `base_path`. All
14- /// configuration values are now accessed through
15- /// `EnvironmentInfra::get_config()`.
12+ /// Only the five fields that cannot be sourced from [`ForgeConfig`] are set
13+ /// here: `os`, `cwd`, `home`, `shell`, and `base_path`. All configuration
14+ /// values are now accessed through `EnvironmentInfra::get_config()`.
1615pub fn to_environment ( cwd : PathBuf ) -> Environment {
1716 Environment {
1817 os : std:: env:: consts:: OS . to_string ( ) ,
19- pid : std:: process:: id ( ) ,
2018 cwd,
2119 home : dirs:: home_dir ( ) ,
2220 shell : if cfg ! ( target_os = "windows" ) {
2321 std:: env:: var ( "COMSPEC" ) . unwrap_or_else ( |_| "cmd.exe" . to_string ( ) )
2422 } else {
2523 std:: env:: var ( "SHELL" ) . unwrap_or_else ( |_| "/bin/sh" . to_string ( ) )
2624 } ,
27- base_path : dirs:: home_dir ( )
28- . map ( |h| h. join ( "forge" ) )
29- . unwrap_or_else ( || PathBuf :: from ( "." ) . join ( "forge" ) ) ,
25+ base_path : ConfigReader :: base_path ( ) ,
3026 }
3127}
3228
@@ -172,19 +168,65 @@ impl EnvironmentInfra for ForgeEnvironmentInfra {
172168#[ cfg( test) ]
173169mod tests {
174170 use std:: path:: PathBuf ;
171+ use std:: sync:: { Mutex , MutexGuard } ;
175172
176173 use forge_config:: ForgeConfig ;
177174 use pretty_assertions:: assert_eq;
178175
179176 use super :: * ;
180177
178+ /// Serializes tests that mutate environment variables to prevent races.
179+ static ENV_MUTEX : Mutex < ( ) > = Mutex :: new ( ( ) ) ;
180+
181+ /// Holds env vars set for a test's duration and removes them on drop,
182+ /// while holding [`ENV_MUTEX`].
183+ struct EnvGuard {
184+ keys : Vec < & ' static str > ,
185+ _lock : MutexGuard < ' static , ( ) > ,
186+ }
187+
188+ impl EnvGuard {
189+ #[ must_use]
190+ fn set ( pairs : & [ ( & ' static str , & str ) ] ) -> Self {
191+ let lock = ENV_MUTEX . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
192+ let keys = pairs. iter ( ) . map ( |( k, _) | * k) . collect ( ) ;
193+ for ( key, value) in pairs {
194+ unsafe { std:: env:: set_var ( key, value) } ;
195+ }
196+ Self { keys, _lock : lock }
197+ }
198+ }
199+
200+ impl Drop for EnvGuard {
201+ fn drop ( & mut self ) {
202+ for key in & self . keys {
203+ unsafe { std:: env:: remove_var ( key) } ;
204+ }
205+ }
206+ }
207+
181208 #[ test]
182209 fn test_to_environment_sets_cwd ( ) {
183210 let fixture_cwd = PathBuf :: from ( "/test/cwd" ) ;
184211 let actual = to_environment ( fixture_cwd. clone ( ) ) ;
185212 assert_eq ! ( actual. cwd, fixture_cwd) ;
186213 }
187214
215+ #[ test]
216+ fn test_to_environment_uses_forge_config_env_var ( ) {
217+ let _guard = EnvGuard :: set ( & [ ( "FORGE_CONFIG" , "/custom/config/dir" ) ] ) ;
218+ let actual = to_environment ( PathBuf :: from ( "/any/cwd" ) ) ;
219+ let expected = PathBuf :: from ( "/custom/config/dir" ) ;
220+ assert_eq ! ( actual. base_path, expected) ;
221+ }
222+
223+ #[ test]
224+ fn test_to_environment_falls_back_to_home_dir_when_env_var_absent ( ) {
225+ let actual = to_environment ( PathBuf :: from ( "/any/cwd" ) ) ;
226+ // Without FORGE_CONFIG the base_path must end with "forge"
227+ assert_eq ! ( actual. base_path. file_name( ) . unwrap( ) , "forge" ) ;
228+ }
229+
188230 #[ test]
189231 fn test_apply_config_op_set_provider ( ) {
190232 use forge_domain:: ProviderId ;
0 commit comments