Add [no-exit-message] recipe annotation (#1354)
When a recipe wraps cli tool and the tool exits with a non-zero code, just adds its own extra exit error message along with the messages from the tool. Introduce the `[no-exit-message]` attribute to suppress this additional message.
This commit is contained in:
parent
dc9f458937
commit
8b7640b633
@ -94,7 +94,9 @@ string : STRING
|
||||
sequence : expression ',' sequence
|
||||
| expression ','?
|
||||
|
||||
recipe : '@'? NAME parameter* variadic? ':' dependency* body?
|
||||
recipe : attribute? '@'? NAME parameter* variadic? ':' dependency* body?
|
||||
|
||||
attribute : '[' NAME ']' eol
|
||||
|
||||
parameter : '$'? NAME
|
||||
| '$'? NAME '=' value
|
||||
|
29
README.md
29
README.md
@ -1987,6 +1987,35 @@ echo 'Bar!'
|
||||
Bar!
|
||||
```
|
||||
|
||||
`just` normally prints error messages when a recipe line fails. These error
|
||||
messages can be suppressed using the `[no-exit-message]` attribute. You may find
|
||||
this especially useful with a recipe that recipe wraps a tool:
|
||||
|
||||
```make
|
||||
git *args:
|
||||
@git {{args}}
|
||||
```
|
||||
|
||||
```sh
|
||||
$ just git status
|
||||
fatal: not a git repository (or any of the parent directories): .git
|
||||
error: Recipe `git` failed on line 2 with exit code 128
|
||||
```
|
||||
|
||||
Add the attribute to suppress the exit error message when the tool exits with a
|
||||
non-zero code:
|
||||
|
||||
```make
|
||||
[no-exit-message]
|
||||
git *args:
|
||||
@git {{args}}
|
||||
```
|
||||
|
||||
```sh
|
||||
$ just git status
|
||||
fatal: not a git repository (or any of the parent directories): .git
|
||||
```
|
||||
|
||||
### Selecting Recipes to Run With an Interactive Chooser
|
||||
|
||||
The `--choose` subcommand makes `just` invoke a chooser to select which recipes to run. Choosers should read lines containing recipe names from standard input and print one or more of those names separated by spaces to standard output.
|
||||
|
13
src/attribute.rs
Normal file
13
src/attribute.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(EnumString)]
|
||||
#[strum(serialize_all = "kebab_case")]
|
||||
pub(crate) enum Attribute {
|
||||
NoExitMessage,
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub(crate) fn from_name(name: Name) -> Option<Attribute> {
|
||||
name.lexeme().parse().ok()
|
||||
}
|
||||
}
|
@ -247,6 +247,9 @@ impl Display for CompileError<'_> {
|
||||
UnknownAliasTarget { alias, target } => {
|
||||
write!(f, "Alias `{}` has an unknown target `{}`", alias, target)?;
|
||||
}
|
||||
UnknownAttribute { attribute } => {
|
||||
write!(f, "Unknown attribute `{}`", attribute)?;
|
||||
}
|
||||
UnknownDependency { recipe, unknown } => {
|
||||
write!(
|
||||
f,
|
||||
|
@ -98,6 +98,9 @@ pub(crate) enum CompileErrorKind<'src> {
|
||||
alias: &'src str,
|
||||
target: &'src str,
|
||||
},
|
||||
UnknownAttribute {
|
||||
attribute: &'src str,
|
||||
},
|
||||
UnknownDependency {
|
||||
recipe: &'src str,
|
||||
unknown: &'src str,
|
||||
|
12
src/error.rs
12
src/error.rs
@ -35,6 +35,7 @@ pub(crate) enum Error<'src> {
|
||||
recipe: &'src str,
|
||||
line_number: Option<usize>,
|
||||
code: i32,
|
||||
suppress_message: bool,
|
||||
},
|
||||
CommandInvoke {
|
||||
binary: OsString,
|
||||
@ -167,6 +168,16 @@ impl<'src> Error<'src> {
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn suppress_message(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Error::Code {
|
||||
suppress_message: true,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> From<CompileError<'src>> for Error<'src> {
|
||||
@ -323,6 +334,7 @@ impl<'src> ColorDisplay for Error<'src> {
|
||||
recipe,
|
||||
line_number,
|
||||
code,
|
||||
..
|
||||
} => {
|
||||
if let Some(n) = line_number {
|
||||
write!(
|
||||
|
@ -472,11 +472,13 @@ mod tests {
|
||||
recipe,
|
||||
line_number,
|
||||
code,
|
||||
suppress_message,
|
||||
},
|
||||
check: {
|
||||
assert_eq!(recipe, "a");
|
||||
assert_eq!(code, 200);
|
||||
assert_eq!(line_number, None);
|
||||
assert!(!suppress_message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,11 +493,13 @@ mod tests {
|
||||
recipe,
|
||||
line_number,
|
||||
code,
|
||||
suppress_message,
|
||||
},
|
||||
check: {
|
||||
assert_eq!(recipe, "fail");
|
||||
assert_eq!(code, 100);
|
||||
assert_eq!(line_number, Some(2));
|
||||
assert!(!suppress_message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,11 +514,13 @@ mod tests {
|
||||
recipe,
|
||||
line_number,
|
||||
code,
|
||||
suppress_message,
|
||||
},
|
||||
check: {
|
||||
assert_eq!(recipe, "a");
|
||||
assert_eq!(code, 150);
|
||||
assert_eq!(line_number, Some(2));
|
||||
assert!(!suppress_message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -664,13 +670,15 @@ mod tests {
|
||||
"#,
|
||||
args: ["--quiet", "wut"],
|
||||
error: Code {
|
||||
line_number,
|
||||
recipe,
|
||||
line_number,
|
||||
suppress_message,
|
||||
..
|
||||
},
|
||||
check: {
|
||||
assert_eq!(recipe, "wut");
|
||||
assert_eq!(line_number, Some(7));
|
||||
assert!(!suppress_message);
|
||||
}
|
||||
}
|
||||
|
||||
|
27
src/lib.rs
27
src/lib.rs
@ -16,19 +16,19 @@
|
||||
pub(crate) use {
|
||||
crate::{
|
||||
alias::Alias, analyzer::Analyzer, assignment::Assignment,
|
||||
assignment_resolver::AssignmentResolver, ast::Ast, binding::Binding, color::Color,
|
||||
color_display::ColorDisplay, command_ext::CommandExt, compile_error::CompileError,
|
||||
compile_error_kind::CompileErrorKind, conditional_operator::ConditionalOperator,
|
||||
config::Config, config_error::ConfigError, count::Count, delimiter::Delimiter,
|
||||
dependency::Dependency, dump_format::DumpFormat, enclosure::Enclosure, error::Error,
|
||||
evaluator::Evaluator, expression::Expression, fragment::Fragment, function::Function,
|
||||
function_context::FunctionContext, interrupt_guard::InterruptGuard,
|
||||
interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, keyed::Keyed,
|
||||
keyword::Keyword, lexer::Lexer, line::Line, list::List, load_dotenv::load_dotenv,
|
||||
loader::Loader, name::Name, ordinal::Ordinal, output::output, output_error::OutputError,
|
||||
parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, platform::Platform,
|
||||
platform_interface::PlatformInterface, position::Position, positional::Positional,
|
||||
range_ext::RangeExt, recipe::Recipe, recipe_context::RecipeContext,
|
||||
assignment_resolver::AssignmentResolver, ast::Ast, attribute::Attribute, binding::Binding,
|
||||
color::Color, color_display::ColorDisplay, command_ext::CommandExt,
|
||||
compile_error::CompileError, compile_error_kind::CompileErrorKind,
|
||||
conditional_operator::ConditionalOperator, config::Config, config_error::ConfigError,
|
||||
count::Count, delimiter::Delimiter, dependency::Dependency, dump_format::DumpFormat,
|
||||
enclosure::Enclosure, error::Error, evaluator::Evaluator, expression::Expression,
|
||||
fragment::Fragment, function::Function, function_context::FunctionContext,
|
||||
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item,
|
||||
justfile::Justfile, keyed::Keyed, keyword::Keyword, lexer::Lexer, line::Line, list::List,
|
||||
load_dotenv::load_dotenv, loader::Loader, name::Name, ordinal::Ordinal, output::output,
|
||||
output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind, parser::Parser,
|
||||
platform::Platform, platform_interface::PlatformInterface, position::Position,
|
||||
positional::Positional, range_ext::RangeExt, recipe::Recipe, recipe_context::RecipeContext,
|
||||
recipe_resolver::RecipeResolver, scope::Scope, search::Search, search_config::SearchConfig,
|
||||
search_error::SearchError, set::Set, setting::Setting, settings::Settings, shebang::Shebang,
|
||||
shell::Shell, show_whitespace::ShowWhitespace, string_kind::StringKind,
|
||||
@ -116,6 +116,7 @@ mod analyzer;
|
||||
mod assignment;
|
||||
mod assignment_resolver;
|
||||
mod ast;
|
||||
mod attribute;
|
||||
mod binding;
|
||||
mod color;
|
||||
mod color_display;
|
||||
|
@ -345,13 +345,20 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
||||
items.push(Item::Assignment(self.parse_assignment(false)?));
|
||||
} else {
|
||||
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
||||
items.push(Item::Recipe(self.parse_recipe(doc, false)?));
|
||||
items.push(Item::Recipe(self.parse_recipe(doc, false, false)?));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if self.accepted(At)? {
|
||||
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
||||
items.push(Item::Recipe(self.parse_recipe(doc, true)?));
|
||||
items.push(Item::Recipe(self.parse_recipe(doc, true, false)?));
|
||||
} else if self.accepted(BracketL)? {
|
||||
let Attribute::NoExitMessage = self.parse_attribute_name()?;
|
||||
self.expect(BracketR)?;
|
||||
self.expect_eol()?;
|
||||
let quiet = self.accepted(At)?;
|
||||
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
||||
items.push(Item::Recipe(self.parse_recipe(doc, quiet, true)?));
|
||||
} else {
|
||||
return Err(self.unexpected_token()?);
|
||||
}
|
||||
@ -595,6 +602,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
||||
&mut self,
|
||||
doc: Option<&'src str>,
|
||||
quiet: bool,
|
||||
suppress_exit_error_messages: bool,
|
||||
) -> CompileResult<'src, UnresolvedRecipe<'src>> {
|
||||
let name = self.parse_name()?;
|
||||
|
||||
@ -658,6 +666,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
||||
parameters: positional.into_iter().chain(variadic).collect(),
|
||||
private: name.lexeme().starts_with('_'),
|
||||
shebang: body.first().map_or(false, Line::is_shebang),
|
||||
suppress_exit_error_messages,
|
||||
priors,
|
||||
body,
|
||||
dependencies,
|
||||
@ -816,6 +825,16 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
||||
|
||||
Ok(Shell { arguments, command })
|
||||
}
|
||||
|
||||
/// Parse a recipe attribute name
|
||||
fn parse_attribute_name(&mut self) -> CompileResult<'src, Attribute> {
|
||||
let name = self.parse_name()?;
|
||||
Attribute::from_name(name).ok_or_else(|| {
|
||||
name.error(CompileErrorKind::UnknownAttribute {
|
||||
attribute: name.lexeme(),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -1966,7 +1985,7 @@ mod tests {
|
||||
column: 0,
|
||||
width: 1,
|
||||
kind: UnexpectedToken {
|
||||
expected: vec![At, Comment, Eof, Eol, Identifier],
|
||||
expected: vec![At, BracketL, Comment, Eof, Eol, Identifier],
|
||||
found: BraceL,
|
||||
},
|
||||
}
|
||||
@ -2144,6 +2163,29 @@ mod tests {
|
||||
},
|
||||
}
|
||||
|
||||
error! {
|
||||
name: empty_attribute,
|
||||
input: "[]\nsome_recipe:\n @exit 3",
|
||||
offset: 1,
|
||||
line: 0,
|
||||
column: 1,
|
||||
width: 1,
|
||||
kind: UnexpectedToken {
|
||||
expected: vec![Identifier],
|
||||
found: BracketR,
|
||||
},
|
||||
}
|
||||
|
||||
error! {
|
||||
name: unknown_attribute,
|
||||
input: "[unknown]\nsome_recipe:\n @exit 3",
|
||||
offset: 1,
|
||||
line: 0,
|
||||
column: 1,
|
||||
width: 7,
|
||||
kind: UnknownAttribute { attribute: "unknown" },
|
||||
}
|
||||
|
||||
error! {
|
||||
name: set_unknown,
|
||||
input: "set shall := []",
|
||||
|
@ -30,6 +30,7 @@ pub(crate) struct Recipe<'src, D = Dependency<'src>> {
|
||||
pub(crate) private: bool,
|
||||
pub(crate) quiet: bool,
|
||||
pub(crate) shebang: bool,
|
||||
pub(crate) suppress_exit_error_messages: bool,
|
||||
}
|
||||
|
||||
impl<'src, D> Recipe<'src, D> {
|
||||
@ -191,10 +192,12 @@ impl<'src, D> Recipe<'src, D> {
|
||||
Ok(exit_status) => {
|
||||
if let Some(code) = exit_status.code() {
|
||||
if code != 0 && !infallible_command {
|
||||
let suppress_message = self.suppress_exit_error_messages;
|
||||
return Err(Error::Code {
|
||||
recipe: self.name(),
|
||||
line_number: Some(line_number),
|
||||
code,
|
||||
suppress_message,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -322,10 +325,12 @@ impl<'src, D> Recipe<'src, D> {
|
||||
if code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
let suppress_message = self.suppress_exit_error_messages;
|
||||
Err(Error::Code {
|
||||
recipe: self.name(),
|
||||
line_number: None,
|
||||
code,
|
||||
suppress_message,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@ -29,7 +29,7 @@ pub fn run() -> Result<(), i32> {
|
||||
config
|
||||
.and_then(|config| config.run(&loader))
|
||||
.map_err(|error| {
|
||||
if !verbosity.quiet() {
|
||||
if !verbosity.quiet() && !error.suppress_message() {
|
||||
eprintln!("{}", error.color_display(color.stderr()));
|
||||
}
|
||||
error.code().unwrap_or(EXIT_FAILURE)
|
||||
|
@ -53,6 +53,7 @@ impl<'src> UnresolvedRecipe<'src> {
|
||||
quiet: self.quiet,
|
||||
shebang: self.shebang,
|
||||
priors: self.priors,
|
||||
suppress_exit_error_messages: self.suppress_exit_error_messages,
|
||||
dependencies,
|
||||
})
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ fn non_leading_byte_order_mark_produces_error() {
|
||||
)
|
||||
.stderr(
|
||||
"
|
||||
error: Expected \'@\', comment, end of file, end of line, or identifier, but found byte order mark
|
||||
error: Expected \'@\', \'[\', comment, end of file, end of line, or identifier, but found byte order mark
|
||||
|
|
||||
3 | \u{feff}
|
||||
| ^
|
||||
@ -41,7 +41,7 @@ fn dont_mention_byte_order_mark_in_errors() {
|
||||
.justfile("{")
|
||||
.stderr(
|
||||
"
|
||||
error: Expected '@', comment, end of file, end of line, or identifier, but found '{'
|
||||
error: Expected '@', '[', comment, end of file, end of line, or identifier, but found '{'
|
||||
|
|
||||
1 | {
|
||||
| ^
|
||||
|
@ -36,6 +36,7 @@ fn alias() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -111,6 +112,7 @@ fn body() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -154,6 +156,7 @@ fn dependencies() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"foo": {
|
||||
"body": [],
|
||||
@ -165,6 +168,7 @@ fn dependencies() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -238,6 +242,7 @@ fn dependency_argument() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"foo": {
|
||||
"body": [],
|
||||
@ -256,6 +261,7 @@ fn dependency_argument() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -311,6 +317,7 @@ fn duplicate_recipes() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -348,6 +355,7 @@ fn doc_comment() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -417,6 +425,7 @@ fn parameters() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"b": {
|
||||
"body": [],
|
||||
@ -435,6 +444,7 @@ fn parameters() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"c": {
|
||||
"body": [],
|
||||
@ -453,6 +463,7 @@ fn parameters() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"d": {
|
||||
"body": [],
|
||||
@ -471,6 +482,7 @@ fn parameters() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"e": {
|
||||
"body": [],
|
||||
@ -489,6 +501,7 @@ fn parameters() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"f": {
|
||||
"body": [],
|
||||
@ -507,6 +520,7 @@ fn parameters() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
},
|
||||
"settings": {
|
||||
@ -548,6 +562,7 @@ fn priors() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
},
|
||||
"b": {
|
||||
"body": [],
|
||||
@ -566,6 +581,7 @@ fn priors() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
"parameters": [],
|
||||
"priors": 1,
|
||||
},
|
||||
@ -578,6 +594,7 @@ fn priors() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
"parameters": [],
|
||||
"priors": 0,
|
||||
},
|
||||
@ -617,6 +634,7 @@ fn private() {
|
||||
"private": true,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -654,6 +672,7 @@ fn quiet() {
|
||||
"private": false,
|
||||
"quiet": true,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -711,6 +730,7 @@ fn settings() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": true,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -754,6 +774,7 @@ fn shebang() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": true,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -791,6 +812,48 @@ fn simple() {
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": false,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"allow_duplicate_recipes": false,
|
||||
"dotenv_load": null,
|
||||
"export": false,
|
||||
"fallback": false,
|
||||
"ignore_comments": false,
|
||||
"positional_arguments": false,
|
||||
"shell": null,
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attribute() {
|
||||
test(
|
||||
"
|
||||
[no-exit-message]
|
||||
foo:
|
||||
",
|
||||
json!({
|
||||
"aliases": {},
|
||||
"assignments": {},
|
||||
"first": "foo",
|
||||
"recipes": {
|
||||
"foo": {
|
||||
"body": [],
|
||||
"dependencies": [],
|
||||
"doc": null,
|
||||
"name": "foo",
|
||||
"parameters": [],
|
||||
"priors": 0,
|
||||
"private": false,
|
||||
"quiet": false,
|
||||
"shebang": false,
|
||||
"suppress_exit_error_messages": true,
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
@ -59,6 +59,7 @@ mod json;
|
||||
mod line_prefixes;
|
||||
mod misc;
|
||||
mod multibyte_char;
|
||||
mod no_exit_message;
|
||||
mod parser;
|
||||
mod positional_arguments;
|
||||
mod quiet;
|
||||
|
122
tests/no_exit_message.rs
Normal file
122
tests/no_exit_message.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use libc::EXIT_FAILURE;
|
||||
|
||||
test! {
|
||||
name: recipe_exit_message_suppressed,
|
||||
justfile: r#"
|
||||
# This is a doc comment
|
||||
[no-exit-message]
|
||||
hello:
|
||||
@echo "Hello, World!"
|
||||
@exit 100
|
||||
"#,
|
||||
stdout: "Hello, World!\n",
|
||||
stderr: "",
|
||||
status: 100,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: silent_recipe_exit_message_suppressed,
|
||||
justfile: r#"
|
||||
# This is a doc comment
|
||||
[no-exit-message]
|
||||
@hello:
|
||||
echo "Hello, World!"
|
||||
exit 100
|
||||
"#,
|
||||
stdout: "Hello, World!\n",
|
||||
stderr: "",
|
||||
status: 100,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: recipe_has_doc_comment,
|
||||
justfile: r#"
|
||||
# This is a doc comment
|
||||
[no-exit-message]
|
||||
hello:
|
||||
@exit 100
|
||||
"#,
|
||||
args: ("--list"),
|
||||
stdout: "
|
||||
Available recipes:
|
||||
hello # This is a doc comment
|
||||
",
|
||||
}
|
||||
|
||||
test! {
|
||||
name: unknown_attribute,
|
||||
justfile: r#"
|
||||
# This is a doc comment
|
||||
[unknown-attribute]
|
||||
hello:
|
||||
@exit 100
|
||||
"#,
|
||||
stderr: r#"
|
||||
error: Unknown attribute `unknown-attribute`
|
||||
|
|
||||
2 | [unknown-attribute]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
"#,
|
||||
status: EXIT_FAILURE,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: empty_attribute,
|
||||
justfile: r#"
|
||||
# This is a doc comment
|
||||
[]
|
||||
hello:
|
||||
@exit 100
|
||||
"#,
|
||||
stderr: r#"
|
||||
error: Expected identifier, but found ']'
|
||||
|
|
||||
2 | []
|
||||
| ^
|
||||
"#,
|
||||
status: EXIT_FAILURE,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: unattached_attribute_before_comment,
|
||||
justfile: r#"
|
||||
[no-exit-message]
|
||||
# This is a doc comment
|
||||
hello:
|
||||
@exit 100
|
||||
"#,
|
||||
stderr: r#"
|
||||
error: Expected '@' or identifier, but found comment
|
||||
|
|
||||
2 | # This is a doc comment
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
"#,
|
||||
|
||||
status: EXIT_FAILURE,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: unattached_attribute_before_empty_line,
|
||||
justfile: r#"
|
||||
[no-exit-message]
|
||||
|
||||
hello:
|
||||
@exit 100
|
||||
"#,
|
||||
stderr: "error: Expected '@' or identifier, but found end of line\n |\n2 | \n | ^\n",
|
||||
status: EXIT_FAILURE,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: shebang_exit_message_suppressed,
|
||||
justfile: r#"
|
||||
[no-exit-message]
|
||||
hello:
|
||||
#!/usr/bin/env bash
|
||||
echo 'Hello, World!'
|
||||
exit 100
|
||||
"#,
|
||||
stdout: "Hello, World!\n",
|
||||
stderr: "",
|
||||
status: 100,
|
||||
}
|
Loading…
Reference in New Issue
Block a user