Add set dotenv-required to require an environment file (#2116)

This commit is contained in:
Casey Rodarmor 2024-05-30 18:12:07 -05:00 committed by GitHub
parent d38c1add13
commit f2201d8684
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 276 additions and 190 deletions

View File

@ -812,6 +812,7 @@ foo:
| `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. | | `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. |
| `dotenv-load` | boolean | `false` | Load a `.env` file, if present. | | `dotenv-load` | boolean | `false` | Load a `.env` file, if present. |
| `dotenv-path` | string | - | Load a `.env` file from a custom path and error if not present. Overrides `dotenv-filename`. | | `dotenv-path` | string | - | Load a `.env` file from a custom path and error if not present. Overrides `dotenv-filename`. |
| `dotenv-required` | boolean | `false` | Error if a `.env` file isn't found. |
| `export` | boolean | `false` | Export all variables as environment variables. | | `export` | boolean | `false` | Export all variables as environment variables. |
| `fallback` | boolean | `false` | Search `justfile` in parent directory if the first recipe on the command line is not found. | | `fallback` | boolean | `false` | Search `justfile` in parent directory if the first recipe on the command line is not found. |
| `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. | | `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. |
@ -877,17 +878,25 @@ bar
#### Dotenv Settings #### Dotenv Settings
If `dotenv-load`, `dotenv-filename` or `dotenv-path` is set, `just` will load If any of `dotenv-load`, `dotenv-filename`, `dotenv-path`, or `dotenv-required`
environment variables from a file. are set, `just` will try to load environment variables from a file.
If `dotenv-path` is set, `just` will look for a file at the given path. It is If `dotenv-path` is set, `just` will look for a file at the given path, which
an error if a dotenv file is not found at `dotenv-path`, but not an error if a may be absolute, or relative to the working directory.
dotenv file is not found with `dotenv-filename`.
Otherwise, `just` looks for a file named `.env` by default, unless If `dotenv-filename` is set `just` will look for a file at the given path,
`dotenv-filename` set, in which case the value of `dotenv-filename` is used. relative to the working directory and each of its ancestors.
This file can be located in the same directory as your `justfile` or in a
parent directory. If `dotenv-filename` is not set, but `dotenv-load` or `dotenv-required` are
set, just will look for a file named `.env`, relative to the working directory
and each of its ancestors.
`dotenv-filename` and `dotenv-path` and similar, but `dotenv-path` is only
checked relative to the working directory, whereas `dotenv-filename` is checked
relative to the working directory and each of its ancestors.
It is not an error if an environment file is not found, unless
`dotenv-required` is set.
The loaded variables are environment variables, not `just` variables, and so The loaded variables are environment variables, not `just` variables, and so
must be accessed using `$VARIABLE_NAME` in recipes and backticks. must be accessed using `$VARIABLE_NAME` in recipes and backticks.

View File

@ -79,6 +79,7 @@ pub(crate) enum Error<'src> {
Dotenv { Dotenv {
dotenv_error: dotenvy::Error, dotenv_error: dotenvy::Error,
}, },
DotenvRequired,
DumpJson { DumpJson {
serde_json_error: serde_json::Error, serde_json_error: serde_json::Error,
}, },
@ -347,6 +348,9 @@ impl<'src> ColorDisplay for Error<'src> {
Dotenv { dotenv_error } => { Dotenv { dotenv_error } => {
write!(f, "Failed to load environment file: {dotenv_error}")?; write!(f, "Failed to load environment file: {dotenv_error}")?;
} }
DotenvRequired => {
write!(f, "Dotenv file not found")?;
}
DumpJson { serde_json_error } => { DumpJson { serde_json_error } => {
write!(f, "Failed to dump JSON to stdout: {serde_json_error}")?; write!(f, "Failed to dump JSON to stdout: {serde_json_error}")?;
} }

View File

@ -10,6 +10,7 @@ pub(crate) enum Keyword {
DotenvFilename, DotenvFilename,
DotenvLoad, DotenvLoad,
DotenvPath, DotenvPath,
DotenvRequired,
Else, Else,
Export, Export,
Fallback, Fallback,

View File

@ -1,7 +1,5 @@
use super::*; use super::*;
const DEFAULT_DOTENV_FILENAME: &str = ".env";
pub(crate) fn load_dotenv( pub(crate) fn load_dotenv(
config: &Config, config: &Config,
settings: &Settings, settings: &Settings,
@ -17,16 +15,21 @@ pub(crate) fn load_dotenv(
.as_ref() .as_ref()
.or(settings.dotenv_path.as_ref()); .or(settings.dotenv_path.as_ref());
if !settings.dotenv_load.unwrap_or_default() && dotenv_filename.is_none() && dotenv_path.is_none() if !settings.dotenv_load
&& dotenv_filename.is_none()
&& dotenv_path.is_none()
&& !settings.dotenv_required
{ {
return Ok(BTreeMap::new()); return Ok(BTreeMap::new());
} }
if let Some(path) = dotenv_path { if let Some(path) = dotenv_path {
if path.is_file() {
return load_from_file(&working_directory.join(path)); return load_from_file(&working_directory.join(path));
} }
}
let filename = dotenv_filename.map_or(DEFAULT_DOTENV_FILENAME, |s| s.as_str()); let filename = dotenv_filename.map_or(".env", |s| s.as_str());
for directory in working_directory.ancestors() { for directory in working_directory.ancestors() {
let path = directory.join(filename); let path = directory.join(filename);
@ -35,8 +38,12 @@ pub(crate) fn load_dotenv(
} }
} }
if settings.dotenv_required {
Err(Error::DotenvRequired)
} else {
Ok(BTreeMap::new()) Ok(BTreeMap::new())
} }
}
fn load_from_file(path: &Path) -> RunResult<'static, BTreeMap<String, String>> { fn load_from_file(path: &Path) -> RunResult<'static, BTreeMap<String, String>> {
let iter = dotenvy::from_path_iter(path)?; let iter = dotenvy::from_path_iter(path)?;

View File

@ -284,6 +284,7 @@ impl<'src> Node<'src> for Set<'src> {
Setting::AllowDuplicateRecipes(value) Setting::AllowDuplicateRecipes(value)
| Setting::AllowDuplicateVariables(value) | Setting::AllowDuplicateVariables(value)
| Setting::DotenvLoad(value) | Setting::DotenvLoad(value)
| Setting::DotenvRequired(value)
| Setting::Export(value) | Setting::Export(value)
| Setting::Fallback(value) | Setting::Fallback(value)
| Setting::PositionalArguments(value) | Setting::PositionalArguments(value)

View File

@ -917,6 +917,7 @@ impl<'run, 'src> Parser<'run, 'src> {
Some(Setting::AllowDuplicateVariables(self.parse_set_bool()?)) Some(Setting::AllowDuplicateVariables(self.parse_set_bool()?))
} }
Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)), Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)),
Keyword::DotenvRequired => Some(Setting::DotenvRequired(self.parse_set_bool()?)),
Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)), Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)),
Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)), Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)),
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)), Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),

