@@ -7,6 +7,7 @@ use rattler_shell::{
77 activation:: PathModificationBehavior ,
88 shell:: { Bash , CmdExe , PowerShell , Shell , ShellEnum , ShellScript } ,
99} ;
10+ use tempfile:: TempPath ;
1011
1112use pixi_config:: { ConfigCli , ConfigCliActivation , ConfigCliPrompt } ;
1213use pixi_core:: {
@@ -137,60 +138,85 @@ fn start_cmdexe(
137138}
138139
139140// allowing dead code so that we test this on unix compilation as well
140- #[ cfg_attr( unix, expect( unused) ) ]
141- fn start_winbash (
142- bash : Bash ,
141+ #[ allow( dead_code) ]
142+ fn start_shell_using_winbash < T : Shell + Copy + ' static , F : FnOnce ( & TempPath ) -> String > (
143+ shell : T ,
144+ exec_format : F ,
143145 env : & HashMap < String , String > ,
144146 prompt : String ,
145147 prefix : & Prefix ,
146148 source_shell_completions : bool ,
147149) -> miette:: Result < Option < i32 > > {
148150 // create a tempfile for activation
149- let mut temp_file = tempfile:: Builder :: new ( )
150- . prefix ( "pixi_env_ " )
151- . suffix ( & format ! ( ".{}" , bash . extension ( ) ) )
151+ let mut init_file = tempfile:: Builder :: new ( )
152+ . prefix ( "pixi_init_ " )
153+ . suffix ( ".sh" )
152154 . rand_bytes ( 3 )
153155 . tempfile ( )
154156 . into_diagnostic ( ) ?;
155157
156- let mut shell_script = ShellScript :: new ( bash, Platform :: current ( ) ) ;
158+ // the init file is always consumed by bash
159+ let mut init_script = ShellScript :: new ( Bash , Platform :: current ( ) ) ;
157160 for ( key, value) in env {
158161 if key == "PATH" || key == "Path" {
159162 // For Git Bash on Windows, the PATH must be formatted as POSIX paths according
160163 // to the cygpath command, and separated by ":" instead of ";". Use the
161- // shell_script .set_path call to handle these details.
164+ // init_script .set_path call to handle these details.
162165 let paths = value
163- . split ( ";" )
166+ . split ( ';' )
164167 . map ( PathBuf :: from)
165168 . collect :: < Vec < PathBuf > > ( ) ;
166- shell_script
169+ init_script
167170 . set_path ( & paths, PathModificationBehavior :: Replace )
168171 . into_diagnostic ( ) ?;
169172 } else {
170- shell_script . set_env_var ( key, value) . into_diagnostic ( ) ?;
173+ init_script . set_env_var ( key, value) . into_diagnostic ( ) ?;
171174 }
172175 }
176+ init_file
177+ . write_all ( init_script. contents ( ) . into_diagnostic ( ) ?. as_bytes ( ) )
178+ . into_diagnostic ( ) ?;
179+
180+ // the env file is consumed by the actual shell
181+ let mut env_file = tempfile:: Builder :: new ( )
182+ . prefix ( "pixi_env_" )
183+ . suffix ( & format ! ( ".{}" , shell. extension( ) ) )
184+ . rand_bytes ( 3 )
185+ . tempfile ( )
186+ . into_diagnostic ( ) ?;
187+
188+ // Write custom prompt to the env file
189+ env_file. write_all ( prompt. as_bytes ( ) ) . into_diagnostic ( ) ?;
190+
191+ // Write code to bootstrap the actual shell we want to start
173192 if source_shell_completions {
174- if let Some ( completions_dir) = bash. completion_script_location ( ) {
175- shell_script
193+ let mut env_script = ShellScript :: new ( shell, Platform :: current ( ) ) ;
194+ if let Some ( completions_dir) = shell. completion_script_location ( ) {
195+ env_script
176196 . source_completions ( & prefix. root ( ) . join ( completions_dir) )
177197 . into_diagnostic ( ) ?;
178198 }
199+ env_file
200+ . write_all ( env_script. contents ( ) . into_diagnostic ( ) ?. as_bytes ( ) )
201+ . into_diagnostic ( ) ?;
179202 }
180- temp_file
181- . write_all ( shell_script. contents ( ) . into_diagnostic ( ) ?. as_bytes ( ) )
203+
204+ env_file. flush ( ) . into_diagnostic ( ) ?;
205+ let env_file_path = env_file. into_temp_path ( ) ;
206+ let exec_str = exec_format ( & env_file_path) ;
207+
208+ init_file
209+ . write_all ( exec_str. as_bytes ( ) )
182210 . into_diagnostic ( ) ?;
183211
184- // Write custom prompt to the env file
185- temp_file. write_all ( prompt. as_bytes ( ) ) . into_diagnostic ( ) ?;
186- temp_file. flush ( ) . into_diagnostic ( ) ?;
212+ init_file. flush ( ) . into_diagnostic ( ) ?;
187213
188214 // close the file handle, but keep the path (needed for Windows)
189- let temp_path = temp_file . into_temp_path ( ) ;
215+ let init_file_path = init_file . into_temp_path ( ) ;
190216
191- let mut command = std:: process:: Command :: new ( bash . executable ( ) ) ;
217+ let mut command = std:: process:: Command :: new ( Bash . executable ( ) ) ;
192218 command. arg ( "--init-file" ) ;
193- command. arg ( & temp_path ) ;
219+ command. arg ( & init_file_path ) ;
194220 command. arg ( "-i" ) ;
195221
196222 ignore_ctrl_c ( ) ;
@@ -390,7 +416,24 @@ pub async fn execute(args: Args) -> miette::Result<()> {
390416 ShellEnum :: PowerShell ( pwsh) => start_powershell ( pwsh, env, prompt_hook) ,
391417 ShellEnum :: CmdExe ( cmdexe) => start_cmdexe ( cmdexe, env, prompt_hook) ,
392418 ShellEnum :: Bash ( bash) => {
393- start_winbash ( bash, env, prompt_hook, & prefix, source_shell_completions)
419+ start_shell_using_winbash (
420+ bash,
421+ |env_file| format ! ( ". \" {}\" " , env_file. as_os_str( ) . to_string_lossy( ) ) ,
422+ env,
423+ prompt_hook,
424+ & prefix,
425+ source_shell_completions,
426+ )
427+ }
428+ ShellEnum :: Fish ( fish) => {
429+ start_shell_using_winbash (
430+ fish,
431+ |env_file| format ! ( "exec fish -i -C \" . \\ \" {}\\ \" \" " , env_file. as_os_str( ) . to_string_lossy( ) ) ,
432+ env,
433+ prompt_hook,
434+ & prefix,
435+ source_shell_completions,
436+ )
394437 }
395438 _ => {
396439 miette:: bail!( "Unsupported shell: {:?}" , interactive_shell) ;
@@ -461,3 +504,4 @@ pub async fn execute(args: Args) -> miette::Result<()> {
461504 }
462505 }
463506}
507+
0 commit comments