Bump version, add --debug
This commit is contained in:
parent
9a368fb351
commit
cc683cbb04
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "j"
|
name = "j"
|
||||||
version = "0.2.7"
|
version = "0.2.8"
|
||||||
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
||||||
license = "WTFPL/MIT/Apache-2.0"
|
license = "WTFPL/MIT/Apache-2.0"
|
||||||
description = "a command runner"
|
description = "a command runner"
|
||||||
|
19
notes
19
notes
@ -1,15 +1,8 @@
|
|||||||
notes
|
todo
|
||||||
-----
|
----
|
||||||
|
|
||||||
- --debug tests that:
|
before release
|
||||||
- should consider renaming it to --evaluate if it actually evaluates stuff
|
--------------
|
||||||
- test values of assignments
|
|
||||||
- test values of interpolations
|
|
||||||
- test results of concatination
|
|
||||||
- test string escape parsing
|
|
||||||
- should it evaluate `` in recipes?
|
|
||||||
|
|
||||||
- before release:
|
|
||||||
|
|
||||||
- where can users get help?
|
- where can users get help?
|
||||||
- irc, email, github, mailing list
|
- irc, email, github, mailing list
|
||||||
@ -54,7 +47,8 @@ notes
|
|||||||
. irc
|
. irc
|
||||||
. r/rust
|
. r/rust
|
||||||
|
|
||||||
enhancements:
|
enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
- lazy assignment: don't evaluate unused assignments
|
- lazy assignment: don't evaluate unused assignments
|
||||||
- use cow strings where we currently use String
|
- use cow strings where we currently use String
|
||||||
@ -66,6 +60,7 @@ enhancements:
|
|||||||
. just ../foo # ../justfile:foo
|
. just ../foo # ../justfile:foo
|
||||||
. just xyz/foo # xyz/justfile:foo
|
. just xyz/foo # xyz/justfile:foo
|
||||||
. just xyz/ # xyz/justfile:DEFAULT
|
. just xyz/ # xyz/justfile:DEFAULT
|
||||||
|
. path prefix is starting dir, so just ../foo can run ../../justfile:foo
|
||||||
- allow setting and exporting environment variables
|
- allow setting and exporting environment variables
|
||||||
. export a as "HELLO_BAR"
|
. export a as "HELLO_BAR"
|
||||||
. export a
|
. export a
|
||||||
|
23
src/app.rs
23
src/app.rs
@ -23,16 +23,19 @@ macro_rules! die {
|
|||||||
|
|
||||||
pub fn app() {
|
pub fn app() {
|
||||||
let matches = App::new("j")
|
let matches = App::new("j")
|
||||||
.version("0.2.7")
|
.version("0.2.8")
|
||||||
.author("Casey R. <casey@rodarmor.com>")
|
.author("Casey Rodarmor <casey@rodarmor.com>")
|
||||||
.about("Just a command runner - https://github.com/casey/j")
|
.about("Just a command runner - https://github.com/casey/j")
|
||||||
.arg(Arg::with_name("list")
|
.arg(Arg::with_name("list")
|
||||||
.short("l")
|
.short("l")
|
||||||
.long("list")
|
.long("list")
|
||||||
.help("Lists available recipes"))
|
.help("Lists available recipes"))
|
||||||
.arg(Arg::with_name("debug")
|
.arg(Arg::with_name("dry-run")
|
||||||
.long("debug")
|
.long("dry-run")
|
||||||
.help("Prints the justfile with debugging information, such as evaluated expression and assignment"))
|
.help("Print recipe text without executing"))
|
||||||
|
.arg(Arg::with_name("evaluate")
|
||||||
|
.long("evaluate")
|
||||||
|
.help("Print evaluated variables"))
|
||||||
.arg(Arg::with_name("show")
|
.arg(Arg::with_name("show")
|
||||||
.short("s")
|
.short("s")
|
||||||
.long("show")
|
.long("show")
|
||||||
@ -108,11 +111,6 @@ pub fn app() {
|
|||||||
|
|
||||||
let justfile = super::parse(&text).unwrap_or_else(|error| die!("{}", error));
|
let justfile = super::parse(&text).unwrap_or_else(|error| die!("{}", error));
|
||||||
|
|
||||||
if matches.is_present("debug") {
|
|
||||||
println!("{:#}", justfile);
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.is_present("list") {
|
if matches.is_present("list") {
|
||||||
if justfile.count() == 0 {
|
if justfile.count() == 0 {
|
||||||
warn!("Justfile contains no recipes");
|
warn!("Justfile contains no recipes");
|
||||||
@ -162,7 +160,10 @@ pub fn app() {
|
|||||||
die!("Justfile contains no recipes");
|
die!("Justfile contains no recipes");
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(run_error) = justfile.run(&overrides, &arguments) {
|
let dry_run = matches.is_present("dry-run");
|
||||||
|
let evaluate = matches.is_present("evaluate");
|
||||||
|
|
||||||
|
if let Err(run_error) = justfile.run(&overrides, &arguments, dry_run, evaluate) {
|
||||||
warn!("{}", run_error);
|
warn!("{}", run_error);
|
||||||
match run_error {
|
match run_error {
|
||||||
super::RunError::Code{code, .. } => process::exit(code),
|
super::RunError::Code{code, .. } => process::exit(code),
|
||||||
|
@ -398,3 +398,62 @@ recipe arg:
|
|||||||
"echo arg=baz=bar\necho barbbaz\n",
|
"echo arg=baz=bar\necho barbbaz\n",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shebangs are printed
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dry_run() {
|
||||||
|
integration_test(
|
||||||
|
"dry_run",
|
||||||
|
&["--dry-run", "shebang", "command"],
|
||||||
|
r#"
|
||||||
|
var = `echo stderr 1>&2; echo backtick`
|
||||||
|
|
||||||
|
command:
|
||||||
|
@touch /this/is/not/a/file
|
||||||
|
{{var}}
|
||||||
|
echo {{`echo command interpolation`}}
|
||||||
|
|
||||||
|
shebang:
|
||||||
|
#!/bin/sh
|
||||||
|
touch /this/is/not/a/file
|
||||||
|
{{var}}
|
||||||
|
echo {{`echo shebang interpolation`}}"#,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"stderr
|
||||||
|
#!/bin/sh
|
||||||
|
touch /this/is/not/a/file
|
||||||
|
backtick
|
||||||
|
echo shebang interpolation
|
||||||
|
touch /this/is/not/a/file
|
||||||
|
backtick
|
||||||
|
echo command interpolation
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate() {
|
||||||
|
integration_test(
|
||||||
|
"evaluate",
|
||||||
|
&["--evaluate"],
|
||||||
|
r#"
|
||||||
|
foo = "a\t"
|
||||||
|
baz = "c"
|
||||||
|
bar = "b\t"
|
||||||
|
abc = foo + bar + baz
|
||||||
|
|
||||||
|
wut:
|
||||||
|
touch /this/is/not/a/file
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
r#"abc = "a b c"
|
||||||
|
bar = "b "
|
||||||
|
baz = "c"
|
||||||
|
foo = "a "
|
||||||
|
"#,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
43
src/lib.rs
43
src/lib.rs
@ -188,7 +188,8 @@ impl<'a> Recipe<'a> {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &BTreeMap<&'a str, String>
|
scope: &BTreeMap<&'a str, String>,
|
||||||
|
dry_run: bool,
|
||||||
) -> Result<(), RunError<'a>> {
|
) -> Result<(), RunError<'a>> {
|
||||||
let argument_map = arguments .iter().enumerate()
|
let argument_map = arguments .iter().enumerate()
|
||||||
.map(|(i, argument)| (self.arguments[i], *argument)).collect();
|
.map(|(i, argument)| (self.arguments[i], *argument)).collect();
|
||||||
@ -206,6 +207,13 @@ impl<'a> Recipe<'a> {
|
|||||||
evaluated_lines.push(try!(evaluator.evaluate_line(&line, &argument_map)));
|
evaluated_lines.push(try!(evaluator.evaluate_line(&line, &argument_map)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dry_run {
|
||||||
|
for line in evaluated_lines {
|
||||||
|
warn!("{}", line);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let tmp = try!(
|
let tmp = try!(
|
||||||
tempdir::TempDir::new("j")
|
tempdir::TempDir::new("j")
|
||||||
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
|
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
|
||||||
@ -266,11 +274,16 @@ impl<'a> Recipe<'a> {
|
|||||||
for line in &self.lines {
|
for line in &self.lines {
|
||||||
let evaluated = &try!(evaluator.evaluate_line(&line, &argument_map));
|
let evaluated = &try!(evaluator.evaluate_line(&line, &argument_map));
|
||||||
let mut command = evaluated.as_str();
|
let mut command = evaluated.as_str();
|
||||||
if !command.starts_with('@') {
|
let quiet = command.starts_with('@');
|
||||||
warn!("{}", command);
|
if quiet {
|
||||||
} else {
|
|
||||||
command = &command[1..];
|
command = &command[1..];
|
||||||
}
|
}
|
||||||
|
if dry_run || !quiet {
|
||||||
|
warn!("{}", command);
|
||||||
|
}
|
||||||
|
if dry_run {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let status = process::Command::new("sh")
|
let status = process::Command::new("sh")
|
||||||
.arg("-cu")
|
.arg("-cu")
|
||||||
.arg(command)
|
.arg(command)
|
||||||
@ -809,7 +822,9 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
fn run(
|
fn run(
|
||||||
&'a self,
|
&'a self,
|
||||||
overrides: &BTreeMap<&'a str, &'a str>,
|
overrides: &BTreeMap<&'a str, &'a str>,
|
||||||
arguments: &[&'a str]
|
arguments: &[&'a str],
|
||||||
|
dry_run: bool,
|
||||||
|
evaluate: bool,
|
||||||
) -> Result<(), RunError<'a>> {
|
) -> Result<(), RunError<'a>> {
|
||||||
let unknown_overrides = overrides.keys().cloned()
|
let unknown_overrides = overrides.keys().cloned()
|
||||||
.filter(|name| !self.assignments.contains_key(name))
|
.filter(|name| !self.assignments.contains_key(name))
|
||||||
@ -820,6 +835,13 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let scope = try!(evaluate_assignments(&self.assignments, overrides));
|
let scope = try!(evaluate_assignments(&self.assignments, overrides));
|
||||||
|
if evaluate {
|
||||||
|
for (name, value) in scope {
|
||||||
|
println!("{} = \"{}\"", name, value);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut ran = HashSet::new();
|
let mut ran = HashSet::new();
|
||||||
|
|
||||||
for (i, argument) in arguments.iter().enumerate() {
|
for (i, argument) in arguments.iter().enumerate() {
|
||||||
@ -836,7 +858,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
expected: recipe.arguments.len(),
|
expected: recipe.arguments.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try!(self.run_recipe(recipe, rest, &scope, &mut ran));
|
try!(self.run_recipe(recipe, rest, &scope, &mut ran, dry_run));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -854,7 +876,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
return Err(RunError::UnknownRecipes{recipes: missing});
|
return Err(RunError::UnknownRecipes{recipes: missing});
|
||||||
}
|
}
|
||||||
for recipe in arguments.iter().map(|name| &self.recipes[name]) {
|
for recipe in arguments.iter().map(|name| &self.recipes[name]) {
|
||||||
try!(self.run_recipe(recipe, &[], &scope, &mut ran));
|
try!(self.run_recipe(recipe, &[], &scope, &mut ran, dry_run));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -864,14 +886,15 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
recipe: &Recipe<'a>,
|
recipe: &Recipe<'a>,
|
||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &BTreeMap<&'c str, String>,
|
scope: &BTreeMap<&'c str, String>,
|
||||||
ran: &mut HashSet<&'a str>
|
ran: &mut HashSet<&'a str>,
|
||||||
|
dry_run: bool,
|
||||||
) -> Result<(), RunError> {
|
) -> Result<(), RunError> {
|
||||||
for dependency_name in &recipe.dependencies {
|
for dependency_name in &recipe.dependencies {
|
||||||
if !ran.contains(dependency_name) {
|
if !ran.contains(dependency_name) {
|
||||||
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran));
|
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran, dry_run));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(recipe.run(arguments, &scope));
|
try!(recipe.run(arguments, &scope, dry_run));
|
||||||
ran.insert(recipe.name);
|
ran.insert(recipe.name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
16
src/unit.rs
16
src/unit.rs
@ -579,7 +579,7 @@ fn conjoin_and() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_recipes() {
|
fn unknown_recipes() {
|
||||||
match parse_success("a:\nb:\nc:").run(&BTreeMap::new(), &["a", "x", "y", "z"]).unwrap_err() {
|
match parse_success("a:\nb:\nc:").run(&BTreeMap::new(), &["a", "x", "y", "z"], false, false).unwrap_err() {
|
||||||
RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]),
|
RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]),
|
||||||
other => panic!("expected an unknown recipe error, but got: {}", other),
|
other => panic!("expected an unknown recipe error, but got: {}", other),
|
||||||
}
|
}
|
||||||
@ -747,7 +747,7 @@ a:
|
|||||||
x
|
x
|
||||||
";
|
";
|
||||||
|
|
||||||
match parse_success(text).run(&BTreeMap::new(), &["a"]).unwrap_err() {
|
match parse_success(text).run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
|
||||||
RunError::Code{recipe, code} => {
|
RunError::Code{recipe, code} => {
|
||||||
assert_eq!(recipe, "a");
|
assert_eq!(recipe, "a");
|
||||||
assert_eq!(code, 200);
|
assert_eq!(code, 200);
|
||||||
@ -758,7 +758,7 @@ a:
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn code_error() {
|
fn code_error() {
|
||||||
match parse_success("fail:\n @function x { return 100; }; x").run(&BTreeMap::new(), &["fail"]).unwrap_err() {
|
match parse_success("fail:\n @function x { return 100; }; x").run(&BTreeMap::new(), &["fail"], false, false).unwrap_err() {
|
||||||
RunError::Code{recipe, code} => {
|
RunError::Code{recipe, code} => {
|
||||||
assert_eq!(recipe, "fail");
|
assert_eq!(recipe, "fail");
|
||||||
assert_eq!(code, 100);
|
assert_eq!(code, 100);
|
||||||
@ -773,7 +773,7 @@ fn run_args() {
|
|||||||
a return code:
|
a return code:
|
||||||
@function x { {{return}} {{code + "0"}}; }; x"#;
|
@function x { {{return}} {{code + "0"}}; }; x"#;
|
||||||
|
|
||||||
match parse_success(text).run(&BTreeMap::new(), &["a", "return", "15"]).unwrap_err() {
|
match parse_success(text).run(&BTreeMap::new(), &["a", "return", "15"], false, false).unwrap_err() {
|
||||||
RunError::Code{recipe, code} => {
|
RunError::Code{recipe, code} => {
|
||||||
assert_eq!(recipe, "a");
|
assert_eq!(recipe, "a");
|
||||||
assert_eq!(code, 150);
|
assert_eq!(code, 150);
|
||||||
@ -784,7 +784,7 @@ a return code:
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_args() {
|
fn missing_args() {
|
||||||
match parse_success("a b c d:").run(&BTreeMap::new(), &["a", "b", "c"]).unwrap_err() {
|
match parse_success("a b c d:").run(&BTreeMap::new(), &["a", "b", "c"], false, false).unwrap_err() {
|
||||||
RunError::ArgumentCountMismatch{recipe, found, expected} => {
|
RunError::ArgumentCountMismatch{recipe, found, expected} => {
|
||||||
assert_eq!(recipe, "a");
|
assert_eq!(recipe, "a");
|
||||||
assert_eq!(found, 2);
|
assert_eq!(found, 2);
|
||||||
@ -796,7 +796,7 @@ fn missing_args() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_default() {
|
fn missing_default() {
|
||||||
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&BTreeMap::new(), &["a"]).unwrap_err() {
|
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
|
||||||
RunError::ArgumentCountMismatch{recipe, found, expected} => {
|
RunError::ArgumentCountMismatch{recipe, found, expected} => {
|
||||||
assert_eq!(recipe, "a");
|
assert_eq!(recipe, "a");
|
||||||
assert_eq!(found, 0);
|
assert_eq!(found, 0);
|
||||||
@ -808,7 +808,7 @@ fn missing_default() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn backtick_code() {
|
fn backtick_code() {
|
||||||
match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&BTreeMap::new(), &["a"]).unwrap_err() {
|
match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
|
||||||
RunError::BacktickCode{code, token} => {
|
RunError::BacktickCode{code, token} => {
|
||||||
assert_eq!(code, 100);
|
assert_eq!(code, 100);
|
||||||
assert_eq!(token.lexeme, "`function f { return 100; }; f`");
|
assert_eq!(token.lexeme, "`function f { return 100; }; f`");
|
||||||
@ -823,7 +823,7 @@ fn unknown_overrides() {
|
|||||||
overrides.insert("foo", "bar");
|
overrides.insert("foo", "bar");
|
||||||
overrides.insert("baz", "bob");
|
overrides.insert("baz", "bob");
|
||||||
match parse_success("a:\n echo {{`function f { return 100; }; f`}}")
|
match parse_success("a:\n echo {{`function f { return 100; }; f`}}")
|
||||||
.run(&overrides, &["a"]).unwrap_err() {
|
.run(&overrides, &["a"], false, false).unwrap_err() {
|
||||||
RunError::UnknownOverrides{overrides} => {
|
RunError::UnknownOverrides{overrides} => {
|
||||||
assert_eq!(overrides, &["baz", "foo"]);
|
assert_eq!(overrides, &["baz", "foo"]);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user