Add dotenv integration (#306)

This commit is contained in:
Casey Rodarmor 2018-03-05 13:21:35 -08:00 committed by GitHub
parent ec56336df7
commit 70234f6193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 298 additions and 81 deletions

135
Cargo.lock generated
View File

@ -11,6 +11,14 @@ name = "ansi_term"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "assert_matches"
version = "1.1.0"
@ -21,11 +29,32 @@ name = "atty"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.1"
@ -40,6 +69,16 @@ dependencies = [
"tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.30.0"
@ -54,6 +93,25 @@ dependencies = [
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "derive-error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dotenv"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"derive-error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "edit-distance"
version = "2.0.1"
@ -64,6 +122,14 @@ name = "either"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "executable-path"
version = "1.0.0"
@ -90,7 +156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -100,16 +166,17 @@ dependencies = [
name = "just"
version = "0.3.8"
dependencies = [
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"brev 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"edit-distance 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"executable-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -132,7 +199,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.36"
version = "0.2.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -140,16 +207,21 @@ name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -192,11 +264,34 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "target"
version = "1.0.0"
@ -216,7 +311,7 @@ name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -243,6 +338,11 @@ name = "unicode-width"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
@ -298,35 +398,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e772942dccdf11b368c31e044e4fca9189f80a773d2f0808379de65894cbf57"
"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum brev 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "d85c3b7957223c752ff78ffd20a1806b0c7262d9aef85ed470546f16b56a5bb2"
"checksum cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9be26b24e988625409b19736d130f0c7d224f01d06454b5f81d8d23d6c1a618f"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b"
"checksum derive-error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92183014af72c63aea490e66526c712bf1066ac50f66c9f34824f02483ec1d98"
"checksum dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70de3c590ce18df70743cace1cf12565637a0b26fd8b04ef10c7d33fdc66cdc"
"checksum edit-distance 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3bd26878c3d921f89797a4e1a1711919f999a9f6946bb6f5a4ffda126d297b7e"
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum executable-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebc5a6d89e3c90b84e8f33c8737933dda8f1c106b5415900b38b9d433841478"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
"checksum itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "23d53b4c7394338044c3b9c8c5b2caaf7b40ae049ecd321578ebdc2e13738cd1"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b"
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
"checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5"
"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10000465bb0cc031c87a44668991b284fd84c0e6bd945f62d4af04e9e52a222a"
"checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"

View File

@ -8,11 +8,12 @@ homepage = "https://github.com/casey/just"
readme = "crates-io-readme.md"
[dependencies]
ansi_term = "0.10"
ansi_term = "0.11"
assert_matches = "1.1.0"
atty = "0.2.1"
brev = "0.1.6"
clap = "2.0.0"
dotenv = "0.11.0"
edit-distance = "2.0.0"
itertools = "0.7"
lazy_static = "1.0.0"

View File

@ -289,6 +289,34 @@ This is an x86_64 machine
- `env_var_or_default(key, default)` Retrieves the environment variable with name `key`, returning `default` if it is not present.
==== Dotenv Integration
`just` will load environment variables from a file named `.env`. This file can be located in the same directory as your justfile or in a parent directory. These variables are environment variables, not `just` variables, and so must be accessed using `$VARIABLE_NAME` in recipes and backticks.
For example, if your `.env` file contains:
```
# a comment, will be ignored
DATABASE_ADDRESS=localhost:6379
SERVER_PORT=1337
```
And your justfile contains:
```make
serve:
@echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT..."
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
```
`just serve` will output:
```sh
$ just serve
Starting server with database localhost:6379 on port 1337...
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
```
=== Command Evaluation Using Backticks
Backticks can be used to store the result of commands:

View File

@ -4,32 +4,35 @@ use brev;
pub struct AssignmentEvaluator<'a: 'b, 'b> {
pub assignments: &'b Map<&'a str, Expression<'a>>,
pub dotenv: &'b Map<String, String>,
pub dry_run: bool,
pub evaluated: Map<&'a str, String>,
pub exports: &'b Set<&'a str>,
pub overrides: &'b Map<&'b str, &'b str>,
pub quiet: bool,
pub scope: &'b Map<&'a str, String>,
pub shell: &'b str,
pub dry_run: bool,
}
impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
pub fn evaluate_assignments(
assignments: &Map<&'a str, Expression<'a>>,
dotenv: &'b Map<String, String>,
overrides: &Map<&str, &str>,
quiet: bool,
shell: &'a str,
dry_run: bool,
) -> RunResult<'a, Map<&'a str, String>> {
let mut evaluator = AssignmentEvaluator {
assignments: assignments,
evaluated: empty(),
exports: &empty(),
overrides: overrides,
quiet: quiet,
scope: &empty(),
shell: shell,
dry_run: dry_run,
evaluated: empty(),
exports: &empty(),
scope: &empty(),
assignments,
dotenv,
dry_run,
overrides,
quiet,
shell,
};
for name in assignments.keys() {
@ -110,7 +113,7 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
if self.dry_run {
Ok(format!("`{}`", raw))
} else {
Ok(self.run_backtick(raw, token)?)
Ok(self.run_backtick(self.dotenv, raw, token)?)
}
}
Expression::Concatination{ref lhs, ref rhs} => {
@ -125,12 +128,13 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
fn run_backtick(
&self,
dotenv: &Map<String, String>,
raw: &str,
token: &Token<'a>,
) -> RunResult<'a, String> {
let mut cmd = Command::new(self.shell);
cmd.export_environment_variables(self.scope, self.exports)?;
cmd.export_environment_variables(self.scope, dotenv, self.exports)?;
cmd.arg("-cu")
.arg(raw);

View File

@ -17,11 +17,11 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
) -> CompilationResult<'a, ()> {
let mut resolver = AssignmentResolver {
assignments: assignments,
assignment_tokens: assignment_tokens,
stack: empty(),
seen: empty(),
evaluated: empty(),
stack: empty(),
seen: empty(),
evaluated: empty(),
assignments,
assignment_tokens,
};
for name in assignments.keys() {

View File

@ -35,7 +35,7 @@ impl Default for Color {
impl Color {
fn restyle(self, style: Style) -> Color {
Color {
style: style,
style,
..self
}
}

View File

@ -3,7 +3,8 @@ use common::*;
pub trait CommandExt {
fn export_environment_variables<'a>(
&mut self,
scope: &Map<&'a str, String>,
scope: &Map<&'a str, String>,
dotenv: &Map<String, String>,
exports: &Set<&'a str>
) -> RunResult<'a, ()>;
}
@ -11,9 +12,13 @@ pub trait CommandExt {
impl CommandExt for Command {
fn export_environment_variables<'a>(
&mut self,
scope: &Map<&'a str, String>,
scope: &Map<&'a str, String>,
dotenv: &Map<String, String>,
exports: &Set<&'a str>
) -> RunResult<'a, ()> {
for (name, value) in dotenv {
self.env(name, value);
}
for name in exports {
if let Some(value) = scope.get(name) {
self.env(name, value);

View File

@ -11,7 +11,7 @@ impl<'a> CookedString<'a> {
let raw = &token.lexeme[1..token.lexeme.len()-1];
if let TokenKind::RawString = token.kind {
Ok(CookedString{raw: raw, cooked: raw.to_string()})
Ok(CookedString{cooked: raw.to_string(), raw})
} else if let TokenKind::StringToken = token.kind {
let mut cooked = String::new();
let mut escape = false;
@ -36,7 +36,7 @@ impl<'a> CookedString<'a> {
}
cooked.push(c);
}
Ok(CookedString{raw: raw, cooked: cooked})
Ok(CookedString{raw, cooked})
} else {
Err(token.error(CompilationErrorKind::Internal {
message: "cook_string() called on non-string token".to_string()

View File

@ -53,8 +53,11 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
return Err(RuntimeError::UnknownOverrides{overrides: unknown_overrides});
}
let dotenv = load_dotenv()?;
let scope = AssignmentEvaluator::evaluate_assignments(
&self.assignments,
&dotenv,
&configuration.overrides,
configuration.quiet,
configuration.shell,
@ -107,12 +110,12 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
} else {
None
};
return Err(RuntimeError::UnknownRecipes{recipes: missing, suggestion: suggestion});
return Err(RuntimeError::UnknownRecipes{recipes: missing, suggestion});
}
let mut ran = empty();
for (recipe, arguments) in grouped {
self.run_recipe(recipe, arguments, &scope, &mut ran, configuration)?
self.run_recipe(recipe, arguments, &scope, &dotenv, configuration, &mut ran)?
}
Ok(())
@ -123,15 +126,16 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
recipe: &Recipe<'a>,
arguments: &[&'a str],
scope: &Map<&'c str, String>,
ran: &mut Set<&'a str>,
dotenv: &Map<String, String>,
configuration: &Configuration<'a>,
ran: &mut Set<&'a str>,
) -> RunResult<()> {
for dependency_name in &recipe.dependencies {
if !ran.contains(dependency_name) {
self.run_recipe(&self.recipes[dependency_name], &[], scope, ran, configuration)?;
self.run_recipe(&self.recipes[dependency_name], &[], scope, dotenv, configuration, ran)?;
}
}
recipe.run(arguments, scope, &self.exports, configuration)?;
recipe.run(arguments, scope, dotenv, &self.exports, configuration)?;
ran.insert(recipe.name);
Ok(())
}

View File

@ -41,12 +41,12 @@ impl<'a> Lexer<'a> {
pub fn lex(text: &'a str) -> CompilationResult<Vec<Token<'a>>> {
let lexer = Lexer{
tokens: vec![],
text: text,
rest: text,
index: 0,
line: 0,
column: 0,
state: vec![State::Start],
text,
};
lexer.inner()
@ -59,7 +59,7 @@ impl<'a> Lexer<'a> {
line: self.line,
column: self.column,
width: None,
kind: kind,
kind,
}
}
@ -69,9 +69,9 @@ impl<'a> Lexer<'a> {
line: self.line,
column: self.column,
text: self.text,
prefix: prefix,
lexeme: lexeme,
kind: kind,
prefix,
lexeme,
kind,
}
}

17
src/load_dotenv.rs Normal file
View File

@ -0,0 +1,17 @@
use common::*;
use dotenv;
pub fn load_dotenv() -> RunResult<'static, Map<String, String>> {
match dotenv::dotenv_iter() {
Ok(iter) => {
let result: dotenv::Result<Map<String, String>> = iter.collect();
result.map_err(|dotenv_error| RuntimeError::Dotenv{dotenv_error})
}
Err(dotenv_error) => if dotenv_error.not_found() {
Ok(Map::new())
} else {
Err(RuntimeError::Dotenv{dotenv_error})
}
}
}

View File

@ -3,6 +3,7 @@ extern crate lazy_static;
extern crate ansi_term;
extern crate brev;
extern crate clap;
extern crate dotenv;
extern crate edit_distance;
extern crate itertools;
extern crate libc;
@ -22,6 +23,7 @@ mod command_ext;
mod compilation_error;
mod configuration;
mod cooked_string;
mod load_dotenv;
mod expression;
mod fragment;
mod functions;
@ -63,6 +65,8 @@ mod common {
pub use expression::Expression;
pub use fragment::Fragment;
pub use justfile::Justfile;
pub use lexer::Lexer;
pub use load_dotenv::load_dotenv;
pub use misc::{default, empty};
pub use parameter::Parameter;
pub use parser::Parser;
@ -70,7 +74,6 @@ mod common {
pub use recipe::Recipe;
pub use recipe_resolver::RecipeResolver;
pub use runtime_error::{RuntimeError, RunResult};
pub use lexer::Lexer;
pub use shebang::Shebang;
pub use token::{Token, TokenKind};
}

View File

@ -22,12 +22,12 @@ impl<'a> Parser<'a> {
pub fn new(text: &'a str, tokens: Vec<Token<'a>>) -> Parser<'a> {
Parser {
text: text,
tokens: itertools::put_back(tokens),
recipes: empty(),
assignments: empty(),
assignment_tokens: empty(),
exports: empty(),
text,
}
}
@ -151,10 +151,10 @@ impl<'a> Parser<'a> {
parsed_variadic_parameter = variadic;
parameters.push(Parameter {
default: default,
name: parameter.lexeme,
token: parameter,
variadic: variadic,
default,
variadic,
});
}
@ -237,13 +237,13 @@ impl<'a> Parser<'a> {
line_number: name.line,
name: name.lexeme,
doc: doc.map(|t| t.lexeme[1..].trim()),
dependencies: dependencies,
dependency_tokens: dependency_tokens,
parameters: parameters,
private: &name.lexeme[0..1] == "_",
lines: lines,
shebang: shebang,
quiet: quiet,
dependencies,
dependency_tokens,
lines,
parameters,
quiet,
shebang,
});
Ok(())

View File

@ -12,8 +12,8 @@ fn error_from_signal(
exit_status: ExitStatus
) -> RuntimeError {
match Platform::signal_from_exit_status(exit_status) {
Some(signal) => RuntimeError::Signal{recipe: recipe, line_number: line_number, signal: signal},
None => RuntimeError::Unknown{recipe: recipe, line_number: line_number},
Some(signal) => RuntimeError::Signal{recipe, line_number, signal},
None => RuntimeError::Unknown{recipe, line_number},
}
}
@ -52,6 +52,7 @@ impl<'a> Recipe<'a> {
&self,
arguments: &[&'a str],
scope: &Map<&'a str, String>,
dotenv: &Map<String, String>,
exports: &Set<&'a str>,
configuration: &Configuration,
) -> RunResult<'a, ()> {
@ -84,14 +85,15 @@ impl<'a> Recipe<'a> {
}
let mut evaluator = AssignmentEvaluator {
evaluated: empty(),
scope: scope,
exports: exports,
assignments: &empty(),
dry_run: configuration.dry_run,
evaluated: empty(),
overrides: &empty(),
quiet: configuration.quiet,
shell: configuration.shell,
dry_run: configuration.dry_run,
dotenv,
exports,
scope,
};
if self.shebang {
@ -153,13 +155,13 @@ impl<'a> Recipe<'a> {
let mut command = Platform::make_shebang_command(&path, interpreter, argument)
.map_err(|output_error| RuntimeError::Cygpath{recipe: self.name, output_error})?;
command.export_environment_variables(scope, exports)?;
command.export_environment_variables(scope, dotenv, exports)?;
// run it!
match command.status() {
Ok(exit_status) => if let Some(code) = exit_status.code() {
if code != 0 {
return Err(RuntimeError::Code{recipe: self.name, line_number: None, code: code})
return Err(RuntimeError::Code{recipe: self.name, line_number: None, code})
}
} else {
return Err(error_from_signal(self.name, None, exit_status))
@ -168,7 +170,7 @@ impl<'a> Recipe<'a> {
recipe: self.name,
command: interpreter.to_string(),
argument: argument.map(String::from),
io_error: io_error
io_error,
})
};
} else {
@ -228,20 +230,22 @@ impl<'a> Recipe<'a> {
cmd.stdout(Stdio::null());
}
cmd.export_environment_variables(scope, exports)?;
cmd.export_environment_variables(scope, dotenv, exports)?;
match cmd.status() {
Ok(exit_status) => if let Some(code) = exit_status.code() {
if code != 0 {
return Err(RuntimeError::Code{
recipe: self.name, line_number: Some(line_number), code: code
recipe: self.name, line_number: Some(line_number), code,
});
}
} else {
return Err(error_from_signal(self.name, Some(line_number), exit_status));
},
Err(io_error) => return Err(RuntimeError::IoError{
recipe: self.name, io_error: io_error}),
recipe: self.name,
io_error,
}),
};
}
}

View File

@ -16,10 +16,10 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
text: &'a str,
) -> CompilationResult<'a, ()> {
let mut resolver = RecipeResolver {
seen: empty(),
stack: empty(),
resolved: empty(),
recipes: recipes,
seen: empty(),
stack: empty(),
resolved: empty(),
recipes,
};
for recipe in recipes.values() {
@ -44,14 +44,14 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
for (function, argc) in expression.functions() {
if let Err(error) = ::functions::resolve_function(function, argc) {
return Err(CompilationError {
text: text,
index: error.index,
line: error.line,
column: error.column,
width: error.width,
kind: UnknownFunction {
function: &text[error.index..error.index + error.width.unwrap()],
}
},
text,
});
}
}
@ -62,14 +62,14 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
if undefined {
let error = variable.error(UndefinedVariable{variable: name});
return Err(CompilationError {
text: text,
index: error.index,
line: error.line,
column: error.column,
width: error.width,
kind: UndefinedVariable {
variable: &text[error.index..error.index + error.width.unwrap()],
}
},
text,
});
}
}

View File

@ -349,11 +349,11 @@ pub fn run() {
dry_run: matches.is_present("DRY-RUN"),
evaluate: matches.is_present("EVALUATE"),
highlight: matches.is_present("HIGHLIGHT"),
overrides: overrides,
quiet: matches.is_present("QUIET"),
shell: matches.value_of("SHELL").unwrap(),
color: color,
verbose: matches.is_present("VERBOSE"),
color,
overrides,
};
if let Err(run_error) = justfile.run(&arguments, &configuration) {

View File

@ -1,5 +1,7 @@
use common::*;
use dotenv;
use brev::OutputError;
use misc::{And, Or, maybe_s, Tick, ticks, write_error_context};
@ -25,6 +27,7 @@ pub enum RuntimeError<'a> {
Backtick{token: Token<'a>, output_error: OutputError},
Code{recipe: &'a str, line_number: Option<usize>, code: i32},
Cygpath{recipe: &'a str, output_error: OutputError},
Dotenv{dotenv_error: dotenv::Error},
FunctionCall{token: Token<'a>, message: String},
Internal{message: String},
IoError{recipe: &'a str, io_error: io::Error},
@ -118,6 +121,9 @@ impl<'a> Display for RuntimeError<'a> {
but output was not utf8: {}", recipe, utf8_error)?;
}
},
Dotenv{ref dotenv_error} => {
write!(f, "Failed to load .env: {}\n", dotenv_error)?;
}
FunctionCall{ref token, ref message} => {
write!(f, "Call to function `{}` failed: {}\n", token.lexeme, message)?;
error_token = Some(token);

View File

@ -14,12 +14,12 @@ pub struct Token<'a> {
impl<'a> Token<'a> {
pub fn error(&self, kind: CompilationErrorKind<'a>) -> CompilationError<'a> {
CompilationError {
text: self.text,
column: self.column + self.prefix.len(),
index: self.index + self.prefix.len(),
line: self.line,
column: self.column + self.prefix.len(),
text: self.text,
width: Some(self.lexeme.len()),
kind: kind,
kind,
}
}
}

View File

@ -51,9 +51,14 @@ fn integration_test(
let tmp = TempDir::new("just-integration")
.unwrap_or_else(
|err| panic!("integration test: failed to create temporary directory: {}", err));
let mut path = tmp.path().to_path_buf();
path.push("justfile");
brev::dump(path, justfile);
let mut justfile_path = tmp.path().to_path_buf();
justfile_path.push("justfile");
brev::dump(justfile_path, justfile);
let mut dotenv_path = tmp.path().to_path_buf();
dotenv_path.push(".env");
brev::dump(dotenv_path, "DOTENV_KEY=dotenv-value");
let output = process::Command::new(&executable_path("just"))
.current_dir(tmp.path())
@ -1739,3 +1744,30 @@ echo:
stderr: "echo 1\n",
status: EXIT_SUCCESS,
}
integration_test! {
name: dotenv_variable_in_recipe,
justfile: "
#
echo:
echo $DOTENV_KEY
",
args: (),
stdout: "dotenv-value\n",
stderr: "echo $DOTENV_KEY\n",
status: EXIT_SUCCESS,
}
integration_test! {
name: dotenv_variable_in_backtick,
justfile: "
#
X=`echo $DOTENV_KEY`
echo:
echo {{X}}
",
args: (),
stdout: "dotenv-value\n",
stderr: "echo dotenv-value\n",
status: EXIT_SUCCESS,
}