diff --git a/README.md b/README.md
index 7600c83..cfd6772 100644
--- a/README.md
+++ b/README.md
@@ -1203,13 +1203,14 @@ These functions can fail, for example if a path does not have an extension, whic
Recipes may be annotated with attributes that change their behavior.
-| Name | Description |
-| ------------------- | ------------------------------------------------- |
-| `[no-exit-message]` | Don't print an error message when a recipe fails. |
-| `[linux]` | Enable recipe on Linux. |
-| `[macos]` | Enable recipe on MacOS. |
-| `[unix]` | Enable recipe on Unixes. |
-| `[windows]` | Enable recipe on Windows. |
+| Name | Description |
+| ------------------- | ----------------------------------------------- |
+| `[no-cd]` | Don't change directory before executing recipe. |
+| `[no-exit-message]` | Don't print an error message if recipe fails. |
+| `[linux]` | Enable recipe on Linux. |
+| `[macos]` | Enable recipe on MacOS. |
+| `[unix]` | Enable recipe on Unixes. |
+| `[windows]` | Enable recipe on Windows. |
#### Enabling and Disabling Recipes
@@ -1235,6 +1236,27 @@ run:
main.exe
```
+#### Disabling Changing Directorymaster
+
+`just` normally executes recipes with the current directory set to the
+directory that contains the `justfile`. This can be disabled using the
+`[no-cd]` attribute. This can be used to create recipes which use paths
+relative to the invocation directory, or which operate on the current
+directory.
+
+For exmaple, this `commit` recipe:
+
+```make
+[no-cd]
+commit file:
+ git add {{file}}
+ git commit
+```
+
+Can be used with paths that are relative to the current directory, because
+`[no-cd]` prevents `just` from changing the current directory when executing
+`commit`.
+
### Command Evaluation Using Backticks
Backticks can be used to store the result of commands:
diff --git a/src/attribute.rs b/src/attribute.rs
index 375f2bb..c205f34 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -8,6 +8,7 @@ use super::*;
pub(crate) enum Attribute {
Linux,
Macos,
+ NoCd,
NoExitMessage,
Unix,
Windows,
diff --git a/src/platform.rs b/src/platform.rs
index 965fac2..9fd9b16 100644
--- a/src/platform.rs
+++ b/src/platform.rs
@@ -6,13 +6,15 @@ pub(crate) struct Platform;
impl PlatformInterface for Platform {
fn make_shebang_command(
path: &Path,
- working_directory: &Path,
+ working_directory: Option<&Path>,
_shebang: Shebang,
) -> Result {
// shebang scripts can be executed directly on unix
let mut cmd = Command::new(path);
- cmd.current_dir(working_directory);
+ if let Some(working_directory) = working_directory {
+ cmd.current_dir(working_directory);
+ }
Ok(cmd)
}
@@ -48,7 +50,7 @@ impl PlatformInterface for Platform {
impl PlatformInterface for Platform {
fn make_shebang_command(
path: &Path,
- working_directory: &Path,
+ working_directory: Option<&Path>,
shebang: Shebang,
) -> Result {
use std::borrow::Cow;
@@ -57,7 +59,9 @@ impl PlatformInterface for Platform {
let command = if shebang.interpreter.contains('/') {
// …translate path to the interpreter from unix style to windows style.
let mut cygpath = Command::new("cygpath");
- cygpath.current_dir(working_directory);
+ if let Some(working_directory) = working_directory {
+ cygpath.current_dir(working_directory);
+ }
cygpath.arg("--windows");
cygpath.arg(shebang.interpreter);
@@ -69,7 +73,9 @@ impl PlatformInterface for Platform {
let mut cmd = Command::new(command.as_ref());
- cmd.current_dir(working_directory);
+ if let Some(working_directory) = working_directory {
+ cmd.current_dir(working_directory);
+ }
if let Some(argument) = shebang.argument {
cmd.arg(argument);
diff --git a/src/platform_interface.rs b/src/platform_interface.rs
index fcab222..0028e09 100644
--- a/src/platform_interface.rs
+++ b/src/platform_interface.rs
@@ -5,7 +5,7 @@ pub(crate) trait PlatformInterface {
/// shebang line `shebang`
fn make_shebang_command(
path: &Path,
- working_directory: &Path,
+ working_directory: Option<&Path>,
shebang: Shebang,
) -> Result;
diff --git a/src/recipe.rs b/src/recipe.rs
index 7da2a61..5fda449 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -66,6 +66,10 @@ impl<'src, D> Recipe<'src, D> {
!self.private
}
+ pub(crate) fn change_directory(&self) -> bool {
+ !self.attributes.contains(&Attribute::NoCd)
+ }
+
pub(crate) fn enabled(&self) -> bool {
let windows = self.attributes.contains(&Attribute::Windows);
let linux = self.attributes.contains(&Attribute::Linux);
@@ -190,7 +194,9 @@ impl<'src, D> Recipe<'src, D> {
let mut cmd = context.settings.shell_command(config);
- cmd.current_dir(&context.search.working_directory);
+ if self.change_directory() {
+ cmd.current_dir(&context.search.working_directory);
+ }
cmd.arg(command);
@@ -322,13 +328,19 @@ impl<'src, D> Recipe<'src, D> {
})?;
// create a command to run the script
- let mut command =
- Platform::make_shebang_command(&path, &context.search.working_directory, shebang).map_err(
- |output_error| Error::Cygpath {
- recipe: self.name(),
- output_error,
- },
- )?;
+ let mut command = Platform::make_shebang_command(
+ &path,
+ if self.change_directory() {
+ Some(&context.search.working_directory)
+ } else {
+ None
+ },
+ shebang,
+ )
+ .map_err(|output_error| Error::Cygpath {
+ recipe: self.name(),
+ output_error,
+ })?;
if context.settings.positional_arguments {
command.args(positional);
diff --git a/tests/lib.rs b/tests/lib.rs
index 7a999fe..e6c7ed4 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -60,6 +60,7 @@ mod json;
mod line_prefixes;
mod misc;
mod multibyte_char;
+mod no_cd;
mod no_exit_message;
mod os_attributes;
mod parser;
diff --git a/tests/no_cd.rs b/tests/no_cd.rs
new file mode 100644
index 0000000..ecdc006
--- /dev/null
+++ b/tests/no_cd.rs
@@ -0,0 +1,43 @@
+use super::*;
+
+#[test]
+fn linewise() {
+ Test::new()
+ .justfile(
+ "
+ [no-cd]
+ foo:
+ cat bar
+ ",
+ )
+ .current_dir("foo")
+ .tree(tree! {
+ foo: {
+ bar: "hello",
+ }
+ })
+ .stderr("cat bar\n")
+ .stdout("hello")
+ .run();
+}
+
+#[test]
+fn shebang() {
+ Test::new()
+ .justfile(
+ "
+ [no-cd]
+ foo:
+ #!/bin/sh
+ cat bar
+ ",
+ )
+ .current_dir("foo")
+ .tree(tree! {
+ foo: {
+ bar: "hello",
+ }
+ })
+ .stdout("hello")
+ .run();
+}