diff --git a/crates/deno_task_shell/src/shell/commands/exit.rs b/crates/deno_task_shell/src/shell/commands/exit.rs index 98e7c50..f8f0ee1 100644 --- a/crates/deno_task_shell/src/shell/commands/exit.rs +++ b/crates/deno_task_shell/src/shell/commands/exit.rs @@ -19,10 +19,10 @@ impl ShellCommand for ExitCommand { mut context: ShellCommandContext, ) -> LocalBoxFuture<'static, ExecuteResult> { let result = match execute_exit(context.args) { - Ok(code) => ExecuteResult::Exit(code, Vec::new()), + Ok(code) => ExecuteResult::Exit(code, Vec::new(), Vec::new()), Err(err) => { context.stderr.write_line(&format!("exit: {err}")).unwrap(); - ExecuteResult::Exit(2, Vec::new()) + ExecuteResult::Exit(2, Vec::new(), Vec::new()) } }; Box::pin(futures::future::ready(result)) diff --git a/crates/deno_task_shell/src/shell/execute.rs b/crates/deno_task_shell/src/shell/execute.rs index 1495369..7f1aece 100644 --- a/crates/deno_task_shell/src/shell/execute.rs +++ b/crates/deno_task_shell/src/shell/execute.rs @@ -136,7 +136,7 @@ pub async fn execute_with_pipes( .await; match result { - ExecuteResult::Exit(code, _) => code, + ExecuteResult::Exit(code, _, _) => code, ExecuteResult::Continue(exit_code, _, _) => exit_code, } } @@ -191,7 +191,10 @@ pub fn execute_sequential_list( ) .await; match result { - ExecuteResult::Exit(exit_code, handles) => { + ExecuteResult::Exit(exit_code, changes, handles) => { + state.apply_changes(&changes); + state.set_shell_var("?", &exit_code.to_string()); + final_changes.extend(changes); async_handles.extend(handles); final_exit_code = exit_code; was_exit = true; @@ -223,7 +226,7 @@ pub fn execute_sequential_list( } if was_exit { - ExecuteResult::Exit(final_exit_code, async_handles) + ExecuteResult::Exit(final_exit_code, final_changes, async_handles) } else { ExecuteResult::Continue( final_exit_code, @@ -305,7 +308,7 @@ fn execute_sequence( ) .await; let (exit_code, mut async_handles) = match first_result { - ExecuteResult::Exit(_, _) => return first_result, + ExecuteResult::Exit(_, _, _) => return first_result, ExecuteResult::Continue( exit_code, sub_changes, @@ -340,9 +343,10 @@ fn execute_sequence( execute_sequence(next, state, stdin, stdout, stderr) .await; match next_result { - ExecuteResult::Exit(code, sub_handles) => { + ExecuteResult::Exit(code, sub_changes, sub_handles) => { + changes.extend(sub_changes); async_handles.extend(sub_handles); - ExecuteResult::Exit(code, async_handles) + ExecuteResult::Exit(code, changes, async_handles) } ExecuteResult::Continue( exit_code, @@ -382,8 +386,8 @@ async fn execute_pipeline( .await; if pipeline.negated { match result { - ExecuteResult::Exit(code, handles) => { - ExecuteResult::Exit(code, handles) + ExecuteResult::Exit(code, changes, handles) => { + ExecuteResult::Exit(code, changes, handles) } ExecuteResult::Continue(code, changes, handles) => { let new_code = if code == 0 { 1 } else { 0 }; @@ -617,8 +621,8 @@ async fn execute_command( CommandInner::Subshell(list) => { // Here the state can be changed but we can not pass by reference match execute_subshell(list, state, stdin, stdout, stderr).await { - ExecuteResult::Exit(code, handles) => { - ExecuteResult::Exit(code, handles) + ExecuteResult::Exit(code, _, handles) => { + ExecuteResult::Exit(code, changes, handles) } ExecuteResult::Continue(code, _, handles) => { ExecuteResult::Continue(code, changes, handles) @@ -705,7 +709,9 @@ async fn execute_while_clause( .await; match exec_result { - ExecuteResult::Exit(code, handles) => { + ExecuteResult::Exit(code, env_changes, handles) => { + state.apply_changes(&env_changes); + changes.extend(env_changes); async_handles.extend(handles); last_exit_code = code; break; @@ -730,7 +736,7 @@ async fn execute_while_clause( state.apply_changes(&changes); if state.exit_on_error() && last_exit_code != 0 { - ExecuteResult::Exit(last_exit_code, async_handles) + ExecuteResult::Exit(last_exit_code, changes, async_handles) } else { ExecuteResult::Continue(last_exit_code, changes, async_handles) } @@ -776,7 +782,8 @@ async fn execute_for_clause( .await; match result { - ExecuteResult::Exit(code, handles) => { + ExecuteResult::Exit(code, env_changes, handles) => { + changes.extend(env_changes); async_handles.extend(handles); last_exit_code = code; break; @@ -792,7 +799,7 @@ async fn execute_for_clause( state.apply_changes(&changes); if state.exit_on_error() && last_exit_code != 0 { - ExecuteResult::Exit(last_exit_code, async_handles) + ExecuteResult::Exit(last_exit_code, changes, async_handles) } else { ExecuteResult::Continue(last_exit_code, changes, async_handles) } @@ -1077,7 +1084,8 @@ async fn execute_pipe_sequence( let mut changes: Vec = changes.into_iter().flatten().collect(); match last_result { - ExecuteResult::Exit(code, mut handles) => { + ExecuteResult::Exit(code, env_changes, mut handles) => { + changes.extend(env_changes); handles.extend(all_handles); ExecuteResult::Continue(code, changes, handles) } @@ -1108,9 +1116,9 @@ async fn execute_subshell( .await; match result { - ExecuteResult::Exit(code, handles) => { + ExecuteResult::Exit(code, env_changes, handles) => { // sub shells do not cause an exit - ExecuteResult::Continue(code, Vec::new(), handles) + ExecuteResult::Continue(code, env_changes, handles) } ExecuteResult::Continue(code, env_changes, handles) => { // env changes are not propagated @@ -1155,8 +1163,9 @@ async fn execute_if_clause( ) .await; match exec_result { - ExecuteResult::Exit(code, handles) => { - return ExecuteResult::Exit(code, handles); + ExecuteResult::Exit(code, env_changes, handles) => { + changes.extend(env_changes); + return ExecuteResult::Exit(code, changes, handles); } ExecuteResult::Continue(code, env_changes, handles) => { changes.extend(env_changes); @@ -1186,8 +1195,11 @@ async fn execute_if_clause( ) .await; match exec_result { - ExecuteResult::Exit(code, handles) => { - return ExecuteResult::Exit(code, handles); + ExecuteResult::Exit(code, env_changes, handles) => { + changes.extend(env_changes); + return ExecuteResult::Exit( + code, changes, handles, + ); } ExecuteResult::Continue( code, @@ -1438,8 +1450,9 @@ async fn execute_simple_command( let result = execute_command_args(args, state, stdin, stdout, stderr).await; match result { - ExecuteResult::Exit(code, handles) => { - ExecuteResult::Exit(code, handles) + ExecuteResult::Exit(code, env_changes, handles) => { + changes.extend(env_changes); + ExecuteResult::Exit(code, changes, handles) } ExecuteResult::Continue(code, env_changes, handles) => { changes.extend(env_changes); diff --git a/crates/deno_task_shell/src/shell/types.rs b/crates/deno_task_shell/src/shell/types.rs index 91025f6..aa4eb95 100644 --- a/crates/deno_task_shell/src/shell/types.rs +++ b/crates/deno_task_shell/src/shell/types.rs @@ -389,13 +389,13 @@ pub const CANCELLATION_EXIT_CODE: i32 = 130; #[derive(Debug)] pub enum ExecuteResult { - Exit(i32, Vec>), + Exit(i32, Vec, Vec>), Continue(i32, Vec, Vec>), } impl ExecuteResult { pub fn for_cancellation() -> ExecuteResult { - ExecuteResult::Exit(CANCELLATION_EXIT_CODE, Vec::new()) + ExecuteResult::Exit(CANCELLATION_EXIT_CODE, Vec::new(), Vec::new()) } pub fn from_exit_code(exit_code: i32) -> ExecuteResult { @@ -404,7 +404,7 @@ impl ExecuteResult { pub fn into_exit_code_and_handles(self) -> (i32, Vec>) { match self { - ExecuteResult::Exit(code, handles) => (code, handles), + ExecuteResult::Exit(code, _, handles) => (code, handles), ExecuteResult::Continue(code, _, handles) => (code, handles), } } @@ -415,7 +415,7 @@ impl ExecuteResult { pub fn into_changes(self) -> Vec { match self { - ExecuteResult::Exit(_, _) => Vec::new(), + ExecuteResult::Exit(_, changes, _) => changes, ExecuteResult::Continue(_, changes, _) => changes, } } @@ -424,10 +424,17 @@ impl ExecuteResult { self, ) -> (Vec>, Vec) { match self { - ExecuteResult::Exit(_, handles) => (handles, Vec::new()), + ExecuteResult::Exit(_, changes, handles) => (handles, changes), ExecuteResult::Continue(_, changes, handles) => (handles, changes), } } + + pub fn exit_code(&self) -> i32 { + match self { + ExecuteResult::Exit(code, _, _) => *code, + ExecuteResult::Continue(code, _, _) => *code, + } + } } /// Reader side of a pipe. diff --git a/crates/shell/src/commands/set.rs b/crates/shell/src/commands/set.rs index b6ffb09..0e01fb0 100644 --- a/crates/shell/src/commands/set.rs +++ b/crates/shell/src/commands/set.rs @@ -17,7 +17,7 @@ impl ShellCommand for SetCommand { Ok((code, env_changes)) => ExecuteResult::Continue(code, env_changes, Vec::new()), Err(err) => { context.stderr.write_line(&format!("set: {err}")).unwrap(); - ExecuteResult::Exit(2, Vec::new()) + ExecuteResult::Exit(2, Vec::new(), Vec::new()) } }; Box::pin(futures::future::ready(result)) diff --git a/crates/shell/src/execute.rs b/crates/shell/src/execute.rs index 2683615..5bbda7e 100644 --- a/crates/shell/src/execute.rs +++ b/crates/shell/src/execute.rs @@ -20,7 +20,7 @@ pub async fn execute_inner( stderr.write_all(format!("Filename: {:?}\n", filename).as_bytes())?; } stderr.write_all(format!("Syntax error: {:?}", e).as_bytes())?; - return Ok(ExecuteResult::Exit(1, vec![])); + return Ok(ExecuteResult::Exit(1, vec![], vec![])); } // spawn a sequential list and pipe its output to the environment @@ -41,18 +41,18 @@ pub async fn execute( text: &str, filename: Option, state: &mut ShellState, -) -> miette::Result { +) -> miette::Result { let result = execute_inner(text, filename, state.clone()).await?; - match result { - ExecuteResult::Continue(exit_code, changes, _) => { - // set CWD to the last command's CWD - state.apply_changes(&changes); - std::env::set_current_dir(state.cwd()) - .into_diagnostic() - .context("Failed to set CWD")?; - Ok(exit_code) - } - ExecuteResult::Exit(exit_code, _) => Ok(exit_code), - } + let changes = match &result { + ExecuteResult::Exit(_, changes, _) => changes, + ExecuteResult::Continue(_, changes, _) => changes, + }; + // set CWD to the last command's CWD + state.apply_changes(changes); + std::env::set_current_dir(state.cwd()) + .into_diagnostic() + .context("Failed to set CWD")?; + + Ok(result) } diff --git a/crates/shell/src/main.rs b/crates/shell/src/main.rs index 502b438..336fa86 100644 --- a/crates/shell/src/main.rs +++ b/crates/shell/src/main.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use clap::Parser; use deno_task_shell::parser::debug_parse; +use deno_task_shell::ExecuteResult; use deno_task_shell::ShellState; use miette::Context; use miette::IntoDiagnostic; @@ -73,14 +74,14 @@ async fn init_state(norc: bool, var_args: &[String]) -> miette::Result, norc: bool, args: &[String]) -> rl.add_history_entry(line.as_str()).into_diagnostic()?; // Process the input (here we just echo it back) - let prev_exit_code = execute(&line, None, &mut state) + let result = execute(&line, None, &mut state) .await .context("Failed to execute")?; - state.set_last_command_exit_code(prev_exit_code); + state.set_last_command_exit_code(result.exit_code()); - // Check for exit command - if line.trim().eq_ignore_ascii_case("exit") { - println!("Exiting..."); - break; + if let ExecuteResult::Exit(exit_code, _, _) = result { + std::process::exit(exit_code); } } Err(ReadlineError::Interrupted) => { @@ -235,13 +234,13 @@ async fn main() -> miette::Result<()> { return Ok(()); } - let exit_code = execute(&script_text, filename, &mut state).await?; + let result = execute(&script_text, filename, &mut state).await?; if options.interact { interactive(Some(state), options.norc, &options.args).await?; } - std::process::exit(exit_code); + std::process::exit(result.exit_code()); } } } diff --git a/crates/tests/test-data/source.sh b/crates/tests/test-data/source.sh new file mode 100644 index 0000000..64205e5 --- /dev/null +++ b/crates/tests/test-data/source.sh @@ -0,0 +1,2 @@ +> export FOO='TESTVALUE' && source $CARGO_MANIFEST_DIR/../../scripts/exit.sh && echo $FOO + diff --git a/scripts/exit.sh b/scripts/exit.sh new file mode 100644 index 0000000..80517f1 --- /dev/null +++ b/scripts/exit.sh @@ -0,0 +1,2 @@ +echo "Hello World" +exit \ No newline at end of file