Skip to content

Commit

Permalink
Require set fallback := true to enable recipe fallback (#1368)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey committed Oct 20, 2022
1 parent ca614ad commit 28be873
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 10 deletions.
1 change: 1 addition & 0 deletions GRAMMAR.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export : 'export' assignment
setting : 'set' 'dotenv-load' boolean?
| 'set' 'ignore-comments' boolean?
| 'set' 'export' boolean?
| 'set' 'fallback' boolean?
| 'set' 'positional-arguments' boolean?
| 'set' 'allow-duplicate-recipes' boolean?
| 'set' 'windows-powershell' boolean?
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ foo:
| `allow-duplicate-recipes` | boolean | False | Allow recipes appearing later in a `justfile` to override earlier recipes with the same name. |
| `dotenv-load` | boolean | False | Load a `.env` file, if present. |
| `export` | boolean | False | Export all variables as environment variables. |
| `fallback` | boolean | False | Search `justfile` in parent directory if the first recipe on the command line is not found. |
| `ignore-comments` | boolean | False | Ignore recipe lines beginning with `#`. |
| `positional-arguments` | boolean | False | Pass positional arguments. |
| `shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
Expand Down Expand Up @@ -2063,15 +2064,18 @@ The `--dump` command can be used with `--dump-format json` to print a JSON repre

### Falling back to parent `justfile`s

If a recipe is not found, `just` will look for `justfile`s in the parent
directory and up, until it reaches the root directory.
If a recipe is not found in a `justfile` and the `fallback` setting is set,
`just` will look for `justfile`s in the parent directory and up, until it
reaches the root directory. `just` will stop after it reaches a `justfile` in
which the `fallback` setting is `false` or unset.

This feature is currently unstable, and so must be enabled with the
`--unstable` flag.

As an example, suppose the current directory contains this `justfile`:

```make
set fallback
foo:
echo foo
```
Expand Down
3 changes: 3 additions & 0 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ impl<'src> Analyzer<'src> {
Setting::Export(export) => {
settings.export = export;
}
Setting::Fallback(fallback) => {
settings.fallback = fallback;
}
Setting::IgnoreComments(ignore_comments) => {
settings.ignore_comments = ignore_comments;
}
Expand Down
1 change: 1 addition & 0 deletions src/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) enum Keyword {
DotenvLoad,
Else,
Export,
Fallback,
False,
If,
IgnoreComments,
Expand Down
1 change: 1 addition & 0 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ impl<'src> Node<'src> for Set<'src> {
Setting::AllowDuplicateRecipes(value)
| Setting::DotenvLoad(value)
| Setting::Export(value)
| Setting::Fallback(value)
| Setting::PositionalArguments(value)
| Setting::WindowsPowerShell(value)
| Setting::IgnoreComments(value) => {
Expand Down
3 changes: 2 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,8 +761,9 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
Some(Setting::AllowDuplicateRecipes(self.parse_set_bool()?))
}
Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)),
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)),
Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)),
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)),
Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)),
_ => None,
Expand Down
6 changes: 4 additions & 2 deletions src/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use super::*;
pub(crate) enum Setting<'src> {
AllowDuplicateRecipes(bool),
DotenvLoad(bool),
IgnoreComments(bool),
Export(bool),
Fallback(bool),
IgnoreComments(bool),
PositionalArguments(bool),
Shell(Shell<'src>),
WindowsPowerShell(bool),
Expand All @@ -17,8 +18,9 @@ impl<'src> Display for Setting<'src> {
match self {
Setting::AllowDuplicateRecipes(value)
| Setting::DotenvLoad(value)
| Setting::IgnoreComments(value)
| Setting::Export(value)
| Setting::Fallback(value)
| Setting::IgnoreComments(value)
| Setting::PositionalArguments(value)
| Setting::WindowsPowerShell(value) => write!(f, "{}", value),
Setting::Shell(shell) | Setting::WindowsShell(shell) => write!(f, "{}", shell),
Expand Down
1 change: 1 addition & 0 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) struct Settings<'src> {
pub(crate) allow_duplicate_recipes: bool,
pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool,
pub(crate) fallback: bool,
pub(crate) ignore_comments: bool,
pub(crate) positional_arguments: bool,
pub(crate) shell: Option<Shell<'src>>,
Expand Down
14 changes: 9 additions & 5 deletions src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ impl Subcommand {
};

match Self::run_inner(config, loader, arguments, overrides, &search) {
Err(err @ Error::UnknownRecipes { .. }) => {
Err((err @ Error::UnknownRecipes { .. }, true)) => {
match search.justfile.parent().unwrap().parent() {
Some(parent) => {
unknown_recipes_errors.get_or_insert(err);
Expand All @@ -144,7 +144,7 @@ impl Subcommand {
None => return Err(err),
}
}
result => return result,
result => return result.map_err(|(err, _fallback)| err),
}
}
} else {
Expand All @@ -155,6 +155,7 @@ impl Subcommand {
overrides,
&Search::find(&config.search_config, &config.invocation_directory)?,
)
.map_err(|(err, _fallback)| err)
}
}

Expand All @@ -164,9 +165,12 @@ impl Subcommand {
arguments: &[String],
overrides: &BTreeMap<String, String>,
search: &Search,
) -> Result<(), Error<'src>> {
let (_src, _ast, justfile) = Self::compile(config, loader, search)?;
justfile.run(config, search, overrides, arguments)
) -> Result<(), (Error<'src>, bool)> {
let (_src, _ast, justfile) =
Self::compile(config, loader, search).map_err(|err| (err, false))?;
justfile
.run(config, search, overrides, arguments)
.map_err(|err| (err, justfile.settings.fallback))
}

fn compile<'src>(
Expand Down
155 changes: 155 additions & 0 deletions tests/fall_back_to_parent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,40 @@ fn runs_recipe_in_parent_if_not_found_in_current() {
.tree(tree! {
bar: {
justfile: "
set fallback
baz:
echo subdir
"
}
})
.justfile(
"
foo:
echo root
",
)
.args(&["--unstable", "foo"])
.current_dir("bar")
.stderr(format!(
"
Trying ..{}justfile
echo root
",
MAIN_SEPARATOR
))
.stdout("root\n")
.run();
}

#[test]
fn setting_accepts_value() {
Test::new()
.tree(tree! {
bar: {
justfile: "
set fallback := true
baz:
echo subdir
"
Expand Down Expand Up @@ -36,6 +70,8 @@ fn print_error_from_parent_if_recipe_not_found_in_current() {
.tree(tree! {
bar: {
justfile: "
set fallback
baz:
echo subdir
"
Expand Down Expand Up @@ -64,6 +100,8 @@ fn requires_unstable() {
.tree(tree! {
bar: {
justfile: "
set fallback
baz:
echo subdir
"
Expand All @@ -82,19 +120,47 @@ fn requires_unstable() {
.run();
}

#[test]
fn requires_setting() {
Test::new()
.tree(tree! {
bar: {
justfile: "
baz:
echo subdir
"
}
})
.justfile(
"
foo:
echo root
",
)
.args(&["--unstable", "foo"])
.current_dir("bar")
.status(EXIT_FAILURE)
.stderr("error: Justfile does not contain recipe `foo`.\n")
.run();
}

#[test]
fn works_with_provided_search_directory() {
Test::new()
.tree(tree! {
bar: {
justfile: "
set fallback
baz:
echo subdir
"
}
})
.justfile(
"
set fallback
foo:
echo root
",
Expand All @@ -118,13 +184,17 @@ fn doesnt_work_with_justfile() {
.tree(tree! {
bar: {
justfile: "
set fallback
baz:
echo subdir
"
}
})
.justfile(
"
set fallback
foo:
echo root
",
Expand All @@ -142,13 +212,17 @@ fn doesnt_work_with_justfile_and_working_directory() {
.tree(tree! {
bar: {
justfile: "
set fallback
baz:
echo subdir
"
}
})
.justfile(
"
set fallback
foo:
echo root
",
Expand All @@ -173,6 +247,8 @@ fn prints_correct_error_message_when_recipe_not_found() {
.tree(tree! {
bar: {
justfile: "
set fallback
bar:
echo subdir
"
Expand All @@ -196,3 +272,82 @@ fn prints_correct_error_message_when_recipe_not_found() {
))
.run();
}

#[test]
fn multiple_levels_of_fallback_work() {
Test::new()
.tree(tree! {
a: {
b: {
justfile: "
set fallback
foo:
echo subdir
"
},
justfile: "
set fallback
bar:
echo subdir
"
}
})
.justfile(
"
baz:
echo root
",
)
.args(&["--unstable", "baz"])
.current_dir("a/b")
.stdout("root\n")
.stderr(format!(
"
Trying ..{}justfile
Trying ..{}..{}justfile
echo root
",
MAIN_SEPARATOR, MAIN_SEPARATOR, MAIN_SEPARATOR
))
.run();
}

#[test]
fn stop_fallback_when_fallback_is_false() {
Test::new()
.tree(tree! {
a: {
b: {
justfile: "
set fallback
foo:
echo subdir
"
},
justfile: "
bar:
echo subdir
"
}
})
.justfile(
"
baz:
echo root
",
)
.args(&["--unstable", "baz"])
.current_dir("a/b")
.stderr(format!(
"
Trying ..{}justfile
error: Justfile does not contain recipe `baz`.
Did you mean `bar`?
",
MAIN_SEPARATOR
))
.status(EXIT_FAILURE)
.run();
}
Loading

0 comments on commit 28be873

Please sign in to comment.