Skip to content

Commit 8525d41

Browse files
committed
Add env attribute to set environment variables for recipes
The env attribute accepts two arguments (env_var_name, value) and can be used multiple times per recipe to set environment variables: [env('API_KEY', 'secret')] [env('LOG_LEVEL', 'debug')] deploy: ./deploy.sh
1 parent 5732ee0 commit 8525d41

4 files changed

Lines changed: 119 additions & 1 deletion

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,6 +2158,7 @@ change their behavior.
21582158
| `[confirm(PROMPT)]`<sup>1.23.0</sup> | recipe | Require confirmation prior to executing recipe with a custom prompt. |
21592159
| `[default]`<sup>1.43.0</sup> | recipe | Use recipe as module's default recipe. |
21602160
| `[doc(DOC)]`<sup>1.27.0</sup> | module, recipe | Set recipe or module's [documentation comment](#documentation-comments) to `DOC`. |
2161+
| `[env(ENV_VAR, VALUE)]` <sup>1.44</sup> | recipe | Set env vars that apply only to this recipe. |
21612162
| `[extension(EXT)]`<sup>1.32.0</sup> | recipe | Set shebang recipe script's file extension to `EXT`. `EXT` should include a period if one is desired. |
21622163
| `[group(NAME)]`<sup>1.27.0</sup> | module, recipe | Put recipe or module in in [group](#groups) `NAME`. |
21632164
| `[linux]`<sup>1.8.0</sup> | recipe | Enable recipe on Linux. |
@@ -2537,6 +2538,17 @@ test $RUST_BACKTRACE="1":
25372538
cargo test
25382539
```
25392540

2541+
Or you can use the `[env(env_var, value)]` attribute to set environment variables that
2542+
apply only to this recipe:
2543+
2544+
```just
2545+
2546+
[env("RUST_BACKTRACE", "1")]
2547+
test:
2548+
# will print a stack trace if it crashes
2549+
cargo test
2550+
```
2551+
25402552
Exported variables and parameters are not exported to backticks in the same scope.
25412553

25422554
```just

src/attribute.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub(crate) enum Attribute<'src> {
2323
Confirm(Option<StringLiteral<'src>>),
2424
Default,
2525
Doc(Option<StringLiteral<'src>>),
26+
Env(StringLiteral<'src>, StringLiteral<'src>),
2627
ExitMessage,
2728
Extension(StringLiteral<'src>),
2829
Group(StringLiteral<'src>),
@@ -61,6 +62,7 @@ impl AttributeDiscriminant {
6162
Self::Confirm | Self::Doc => 0..=1,
6263
Self::Script => 0..=usize::MAX,
6364
Self::Arg | Self::Extension | Self::Group | Self::WorkingDirectory => 1..=1,
65+
Self::Env => 2..=2,
6466
Self::Metadata => 1..=usize::MAX,
6567
}
6668
}
@@ -179,6 +181,20 @@ impl<'src> Attribute<'src> {
179181
AttributeDiscriminant::Confirm => Self::Confirm(arguments.into_iter().next()),
180182
AttributeDiscriminant::Default => Self::Default,
181183
AttributeDiscriminant::Doc => Self::Doc(arguments.into_iter().next()),
184+
AttributeDiscriminant::Env => {
185+
let [key, value]: [StringLiteral; 2] =
186+
arguments
187+
.try_into()
188+
.map_err(|arguments: Vec<StringLiteral>| {
189+
name.error(CompileErrorKind::AttributeArgumentCountMismatch {
190+
attribute: name,
191+
found: arguments.len(),
192+
min: 2,
193+
max: 2,
194+
})
195+
})?;
196+
Self::Env(key, value)
197+
}
182198
AttributeDiscriminant::ExitMessage => Self::ExitMessage,
183199
AttributeDiscriminant::Extension => Self::Extension(arguments.into_iter().next().unwrap()),
184200
AttributeDiscriminant::Group => Self::Group(arguments.into_iter().next().unwrap()),
@@ -243,7 +259,7 @@ impl<'src> Attribute<'src> {
243259
pub(crate) fn repeatable(&self) -> bool {
244260
matches!(
245261
self,
246-
Attribute::Arg { .. } | Attribute::Group(_) | Attribute::Metadata(_),
262+
Attribute::Arg { .. } | Attribute::Env(_, _) | Attribute::Group(_) | Attribute::Metadata(_),
247263
)
248264
}
249265
}
@@ -307,6 +323,7 @@ impl Display for Attribute<'_> {
307323
| Self::Extension(argument)
308324
| Self::Group(argument)
309325
| Self::WorkingDirectory(argument) => write!(f, "({argument})")?,
326+
Self::Env(key, value) => write!(f, "({key}, {value})")?,
310327
Self::Metadata(arguments) => {
311328
write!(f, "(")?;
312329
for (i, argument) in arguments.iter().enumerate() {

src/recipe.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,12 @@ impl<'src, D> Recipe<'src, D> {
334334
cmd.stdout(Stdio::null());
335335
}
336336

337+
for attribute in &self.attributes {
338+
if let Attribute::Env(key, value) = attribute {
339+
cmd.env(&key.cooked, &value.cooked);
340+
}
341+
}
342+
337343
cmd.export(
338344
&context.module.settings,
339345
context.dotenv,
@@ -473,6 +479,12 @@ impl<'src, D> Recipe<'src, D> {
473479
command.args(positional);
474480
}
475481

482+
for attribute in &self.attributes {
483+
if let Attribute::Env(key, value) = attribute {
484+
command.env(&key.cooked, &value.cooked);
485+
}
486+
}
487+
476488
command.export(
477489
&context.module.settings,
478490
context.dotenv,

tests/attributes.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,80 @@ fn shell_expanded_strings_can_be_used_in_attributes() {
323323
)
324324
.run();
325325
}
326+
327+
#[test]
328+
fn env_attribute_single() {
329+
Test::new()
330+
.justfile(
331+
"
332+
[env('MY_VAR', 'my_value')]
333+
foo:
334+
echo $MY_VAR
335+
",
336+
)
337+
.stdout("my_value\n")
338+
.stderr("echo $MY_VAR\n")
339+
.run();
340+
}
341+
342+
#[test]
343+
fn env_attribute_multiple() {
344+
Test::new()
345+
.justfile(
346+
"
347+
[env('VAR1', 'value1')]
348+
[env('VAR2', 'value 2')]
349+
foo:
350+
echo $VAR1 $VAR2
351+
",
352+
)
353+
.stdout("value1 value 2\n")
354+
.stderr("echo $VAR1 $VAR2\n")
355+
.run();
356+
}
357+
358+
#[test]
359+
fn env_attribute_1_arg() {
360+
Test::new()
361+
.justfile(
362+
"
363+
[env('MY_VAR')]
364+
foo:
365+
echo bar
366+
",
367+
)
368+
.stderr(
369+
"
370+
error: Attribute `env` got 1 argument but takes 2 arguments
371+
——▶ justfile:1:2
372+
373+
1 │ [env('MY_VAR')]
374+
│ ^^^
375+
",
376+
)
377+
.status(EXIT_FAILURE)
378+
.run();
379+
}
380+
381+
#[test]
382+
fn env_attribute_3_args() {
383+
Test::new()
384+
.justfile(
385+
"
386+
[env('A', 'B', 'C')]
387+
foo:
388+
echo bar
389+
",
390+
)
391+
.stderr(
392+
"
393+
error: Attribute `env` got 3 arguments but takes 2 arguments
394+
——▶ justfile:1:2
395+
396+
1 │ [env('A', 'B', 'C')]
397+
│ ^^^
398+
",
399+
)
400+
.status(EXIT_FAILURE)
401+
.run();
402+
}

0 commit comments

Comments
 (0)