diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1347d2c..93c9476 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -42,6 +42,9 @@ jobs:
steps:
- *attach_workspace
- *restore_cache
+ - run:
+ name: Make mask available for certain test suites that depend on it
+ command: echo 'export PATH="$PATH:~/repo/target/debug"' >> $BASH_ENV
- run:
name: Install latest node from PPA
command: apt-get update && apt-get install -y curl software-properties-common && curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install nodejs
diff --git a/README.md b/README.md
index 7ef3f56..4836159 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
-
-
+
[![build status](https://img.shields.io/circleci/build/github/jakedeichert/mask/master.svg)][circleci]
[![mask version](https://img.shields.io/crates/v/mask.svg)][crate]
@@ -263,7 +262,7 @@ ARGS:
### Running mask from within a script
-You can easily call `mask` within scripts if you need to chain commands together.
+You can easily call `mask` within scripts if you need to chain commands together. However, if you plan on [running mask with a different maskfile](#running-mask-with-a-different-maskfile), you should consider using the `$MASK` utility instead which allows your scripts to be location-agnostic.
**Example:**
@@ -276,8 +275,11 @@ You can easily call `mask` within scripts if you need to chain commands together
mask install
mask build
mask link
-mask db migrate
-mask start
+# $MASK also works. It's an alias variable for `mask --maskfile `
+# which guarantees your scripts will still work even if they are called from
+# another directory.
+$MASK db migrate
+$MASK start
~~~
```
diff --git a/src/executor.rs b/src/executor.rs
index b9b9076..6e945c6 100644
--- a/src/executor.rs
+++ b/src/executor.rs
@@ -1,10 +1,14 @@
+use std::fs::canonicalize;
use std::io::Result;
+use std::path::{Path, PathBuf};
use std::process;
use std::process::ExitStatus;
+use clap::crate_name;
+
use crate::command::Command;
-pub fn execute_command(cmd: Command) -> Result {
+pub fn execute_command(cmd: Command, maskfile_path: String) -> Result {
let mut child = match cmd.executor.as_ref() {
"js" | "javascript" => {
let mut child = process::Command::new("node");
@@ -38,6 +42,8 @@ pub fn execute_command(cmd: Command) -> Result {
}
};
+ child = add_utility_variables(child, maskfile_path);
+
// Add all required args as environment variables
for arg in cmd.required_args {
child.env(arg.name, arg.val);
@@ -52,3 +58,33 @@ pub fn execute_command(cmd: Command) -> Result {
child.spawn()?.wait()
}
+
+// Add some useful environment variables that scripts can use
+fn add_utility_variables(mut child: process::Command, maskfile_path: String) -> process::Command {
+ let maskfile_path = PathBuf::from(maskfile_path);
+
+ // Find the absolute path to the maskfile
+ let absolute_path = canonicalize(&maskfile_path)
+ .expect("canonicalize maskfile path failed")
+ .to_str()
+ .unwrap()
+ .to_string();
+ let absolute_path = Path::new(&absolute_path);
+ let absolute_path_str = absolute_path.to_str().unwrap();
+
+ // Find the absolute path to the maskfile's parent directory
+ let parent_dir = absolute_path.parent().unwrap().to_str().unwrap();
+
+ // This allows us to call "$MASK command" instead of "mask --maskfile command"
+ // inside scripts so that they can be location-agnostic (not care where they are
+ // called from). This is useful for global maskfiles especially.
+ child.env(
+ "MASK",
+ format!("{} --maskfile {}", crate_name!(), absolute_path_str),
+ );
+ // This allows us to refer to the directory the maskfile lives in which can be handy
+ // for loading relative files to it.
+ child.env("MASKFILE_DIR", parent_dir);
+
+ child
+}
diff --git a/src/main.rs b/src/main.rs
index d3609b2..e91ed45 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,7 +15,7 @@ fn main() {
.about(crate_description!())
.arg(custom_maskfile_path_arg());
- let maskfile = find_maskfile();
+ let (maskfile, maskfile_path) = find_maskfile();
if maskfile.is_err() {
// If the maskfile can't be found, at least parse for --version or --help
cli_app.get_matches();
@@ -31,7 +31,7 @@ fn main() {
std::process::exit(1);
}
- match mask::executor::execute_command(chosen_cmd.unwrap()) {
+ match mask::executor::execute_command(chosen_cmd.unwrap(), maskfile_path) {
Ok(status) => match status.code() {
Some(code) => std::process::exit(code),
None => return,
@@ -40,7 +40,7 @@ fn main() {
}
}
-fn find_maskfile() -> Result {
+fn find_maskfile() -> (Result, String) {
let args: Vec = env::args().collect();
let maybe_maskfile = args.get(1);
@@ -68,7 +68,7 @@ fn find_maskfile() -> Result {
}
}
- maskfile
+ (maskfile, maskfile_path.to_str().unwrap().to_string())
}
fn custom_maskfile_path_arg<'a, 'b>() -> Arg<'a, 'b> {
diff --git a/tests/env_vars_test.rs b/tests/env_vars_test.rs
new file mode 100644
index 0000000..48ed484
--- /dev/null
+++ b/tests/env_vars_test.rs
@@ -0,0 +1,87 @@
+use assert_cmd::prelude::*;
+use predicates::str::contains;
+
+mod common;
+use common::MaskCommandExt;
+
+// NOTE: This test suite depends on the mask binary being available in the current shell
+
+// Using current_dir(".github") to make sure the default maskfile.md can't be found
+mod env_var_mask {
+ use super::*;
+
+ #[test]
+ fn works_from_any_dir() {
+ let (_temp, maskfile_path) = common::maskfile(
+ r#"
+## ci
+
+~~~bash
+$MASK test
+~~~
+
+## test
+
+~~~bash
+echo "tests passed"
+~~~
+"#,
+ );
+
+ common::run_mask(&maskfile_path)
+ .current_dir(".github")
+ .command("ci")
+ .assert()
+ .stdout(contains("tests passed"))
+ .success();
+ }
+
+ #[test]
+ fn set_to_the_correct_value() {
+ let (_temp, maskfile_path) = common::maskfile(
+ r#"
+## run
+
+~~~bash
+echo "mask = $MASK"
+~~~
+"#,
+ );
+
+ common::run_mask(&maskfile_path)
+ .current_dir(".github")
+ .command("run")
+ .assert()
+ // Absolute maskfile path starts with /
+ .stdout(contains("mask = mask --maskfile /"))
+ // And ends with maskfile.md
+ .stdout(contains("maskfile.md"))
+ .success();
+ }
+}
+
+// Using current_dir(".github") to make sure the default maskfile.md can't be found
+mod env_var_maskfile_dir {
+ use super::*;
+
+ #[test]
+ fn set_to_the_correct_value() {
+ let (_temp, maskfile_path) = common::maskfile(
+ r#"
+## run
+
+~~~bash
+echo "maskfile_dir = $MASKFILE_DIR"
+~~~
+"#,
+ );
+
+ common::run_mask(&maskfile_path)
+ .current_dir(".github")
+ .command("run")
+ .assert()
+ // Absolute maskfile path starts with /
+ .stdout(contains("maskfile_dir = /"))
+ .success();
+ }
+}