diff --git a/src/evaluator.rs b/src/evaluator.rs index 5cb6cec876..da18ca149e 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -689,6 +689,8 @@ impl<'src, 'run> Evaluator<'src, 'run> { command: &str, args: Option<&[String]>, ) -> Result { + assert!(!context.config.dry_run); + let mut cmd = context.module.settings.shell_command(context.config); cmd.arg(command); diff --git a/src/function.rs b/src/function.rs index 691695e480..1188f036be 100644 --- a/src/function.rs +++ b/src/function.rs @@ -634,6 +634,22 @@ fn sha256_file(context: Context, path: &str) -> StringResult { } fn shell(context: Context, command: &str, args: &[String]) -> StringResult { + if context.execution_context.config.dry_run { + let mut output = String::from("shell("); + for (i, arg) in iter::once(command) + .chain(args.iter().map(String::as_str)) + .enumerate() + { + if i > 0 { + output.push_str(", "); + } + output.push_str(&Element(arg).color_display(Color::never()).to_string()); + } + output.push(')'); + + return Ok(output); + } + Evaluator::run_command( context.execution_context, &BTreeMap::new(), diff --git a/tests/functions.rs b/tests/functions.rs index eb19383718..a7815aa2c0 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -1636,3 +1636,35 @@ fn num_jobs_requires_lists() { ) .failure(); } + +#[test] +fn dry_run_does_not_execute_shell_function() { + Test::new() + .justfile( + " + foo: + echo {{ shell('exit 1') }} + ", + ) + .arg("--dry-run") + .stderr("echo shell(\"exit 1\")\n") + .success(); +} + +#[test] +fn dry_run_shell_function_output_is_escaped() { + Test::new() + .justfile( + r#" + foo: + echo {{ shell('exit 1', '"') }} + "#, + ) + .arg("--dry-run") + .stderr( + r#" + echo shell("exit 1", "\"") + "#, + ) + .success(); +}