View File

@ -7,6 +7,7 @@ pub(crate) enum Setting<'src> {
DotenvFilename(String), DotenvFilename(String),
DotenvLoad(bool), DotenvLoad(bool),
DotenvPath(String), DotenvPath(String),
DotenvRequired(bool),
Export(bool), Export(bool),
Fallback(bool), Fallback(bool),
IgnoreComments(bool), IgnoreComments(bool),
@ -24,6 +25,7 @@ impl<'src> Display for Setting<'src> {
Self::AllowDuplicateRecipes(value) Self::AllowDuplicateRecipes(value)
| Self::AllowDuplicateVariables(value) | Self::AllowDuplicateVariables(value)
| Self::DotenvLoad(value) | Self::DotenvLoad(value)
| Self::DotenvRequired(value)
| Self::Export(value) | Self::Export(value)
| Self::Fallback(value) | Self::Fallback(value)
| Self::IgnoreComments(value) | Self::IgnoreComments(value)

View File

@ -10,8 +10,9 @@ pub(crate) struct Settings<'src> {
pub(crate) allow_duplicate_recipes: bool, pub(crate) allow_duplicate_recipes: bool,
pub(crate) allow_duplicate_variables: bool, pub(crate) allow_duplicate_variables: bool,
pub(crate) dotenv_filename: Option<String>, pub(crate) dotenv_filename: Option<String>,
pub(crate) dotenv_load: Option<bool>, pub(crate) dotenv_load: bool,
pub(crate) dotenv_path: Option<PathBuf>, pub(crate) dotenv_path: Option<PathBuf>,
pub(crate) dotenv_required: bool,
pub(crate) export: bool, pub(crate) export: bool,
pub(crate) fallback: bool, pub(crate) fallback: bool,
pub(crate) ignore_comments: bool, pub(crate) ignore_comments: bool,
@ -39,11 +40,14 @@ impl<'src> Settings<'src> {
settings.dotenv_filename = Some(filename); settings.dotenv_filename = Some(filename);
} }
Setting::DotenvLoad(dotenv_load) => { Setting::DotenvLoad(dotenv_load) => {
settings.dotenv_load = Some(dotenv_load); settings.dotenv_load = dotenv_load;
} }
Setting::DotenvPath(path) => { Setting::DotenvPath(path) => {
settings.dotenv_path = Some(PathBuf::from(path)); settings.dotenv_path = Some(PathBuf::from(path));
} }
Setting::DotenvRequired(dotenv_required) => {
settings.dotenv_required = dotenv_required;
}
Setting::Export(export) => { Setting::Export(export) => {
settings.export = export; settings.export = export;
} }

