diff --git a/GRAMMAR.md b/GRAMMAR.md
index 920ad84..4c4a25c 100644
--- a/GRAMMAR.md
+++ b/GRAMMAR.md
@@ -69,6 +69,7 @@ setting : 'set' 'allow-duplicate-recipes' boolean?
| 'set' 'fallback' boolean?
| 'set' 'ignore-comments' boolean?
| 'set' 'positional-arguments' boolean?
+ | 'set' 'quiet' boolean?
| 'set' 'shell' ':=' '[' string (',' string)* ','? ']'
| 'set' 'tempdir ':=' string
| 'set' 'windows-powershell' boolean?
diff --git a/README.md b/README.md
index c854c56..0020dab 100644
--- a/README.md
+++ b/README.md
@@ -1460,6 +1460,7 @@ Recipes may be annotated with attributes that change their behavior.
| `[macos]`1.8.0 | Enable recipe on MacOS. |
| `[no-cd]`1.9.0 | Don't change directory before executing recipe. |
| `[no-exit-message]`1.7.0 | Don't print an error message if recipe fails. |
+| `[no-quiet]`master | Override globally quiet recipes and always echo out the recipe. |
| `[private]`1.10.0 | See [Private Recipes](#private-recipes). |
| `[unix]`1.8.0 | Enable recipe on Unixes. (Includes MacOS). |
| `[windows]`1.8.0 | Enable recipe on Windows. |
@@ -2496,6 +2497,31 @@ goodbye
# all done!
```
+All recipes in a Justfile can be made quiet with `set quiet`:
+
+```just
+set quiet
+
+foo:
+ echo "This is quiet"
+
+@foo2:
+ echo "This is also quiet"
+```
+
+The `[no-quiet]` attribute overrides this setting:
+
+```just
+set quiet
+
+foo:
+ echo "This is quiet"
+
+[no-quiet]
+foo2:
+ echo "This is not quiet"
+```
+
Shebang recipes are quiet by default:
```just
diff --git a/src/attribute.rs b/src/attribute.rs
index b4c7166..0c361e3 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -12,6 +12,7 @@ pub(crate) enum Attribute {
NoCd,
NoExitMessage,
Private,
+ NoQuiet,
Unix,
Windows,
}
diff --git a/src/keyword.rs b/src/keyword.rs
index 8bde1a8..830207e 100644
--- a/src/keyword.rs
+++ b/src/keyword.rs
@@ -17,6 +17,7 @@ pub(crate) enum Keyword {
Import,
Mod,
PositionalArguments,
+ Quiet,
Set,
Shell,
Tempdir,
diff --git a/src/node.rs b/src/node.rs
index 77309d6..1d98b99 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -269,6 +269,7 @@ impl<'src> Node<'src> for Set<'src> {
| Setting::Export(value)
| Setting::Fallback(value)
| Setting::PositionalArguments(value)
+ | Setting::Quiet(value)
| Setting::WindowsPowerShell(value)
| Setting::IgnoreComments(value) => {
set.push_mut(value.to_string());
diff --git a/src/parser.rs b/src/parser.rs
index 6608c5f..3f7deda 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -852,6 +852,7 @@ impl<'run, 'src> Parser<'run, 'src> {
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::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)),
Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)),
_ => None,
};
@@ -1927,6 +1928,24 @@ mod tests {
tree: (justfile (set positional_arguments true)),
}
+ test! {
+ name: set_quiet_implicit,
+ text: "set quiet",
+ tree: (justfile (set quiet true)),
+ }
+
+ test! {
+ name: set_quiet_true,
+ text: "set quiet := true",
+ tree: (justfile (set quiet true)),
+ }
+
+ test! {
+ name: set_quiet_false,
+ text: "set quiet := false",
+ tree: (justfile (set quiet false)),
+ }
+
test! {
name: set_positional_arguments_false,
text: "set positional-arguments := false",
diff --git a/src/recipe.rs b/src/recipe.rs
index 99f90e1..bc06cf4 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -134,6 +134,10 @@ impl<'src, D> Recipe<'src, D> {
}
}
+ fn no_quiet(&self) -> bool {
+ self.attributes.contains(&Attribute::NoQuiet)
+ }
+
pub(crate) fn run<'run>(
&self,
context: &RecipeContext<'src, 'run>,
@@ -181,8 +185,8 @@ impl<'src, D> Recipe<'src, D> {
}
let mut evaluated = String::new();
let mut continued = false;
- let quiet_command = lines.peek().map_or(false, |line| line.is_quiet());
- let infallible_command = lines.peek().map_or(false, |line| line.is_infallible());
+ let quiet_line = lines.peek().map_or(false, |line| line.is_quiet());
+ let infallible_line = lines.peek().map_or(false, |line| line.is_infallible());
let comment_line =
context.settings.ignore_comments && lines.peek().map_or(false, |line| line.is_comment());
@@ -210,7 +214,7 @@ impl<'src, D> Recipe<'src, D> {
let mut command = evaluated.as_str();
- let sigils = usize::from(infallible_command) + usize::from(quiet_command);
+ let sigils = usize::from(infallible_line) + usize::from(quiet_line);
command = &command[sigils..];
@@ -220,7 +224,9 @@ impl<'src, D> Recipe<'src, D> {
if config.dry_run
|| config.verbosity.loquacious()
- || !((quiet_command ^ self.quiet) || config.verbosity.quiet())
+ || !((quiet_line ^ self.quiet)
+ || (context.settings.quiet && !self.no_quiet())
+ || config.verbosity.quiet())
{
let color = if config.highlight {
config.color.command(config.command_color)
@@ -257,7 +263,7 @@ impl<'src, D> Recipe<'src, D> {
match InterruptHandler::guard(|| cmd.status()) {
Ok(exit_status) => {
if let Some(code) = exit_status.code() {
- if code != 0 && !infallible_command {
+ if code != 0 && !infallible_line {
return Err(Error::Code {
recipe: self.name(),
line_number: Some(line_number),
diff --git a/src/setting.rs b/src/setting.rs
index 9b2a9fe..0256ff9 100644
--- a/src/setting.rs
+++ b/src/setting.rs
@@ -10,6 +10,7 @@ pub(crate) enum Setting<'src> {
Fallback(bool),
IgnoreComments(bool),
PositionalArguments(bool),
+ Quiet(bool),
Shell(Shell<'src>),
Tempdir(String),
WindowsPowerShell(bool),
@@ -25,6 +26,7 @@ impl<'src> Display for Setting<'src> {
| Setting::Fallback(value)
| Setting::IgnoreComments(value)
| Setting::PositionalArguments(value)
+ | Setting::Quiet(value)
| Setting::WindowsPowerShell(value) => write!(f, "{value}"),
Setting::Shell(shell) | Setting::WindowsShell(shell) => write!(f, "{shell}"),
Setting::DotenvFilename(value) | Setting::DotenvPath(value) | Setting::Tempdir(value) => {
diff --git a/src/settings.rs b/src/settings.rs
index 5c5e64c..f72cb54 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -15,6 +15,7 @@ pub(crate) struct Settings<'src> {
pub(crate) fallback: bool,
pub(crate) ignore_comments: bool,
pub(crate) positional_arguments: bool,
+ pub(crate) quiet: bool,
pub(crate) shell: Option>,
pub(crate) tempdir: Option,
pub(crate) windows_powershell: bool,
@@ -51,6 +52,9 @@ impl<'src> Settings<'src> {
Setting::PositionalArguments(positional_arguments) => {
settings.positional_arguments = positional_arguments;
}
+ Setting::Quiet(quiet) => {
+ settings.quiet = quiet;
+ }
Setting::Shell(shell) => {
settings.shell = Some(shell);
}
diff --git a/tests/json.rs b/tests/json.rs
index c70063f..4c71b43 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -50,6 +50,7 @@ fn alias() {
"export": false,
"fallback": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"ignore_comments": false,
@@ -86,6 +87,7 @@ fn assignment() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -136,6 +138,7 @@ fn body() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -198,6 +201,7 @@ fn dependencies() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -297,6 +301,7 @@ fn dependency_argument() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -359,6 +364,7 @@ fn duplicate_recipes() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -402,6 +408,7 @@ fn doc_comment() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -431,6 +438,7 @@ fn empty_justfile() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -581,6 +589,7 @@ fn parameters() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -664,6 +673,7 @@ fn priors() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -707,6 +717,7 @@ fn private() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -750,6 +761,7 @@ fn quiet() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"windows_powershell": false,
@@ -770,6 +782,7 @@ fn settings() {
set export
set fallback
set positional-arguments
+ set quiet
set ignore-comments
set shell := ['a', 'b', 'c']
foo:
@@ -804,6 +817,7 @@ fn settings() {
"fallback": true,
"ignore_comments": true,
"positional_arguments": true,
+ "quiet": true,
"shell": {
"arguments": ["b", "c"],
"command": "a",
@@ -853,6 +867,7 @@ fn shebang() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir": null,
"windows_powershell": false,
@@ -896,6 +911,7 @@ fn simple() {
"fallback": false,
"ignore_comments": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir": null,
"windows_powershell": false,
@@ -941,6 +957,7 @@ fn attribute() {
"export": false,
"fallback": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"ignore_comments": false,
@@ -1000,6 +1017,7 @@ fn module() {
"export": false,
"fallback": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"ignore_comments": false,
@@ -1018,6 +1036,7 @@ fn module() {
"export": false,
"fallback": false,
"positional_arguments": false,
+ "quiet": false,
"shell": null,
"tempdir" : null,
"ignore_comments": false,
diff --git a/tests/quiet.rs b/tests/quiet.rs
index 8fc0e4f..59ad41e 100644
--- a/tests/quiet.rs
+++ b/tests/quiet.rs
@@ -129,3 +129,111 @@ test! {
",
args: ("--quiet"),
}
+
+#[test]
+fn no_quiet_setting() {
+ Test::new()
+ .justfile(
+ "
+ foo:
+ echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .stderr("echo FOO\n")
+ .run();
+}
+
+#[test]
+fn quiet_setting() {
+ Test::new()
+ .justfile(
+ "
+ set quiet
+
+ foo:
+ echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .run();
+}
+
+#[test]
+fn quiet_setting_with_no_quiet_attribute() {
+ Test::new()
+ .justfile(
+ "
+ set quiet
+
+ [no-quiet]
+ foo:
+ echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .stderr("echo FOO\n")
+ .run();
+}
+
+#[test]
+fn quiet_setting_with_quiet_recipe() {
+ Test::new()
+ .justfile(
+ "
+ set quiet
+
+ @foo:
+ echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .run();
+}
+
+#[test]
+fn quiet_setting_with_quiet_line() {
+ Test::new()
+ .justfile(
+ "
+ set quiet
+
+ foo:
+ @echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .run();
+}
+
+#[test]
+fn quiet_setting_with_no_quiet_attribute_and_quiet_recipe() {
+ Test::new()
+ .justfile(
+ "
+ set quiet
+
+ [no-quiet]
+ @foo:
+ echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .run();
+}
+
+#[test]
+fn quiet_setting_with_no_quiet_attribute_and_quiet_line() {
+ Test::new()
+ .justfile(
+ "
+ set quiet
+
+ [no-quiet]
+ foo:
+ @echo FOO
+ ",
+ )
+ .stdout("FOO\n")
+ .run();
+}