mirror of
https://github.com/casey/just.git
synced 2024-11-22 18:34:06 +03:00
Allow setting custom confirm prompt (#1834)
This commit is contained in:
parent
5bbc89b718
commit
8bd411de45
@ -104,9 +104,11 @@ string : STRING
|
|||||||
sequence : expression ',' sequence
|
sequence : expression ',' sequence
|
||||||
| expression ','?
|
| expression ','?
|
||||||
|
|
||||||
recipe : attribute? '@'? NAME parameter* variadic? ':' dependency* body?
|
recipe : attributes* '@'? NAME parameter* variadic? ':' dependency* body?
|
||||||
|
|
||||||
attribute : '[' NAME ']' eol
|
attributes : '[' attribute* ']' eol
|
||||||
|
|
||||||
|
attribute : NAME ( '(' string ')' )?
|
||||||
|
|
||||||
parameter : '$'? NAME
|
parameter : '$'? NAME
|
||||||
| '$'? NAME '=' value
|
| '$'? NAME '=' value
|
||||||
|
11
README.md
11
README.md
@ -1456,6 +1456,7 @@ Recipes may be annotated with attributes that change their behavior.
|
|||||||
| Name | Description |
|
| Name | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `[confirm]`<sup>1.17.0</sup> | Require confirmation prior to executing recipe. |
|
| `[confirm]`<sup>1.17.0</sup> | Require confirmation prior to executing recipe. |
|
||||||
|
| `[confirm("prompt")]`<sup>master</sup> | Require confirmation prior to executing recipe with a custom prompt. |
|
||||||
| `[linux]`<sup>1.8.0</sup> | Enable recipe on Linux. |
|
| `[linux]`<sup>1.8.0</sup> | Enable recipe on Linux. |
|
||||||
| `[macos]`<sup>1.8.0</sup> | Enable recipe on MacOS. |
|
| `[macos]`<sup>1.8.0</sup> | Enable recipe on MacOS. |
|
||||||
| `[no-cd]`<sup>1.9.0</sup> | Don't change directory before executing recipe. |
|
| `[no-cd]`<sup>1.9.0</sup> | Don't change directory before executing recipe. |
|
||||||
@ -1544,6 +1545,16 @@ delete all:
|
|||||||
rm -rf *
|
rm -rf *
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Custom Confirmation Prompt<sup>master</sup>
|
||||||
|
|
||||||
|
The default confirmation prompt can be overridden with `[confirm(PROMPT)]`:
|
||||||
|
|
||||||
|
```just
|
||||||
|
[confirm("Are you sure you want to delete everything?")]
|
||||||
|
delete-everything:
|
||||||
|
rm -rf *
|
||||||
|
```
|
||||||
|
|
||||||
### Command Evaluation Using Backticks
|
### Command Evaluation Using Backticks
|
||||||
|
|
||||||
Backticks can be used to store the result of commands:
|
Backticks can be used to store the result of commands:
|
||||||
|
@ -3,7 +3,7 @@ use super::*;
|
|||||||
/// An alias, e.g. `name := target`
|
/// An alias, e.g. `name := target`
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||||
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
|
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
|
||||||
pub(crate) attributes: BTreeSet<Attribute>,
|
pub(crate) attributes: BTreeSet<Attribute<'src>>,
|
||||||
pub(crate) name: Name<'src>,
|
pub(crate) name: Name<'src>,
|
||||||
#[serde(
|
#[serde(
|
||||||
bound(serialize = "T: Keyed<'src>"),
|
bound(serialize = "T: Keyed<'src>"),
|
||||||
|
@ -211,11 +211,11 @@ impl<'src> Analyzer<'src> {
|
|||||||
fn analyze_alias(alias: &Alias<'src, Name<'src>>) -> CompileResult<'src> {
|
fn analyze_alias(alias: &Alias<'src, Name<'src>>) -> CompileResult<'src> {
|
||||||
let name = alias.name.lexeme();
|
let name = alias.name.lexeme();
|
||||||
|
|
||||||
for attr in &alias.attributes {
|
for attribute in &alias.attributes {
|
||||||
if *attr != Attribute::Private {
|
if *attribute != Attribute::Private {
|
||||||
return Err(alias.name.token.error(AliasInvalidAttribute {
|
return Err(alias.name.token.error(AliasInvalidAttribute {
|
||||||
alias: name,
|
alias: name,
|
||||||
attr: *attr,
|
attribute: attribute.clone(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(
|
#[derive(EnumString, PartialEq, Debug, Clone, Serialize, Ord, PartialOrd, Eq, IntoStaticStr)]
|
||||||
EnumString, PartialEq, Debug, Copy, Clone, Serialize, Ord, PartialOrd, Eq, IntoStaticStr,
|
|
||||||
)]
|
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub(crate) enum Attribute {
|
pub(crate) enum Attribute<'src> {
|
||||||
Confirm,
|
Confirm(Option<StringLiteral<'src>>),
|
||||||
Linux,
|
Linux,
|
||||||
Macos,
|
Macos,
|
||||||
NoCd,
|
NoCd,
|
||||||
@ -17,14 +15,45 @@ pub(crate) enum Attribute {
|
|||||||
Windows,
|
Windows,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attribute {
|
impl<'src> Attribute<'src> {
|
||||||
pub(crate) fn from_name(name: Name) -> Option<Attribute> {
|
pub(crate) fn from_name(name: Name) -> Option<Self> {
|
||||||
name.lexeme().parse().ok()
|
name.lexeme().parse().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn to_str(self) -> &'static str {
|
pub(crate) fn name(&self) -> &'static str {
|
||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_argument(
|
||||||
|
self,
|
||||||
|
name: Name<'src>,
|
||||||
|
argument: StringLiteral<'src>,
|
||||||
|
) -> CompileResult<'src, Self> {
|
||||||
|
match self {
|
||||||
|
Self::Confirm(_) => Ok(Self::Confirm(Some(argument))),
|
||||||
|
_ => Err(name.error(CompileErrorKind::UnexpectedAttributeArgument { attribute: self })),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn argument(&self) -> Option<&StringLiteral> {
|
||||||
|
if let Self::Confirm(prompt) = self {
|
||||||
|
prompt.as_ref()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> Display for Attribute<'src> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", self.name())?;
|
||||||
|
|
||||||
|
if let Some(argument) = self.argument() {
|
||||||
|
write!(f, "({argument})")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -32,7 +61,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_str() {
|
fn name() {
|
||||||
assert_eq!(Attribute::NoExitMessage.to_str(), "no-exit-message");
|
assert_eq!(Attribute::NoExitMessage.name(), "no-exit-message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,13 @@ impl Display for CompileError<'_> {
|
|||||||
use CompileErrorKind::*;
|
use CompileErrorKind::*;
|
||||||
|
|
||||||
match &*self.kind {
|
match &*self.kind {
|
||||||
AliasInvalidAttribute { alias, attr } => write!(
|
AliasInvalidAttribute { alias, attribute } => {
|
||||||
f,
|
write!(
|
||||||
"Alias {alias} has an invalid attribute `{}`",
|
f,
|
||||||
attr.to_str(),
|
"Alias `{alias}` has invalid attribute `{}`",
|
||||||
),
|
attribute.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
AliasShadowsRecipe { alias, recipe_line } => write!(
|
AliasShadowsRecipe { alias, recipe_line } => write!(
|
||||||
f,
|
f,
|
||||||
"Alias `{alias}` defined on line {} shadows recipe `{alias}` defined on line {}",
|
"Alias `{alias}` defined on line {} shadows recipe `{alias}` defined on line {}",
|
||||||
@ -209,6 +211,13 @@ impl Display for CompileError<'_> {
|
|||||||
"Non-default parameter `{parameter}` follows default parameter"
|
"Non-default parameter `{parameter}` follows default parameter"
|
||||||
),
|
),
|
||||||
UndefinedVariable { variable } => write!(f, "Variable `{variable}` not defined"),
|
UndefinedVariable { variable } => write!(f, "Variable `{variable}` not defined"),
|
||||||
|
UnexpectedAttributeArgument { attribute } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Attribute `{}` specified with argument but takes no arguments",
|
||||||
|
attribute.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
UnexpectedCharacter { expected } => write!(f, "Expected character `{expected}`"),
|
UnexpectedCharacter { expected } => write!(f, "Expected character `{expected}`"),
|
||||||
UnexpectedClosingDelimiter { close } => {
|
UnexpectedClosingDelimiter { close } => {
|
||||||
write!(f, "Unexpected closing delimiter `{}`", close.close())
|
write!(f, "Unexpected closing delimiter `{}`", close.close())
|
||||||
|
@ -4,7 +4,7 @@ use super::*;
|
|||||||
pub(crate) enum CompileErrorKind<'src> {
|
pub(crate) enum CompileErrorKind<'src> {
|
||||||
AliasInvalidAttribute {
|
AliasInvalidAttribute {
|
||||||
alias: &'src str,
|
alias: &'src str,
|
||||||
attr: Attribute,
|
attribute: Attribute<'src>,
|
||||||
},
|
},
|
||||||
AliasShadowsRecipe {
|
AliasShadowsRecipe {
|
||||||
alias: &'src str,
|
alias: &'src str,
|
||||||
@ -85,6 +85,9 @@ pub(crate) enum CompileErrorKind<'src> {
|
|||||||
UndefinedVariable {
|
UndefinedVariable {
|
||||||
variable: &'src str,
|
variable: &'src str,
|
||||||
},
|
},
|
||||||
|
UnexpectedAttributeArgument {
|
||||||
|
attribute: Attribute<'src>,
|
||||||
|
},
|
||||||
UnexpectedCharacter {
|
UnexpectedCharacter {
|
||||||
expected: char,
|
expected: char,
|
||||||
},
|
},
|
||||||
|
@ -435,7 +435,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
/// Parse an alias, e.g `alias name := target`
|
/// Parse an alias, e.g `alias name := target`
|
||||||
fn parse_alias(
|
fn parse_alias(
|
||||||
&mut self,
|
&mut self,
|
||||||
attributes: BTreeSet<Attribute>,
|
attributes: BTreeSet<Attribute<'src>>,
|
||||||
) -> CompileResult<'src, Alias<'src, Name<'src>>> {
|
) -> CompileResult<'src, Alias<'src, Name<'src>>> {
|
||||||
self.presume_keyword(Keyword::Alias)?;
|
self.presume_keyword(Keyword::Alias)?;
|
||||||
let name = self.parse_name()?;
|
let name = self.parse_name()?;
|
||||||
@ -672,7 +672,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
doc: Option<&'src str>,
|
doc: Option<&'src str>,
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
attributes: BTreeSet<Attribute>,
|
attributes: BTreeSet<Attribute<'src>>,
|
||||||
) -> CompileResult<'src, UnresolvedRecipe<'src>> {
|
) -> CompileResult<'src, UnresolvedRecipe<'src>> {
|
||||||
let name = self.parse_name()?;
|
let name = self.parse_name()?;
|
||||||
|
|
||||||
@ -905,7 +905,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse recipe attributes
|
/// Parse recipe attributes
|
||||||
fn parse_attributes(&mut self) -> CompileResult<'src, Option<BTreeSet<Attribute>>> {
|
fn parse_attributes(&mut self) -> CompileResult<'src, Option<BTreeSet<Attribute<'src>>>> {
|
||||||
let mut attributes = BTreeMap::new();
|
let mut attributes = BTreeMap::new();
|
||||||
|
|
||||||
while self.accepted(BracketL)? {
|
while self.accepted(BracketL)? {
|
||||||
@ -922,6 +922,15 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
first: *line,
|
first: *line,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let attribute = if self.accepted(ParenL)? {
|
||||||
|
let argument = self.parse_string_literal()?;
|
||||||
|
self.expect(ParenR)?;
|
||||||
|
attribute.with_argument(name, argument)?
|
||||||
|
} else {
|
||||||
|
attribute
|
||||||
|
};
|
||||||
|
|
||||||
attributes.insert(attribute, name.line);
|
attributes.insert(attribute, name.line);
|
||||||
|
|
||||||
if !self.accepted(Comma)? {
|
if !self.accepted(Comma)? {
|
||||||
|
@ -13,11 +13,13 @@ pub(crate) struct DisplayRange<T>(T);
|
|||||||
impl Display for DisplayRange<&Range<usize>> {
|
impl Display for DisplayRange<&Range<usize>> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
if self.0.start == self.0.end {
|
if self.0.start == self.0.end {
|
||||||
|
write!(f, "0")?;
|
||||||
|
} else if self.0.start == self.0.end - 1 {
|
||||||
write!(f, "{}", self.0.start)?;
|
write!(f, "{}", self.0.start)?;
|
||||||
} else if self.0.end == usize::MAX {
|
} else if self.0.end == usize::MAX {
|
||||||
write!(f, "{} or more", self.0.start)?;
|
write!(f, "{} or more", self.0.start)?;
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{} to {}", self.0.start, self.0.end)?;
|
write!(f, "{} to {}", self.0.start, self.0.end - 1)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -47,15 +49,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exclusive() {
|
fn exclusive() {
|
||||||
assert!(!(0..0).range_contains(&0));
|
|
||||||
assert!(!(0..0).range_contains(&0));
|
assert!(!(0..0).range_contains(&0));
|
||||||
assert!(!(1..10).range_contains(&0));
|
assert!(!(1..10).range_contains(&0));
|
||||||
assert!(!(1..10).range_contains(&10));
|
assert!(!(1..10).range_contains(&10));
|
||||||
assert!(!(1..10).range_contains(&0));
|
assert!(!(1..10).range_contains(&0));
|
||||||
assert!(!(1..10).range_contains(&10));
|
|
||||||
assert!((0..1).range_contains(&0));
|
assert!((0..1).range_contains(&0));
|
||||||
assert!((0..1).range_contains(&0));
|
|
||||||
assert!((10..20).range_contains(&15));
|
|
||||||
assert!((10..20).range_contains(&15));
|
assert!((10..20).range_contains(&15));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +73,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display() {
|
fn display() {
|
||||||
assert_eq!((1..1).display().to_string(), "1");
|
assert!(!(1..1).contains(&1));
|
||||||
assert_eq!((1..2).display().to_string(), "1 to 2");
|
assert!((1..1).is_empty());
|
||||||
|
assert!((5..5).is_empty());
|
||||||
|
assert_eq!((1..1).display().to_string(), "0");
|
||||||
|
assert_eq!((1..2).display().to_string(), "1");
|
||||||
|
assert_eq!((5..6).display().to_string(), "5");
|
||||||
|
assert_eq!((5..10).display().to_string(), "5 to 9");
|
||||||
assert_eq!((1..usize::MAX).display().to_string(), "1 or more");
|
assert_eq!((1..usize::MAX).display().to_string(), "1 or more");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ fn error_from_signal(recipe: &str, line_number: Option<usize>, exit_status: Exit
|
|||||||
/// A recipe, e.g. `foo: bar baz`
|
/// A recipe, e.g. `foo: bar baz`
|
||||||
#[derive(PartialEq, Debug, Clone, Serialize)]
|
#[derive(PartialEq, Debug, Clone, Serialize)]
|
||||||
pub(crate) struct Recipe<'src, D = Dependency<'src>> {
|
pub(crate) struct Recipe<'src, D = Dependency<'src>> {
|
||||||
pub(crate) attributes: BTreeSet<Attribute>,
|
pub(crate) attributes: BTreeSet<Attribute<'src>>,
|
||||||
pub(crate) body: Vec<Line<'src>>,
|
pub(crate) body: Vec<Line<'src>>,
|
||||||
pub(crate) dependencies: Vec<D>,
|
pub(crate) dependencies: Vec<D>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@ -71,17 +71,22 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn confirm(&self) -> RunResult<'src, bool> {
|
pub(crate) fn confirm(&self) -> RunResult<'src, bool> {
|
||||||
if self.attributes.contains(&Attribute::Confirm) {
|
for attribute in &self.attributes {
|
||||||
eprint!("Run recipe `{}`? ", self.name);
|
if let Attribute::Confirm(prompt) = attribute {
|
||||||
let mut line = String::new();
|
if let Some(prompt) = prompt {
|
||||||
std::io::stdin()
|
eprint!("{} ", prompt.cooked);
|
||||||
.read_line(&mut line)
|
} else {
|
||||||
.map_err(|io_error| Error::GetConfirmation { io_error })?;
|
eprint!("Run recipe `{}`? ", self.name);
|
||||||
let line = line.trim().to_lowercase();
|
}
|
||||||
Ok(line == "y" || line == "yes")
|
let mut line = String::new();
|
||||||
} else {
|
std::io::stdin()
|
||||||
Ok(true)
|
.read_line(&mut line)
|
||||||
|
.map_err(|io_error| Error::GetConfirmation { io_error })?;
|
||||||
|
let line = line.trim().to_lowercase();
|
||||||
|
return Ok(line == "y" || line == "yes");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn check_can_be_default_recipe(&self) -> RunResult<'src, ()> {
|
pub(crate) fn check_can_be_default_recipe(&self) -> RunResult<'src, ()> {
|
||||||
@ -423,7 +428,7 @@ impl<'src, D: Display> ColorDisplay for Recipe<'src, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for attribute in &self.attributes {
|
for attribute in &self.attributes {
|
||||||
writeln!(f, "[{}]", attribute.to_str())?;
|
writeln!(f, "[{attribute}]")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.quiet {
|
if self.quiet {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone, Ord, Eq, PartialOrd)]
|
||||||
pub(crate) struct StringLiteral<'src> {
|
pub(crate) struct StringLiteral<'src> {
|
||||||
pub(crate) kind: StringKind,
|
pub(crate) kind: StringKind,
|
||||||
pub(crate) raw: &'src str,
|
pub(crate) raw: &'src str,
|
||||||
|
@ -72,7 +72,7 @@ fn multiple_attributes_one_line_error_message() {
|
|||||||
)
|
)
|
||||||
.stderr(
|
.stderr(
|
||||||
"
|
"
|
||||||
error: Expected ']' or ',', but found identifier
|
error: Expected ']', ',', or '(', but found identifier
|
||||||
——▶ justfile:1:17
|
——▶ justfile:1:17
|
||||||
│
|
│
|
||||||
1 │ [macos, windows linux]
|
1 │ [macos, windows linux]
|
||||||
@ -106,3 +106,26 @@ fn multiple_attributes_one_line_duplicate_check() {
|
|||||||
.status(1)
|
.status(1)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unexpected_attribute_argument() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
[private('foo')]
|
||||||
|
foo:
|
||||||
|
exit 1
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.stderr(
|
||||||
|
"
|
||||||
|
error: Attribute `private` specified with argument but takes no arguments
|
||||||
|
——▶ justfile:1:2
|
||||||
|
│
|
||||||
|
1 │ [private('foo')]
|
||||||
|
│ ^^^^^^^
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -103,3 +103,49 @@ fn do_not_confirm_recipe_with_confirm_recipe_dependency() {
|
|||||||
.status(1)
|
.status(1)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn confirm_recipe_with_prompt() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
[confirm(\"This is dangerous - are you sure you want to run it?\")]
|
||||||
|
requires_confirmation:
|
||||||
|
echo confirmed
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.stderr("This is dangerous - are you sure you want to run it? echo confirmed\n")
|
||||||
|
.stdout("confirmed\n")
|
||||||
|
.stdin("y")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn confirm_recipe_with_prompt_too_many_args() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
[confirm(\"This is dangerous - are you sure you want to run it?\",\"this second argument is not supported\")]
|
||||||
|
requires_confirmation:
|
||||||
|
echo confirmed
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.stderr("error: Expected ')', but found ','\n ——▶ justfile:1:64\n │\n1 │ [confirm(\"This is dangerous - are you sure you want to run it?\",\"this second argument is not supported\")]\n │ ^\n")
|
||||||
|
.stdout("")
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn confirm_attribute_is_formatted_correctly() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
[confirm('prompt')]
|
||||||
|
foo:
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.arg("--dump")
|
||||||
|
.stdout("[confirm('prompt')]\nfoo:\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ test! {
|
|||||||
name: invalid_alias_attribute,
|
name: invalid_alias_attribute,
|
||||||
justfile: "[private]\n[linux]\nalias t := test\n\ntest:\n",
|
justfile: "[private]\n[linux]\nalias t := test\n\ntest:\n",
|
||||||
stderr: "
|
stderr: "
|
||||||
error: Alias t has an invalid attribute `linux`
|
error: Alias `t` has invalid attribute `linux`
|
||||||
——▶ justfile:3:7
|
——▶ justfile:3:7
|
||||||
│
|
│
|
||||||
3 │ alias t := test
|
3 │ alias t := test
|
||||||
|
Loading…
Reference in New Issue
Block a user