View File

@ -47,16 +47,21 @@ test! {
status: 2, status: 2,
} }
test! { #[test]
name: env_is_loaded, fn env_is_loaded() {
justfile: " Test::new()
.justfile(
"
set dotenv-load set dotenv-load
x: x:
echo XYZ echo XYZ
", ",
args: ("--command", "sh", "-c", "printf $DOTENV_KEY"), )
stdout: "dotenv-value", .args(["--command", "sh", "-c", "printf $DOTENV_KEY"])
.write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value")
.run();
} }
test! { test! {

View File

@ -12,40 +12,54 @@ fn dotenv() {
.run(); .run();
} }
test! { #[test]
name: set_false, fn set_false() {
justfile: r#" Test::new()
.justfile(
r#"
set dotenv-load := false set dotenv-load := false
foo: @foo:
if [ -n "${DOTENV_KEY+1}" ]; then echo defined; else echo undefined; fi if [ -n "${DOTENV_KEY+1}" ]; then echo defined; else echo undefined; fi
"#, "#,
stdout: "undefined\n", )
stderr: "if [ -n \"${DOTENV_KEY+1}\" ]; then echo defined; else echo undefined; fi\n", .write(".env", "DOTENV_KEY=dotenv-value")
.stdout("undefined\n")
.run();
} }
test! { #[test]
name: set_implicit, fn set_implicit() {
justfile: r#" Test::new()
.justfile(
"
set dotenv-load set dotenv-load
foo: foo:
echo $DOTENV_KEY echo $DOTENV_KEY
"#, ",
stdout: "dotenv-value\n", )
stderr: "echo $DOTENV_KEY\n", .write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value\n")
.stderr("echo $DOTENV_KEY\n")
.run();
} }
test! { #[test]
name: set_true, fn set_true() {
justfile: r#" Test::new()
.justfile(
"
set dotenv-load := true set dotenv-load := true
foo: foo:
echo $DOTENV_KEY echo $DOTENV_KEY
"#, ",
stdout: "dotenv-value\n", )
stderr: "echo $DOTENV_KEY\n", .write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value\n")
.stderr("echo $DOTENV_KEY\n")
.run();
} }
#[test] #[test]
@ -57,28 +71,24 @@ fn no_warning() {
echo ${DOTENV_KEY:-unset} echo ${DOTENV_KEY:-unset}
", ",
) )
.write(".env", "DOTENV_KEY=dotenv-value")
.stdout("unset\n") .stdout("unset\n")
.stderr("echo ${DOTENV_KEY:-unset}\n") .stderr("echo ${DOTENV_KEY:-unset}\n")
.run(); .run();
} }
#[test] #[test]
fn path_not_found() { fn dotenv_required() {
Test::new() Test::new()
.justfile( .justfile(
" "
set dotenv-required
foo: foo:
echo $JUST_TEST_VARIABLE
", ",
) )
.args(["--dotenv-path", ".env.prod"]) .stderr("error: Dotenv file not found\n")
.stderr(if cfg!(windows) { .status(1)
"error: Failed to load environment file: The system cannot find the file specified. (os \
error 2)\n"
} else {
"error: Failed to load environment file: No such file or directory (os error 2)\n"
})
.status(EXIT_FAILURE)
.run(); .run();
} }
@ -227,12 +237,12 @@ fn program_argument_has_priority_for_dotenv_filename() {
fn program_argument_has_priority_for_dotenv_path() { fn program_argument_has_priority_for_dotenv_path() {
Test::new() Test::new()
.justfile( .justfile(
r#" "
set dotenv-path := "subdir/.env" set dotenv-path := 'subdir/.env'
foo: foo:
@echo $JUST_TEST_VARIABLE @echo $JUST_TEST_VARIABLE
"#, ",
) )
.tree(tree! { .tree(tree! {
subdir: { subdir: {
@ -257,8 +267,111 @@ fn dotenv_path_is_relative_to_working_directory() {
@echo $DOTENV_KEY @echo $DOTENV_KEY
", ",
) )
.write(".env", "DOTENV_KEY=dotenv-value")
.tree(tree! { subdir: { } }) .tree(tree! { subdir: { } })
.current_dir("subdir") .current_dir("subdir")
.stdout("dotenv-value\n") .stdout("dotenv-value\n")
.run(); .run();
} }
#[test]
fn dotenv_variable_in_recipe() {
Test::new()
.justfile(
"
set dotenv-load
echo:
echo $DOTENV_KEY
",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value\n")
.stderr("echo $DOTENV_KEY\n")
.run();
}
#[test]
fn dotenv_variable_in_backtick() {
Test::new()
.justfile(
"
set dotenv-load
X:=`echo $DOTENV_KEY`
echo:
echo {{X}}
",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value\n")
.stderr("echo dotenv-value\n")
.run();
}
#[test]
fn dotenv_variable_in_function_in_recipe() {
Test::new()
.justfile(
"
set dotenv-load
echo:
echo {{env_var_or_default('DOTENV_KEY', 'foo')}}
echo {{env_var('DOTENV_KEY')}}
",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value\ndotenv-value\n")
.stderr("echo dotenv-value\necho dotenv-value\n")
.run();
}
#[test]
fn dotenv_variable_in_function_in_backtick() {
Test::new()
.justfile(
"
set dotenv-load
X:=env_var_or_default('DOTENV_KEY', 'foo')
Y:=env_var('DOTENV_KEY')
echo:
echo {{X}}
echo {{Y}}
",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.stdout("dotenv-value\ndotenv-value\n")
.stderr("echo dotenv-value\necho dotenv-value\n")
.run();
}
#[test]
fn no_dotenv() {
Test::new()
.justfile(
"
X:=env_var_or_default('DOTENV_KEY', 'DEFAULT')
echo:
echo {{X}}
",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.arg("--no-dotenv")
.stdout("DEFAULT\n")
.stderr("echo DEFAULT\n")
.run();
}
#[test]
fn dotenv_env_var_override() {
Test::new()
.justfile(
"
echo:
echo $DOTENV_KEY
",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.env("DOTENV_KEY", "not-the-dotenv-value")
.stdout("not-the-dotenv-value\n")
.stderr("echo $DOTENV_KEY\n")
.run();
}

View File

@ -46,8 +46,9 @@ fn alias() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"positional_arguments": false, "positional_arguments": false,
@ -84,8 +85,9 @@ fn assignment() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -136,8 +138,9 @@ fn body() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -200,8 +203,9 @@ fn dependencies() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -302,8 +306,9 @@ fn dependency_argument() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -366,8 +371,9 @@ fn duplicate_recipes() {
"allow_duplicate_recipes": true, "allow_duplicate_recipes": true,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -408,8 +414,9 @@ fn duplicate_variables() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": true, "allow_duplicate_variables": true,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -453,8 +460,9 @@ fn doc_comment() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -484,8 +492,9 @@ fn empty_justfile() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -636,8 +645,9 @@ fn parameters() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -721,8 +731,9 @@ fn priors() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -766,8 +777,9 @@ fn private() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -811,8 +823,9 @@ fn quiet() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -870,6 +883,7 @@ fn settings() {
"dotenv_filename": "filename", "dotenv_filename": "filename",
"dotenv_load": true, "dotenv_load": true,
"dotenv_path": "path", "dotenv_path": "path",
"dotenv_required": false,
"export": true, "export": true,
"fallback": true, "fallback": true,
"ignore_comments": true, "ignore_comments": true,
@ -919,8 +933,9 @@ fn shebang() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -964,8 +979,9 @@ fn simple() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"ignore_comments": false, "ignore_comments": false,
@ -1012,8 +1028,9 @@ fn attribute() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"positional_arguments": false, "positional_arguments": false,
@ -1073,8 +1090,9 @@ fn module() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"positional_arguments": false, "positional_arguments": false,
@ -1093,8 +1111,9 @@ fn module() {
"allow_duplicate_recipes": false, "allow_duplicate_recipes": false,
"allow_duplicate_variables": false, "allow_duplicate_variables": false,
"dotenv_filename": null, "dotenv_filename": null,
"dotenv_load": null, "dotenv_load": false,
"dotenv_path": null, "dotenv_path": null,
"dotenv_required": false,
"export": false, "export": false,
"fallback": false, "fallback": false,
"positional_arguments": false, "positional_arguments": false,

View File

@ -1589,84 +1589,6 @@ echo:
stderr: "echo 1\n", stderr: "echo 1\n",
} }
test! {
name: dotenv_variable_in_recipe,
justfile: "
#
set dotenv-load
echo:
echo $DOTENV_KEY
",
stdout: "dotenv-value\n",
stderr: "echo $DOTENV_KEY\n",
}
test! {
name: dotenv_variable_in_backtick,
justfile: "
#
set dotenv-load
X:=`echo $DOTENV_KEY`
echo:
echo {{X}}
",
stdout: "dotenv-value\n",
stderr: "echo dotenv-value\n",
}
test! {
name: dotenv_variable_in_function_in_recipe,
justfile: "
#
set dotenv-load
echo:
echo {{env_var_or_default('DOTENV_KEY', 'foo')}}
echo {{env_var('DOTENV_KEY')}}
",
stdout: "dotenv-value\ndotenv-value\n",
stderr: "echo dotenv-value\necho dotenv-value\n",
}
test! {
name: dotenv_variable_in_function_in_backtick,
justfile: "
#
set dotenv-load
X:=env_var_or_default('DOTENV_KEY', 'foo')
Y:=env_var('DOTENV_KEY')
echo:
echo {{X}}
echo {{Y}}
",
stdout: "dotenv-value\ndotenv-value\n",
stderr: "echo dotenv-value\necho dotenv-value\n",
}
test! {
name: no_dotenv,
justfile: "
#
X:=env_var_or_default('DOTENV_KEY', 'DEFAULT')
echo:
echo {{X}}
",
args: ("--no-dotenv"),
stdout: "DEFAULT\n",
stderr: "echo DEFAULT\n",
}
test! {
name: dotenv_env_var_override,
justfile: "
#
echo:
echo $DOTENV_KEY
",
env: {"DOTENV_KEY": "not-the-dotenv-value",},
stdout: "not-the-dotenv-value\n",
stderr: "echo $DOTENV_KEY\n",
}
test! { test! {
name: invalid_escape_sequence_message, name: invalid_escape_sequence_message,
justfile: r#" justfile: r#"

View File

@ -515,7 +515,6 @@ fn missing_optional_modules_do_not_conflict() {
#[test] #[test]
fn root_dotenv_is_available_to_submodules() { fn root_dotenv_is_available_to_submodules() {
Test::new() Test::new()
.write("foo.just", "foo:\n @echo $DOTENV_KEY")
.justfile( .justfile(
" "
set dotenv-load set dotenv-load
@ -523,10 +522,10 @@ fn root_dotenv_is_available_to_submodules() {
mod foo mod foo
", ",
) )
.write("foo.just", "foo:\n @echo $DOTENV_KEY")
.write(".env", "DOTENV_KEY=dotenv-value")
.test_round_trip(false) .test_round_trip(false)
.arg("--unstable") .args(["--unstable", "foo", "foo"])
.arg("foo")
.arg("foo")
.stdout("dotenv-value\n") .stdout("dotenv-value\n")
.run(); .run();
} }
@ -534,10 +533,6 @@ fn root_dotenv_is_available_to_submodules() {
#[test] #[test]
fn dotenv_settings_in_submodule_are_ignored() { fn dotenv_settings_in_submodule_are_ignored() {
Test::new() Test::new()
.write(
"foo.just",
"set dotenv-load := false\nfoo:\n @echo $DOTENV_KEY",
)
.justfile( .justfile(
" "
set dotenv-load set dotenv-load
@ -545,10 +540,13 @@ fn dotenv_settings_in_submodule_are_ignored() {
mod foo mod foo
", ",
) )
.write(
"foo.just",
"set dotenv-load := false\nfoo:\n @echo $DOTENV_KEY",
)
.write(".env", "DOTENV_KEY=dotenv-value")
.test_round_trip(false) .test_round_trip(false)
.arg("--unstable") .args(["--unstable", "foo", "foo"])
.arg("foo")
.arg("foo")
.stdout("dotenv-value\n") .stdout("dotenv-value\n")
.run(); .run();
} }

View File

@ -82,6 +82,7 @@ fn shell_expanded_strings_can_be_used_in_settings() {
echo $DOTENV_KEY echo $DOTENV_KEY
", ",
) )
.write(".env", "DOTENV_KEY=dotenv-value")
.env("JUST_TEST_VARIABLE", ".env") .env("JUST_TEST_VARIABLE", ".env")
.stdout("dotenv-value\n") .stdout("dotenv-value\n")
.run(); .run();

View File

@ -201,9 +201,8 @@ impl Test {
} else { } else {
self.stdout.clone() self.stdout.clone()
}; };
let stderr = unindent(&self.stderr);
fs::write(self.tempdir.path().join(".env"), "DOTENV_KEY=dotenv-value").unwrap(); let stderr = unindent(&self.stderr);
let mut command = Command::new(executable_path("just")); let mut command = Command::new(executable_path("just"));