Skip to content

Commit 876b402

Browse files
authored
Add dotenv-override setting (#2785)
1 parent 0514c2c commit 876b402

File tree

9 files changed

+65
-9
lines changed

9 files changed

+65
-9
lines changed

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,7 @@ foo:
989989
| `allow-duplicate-variables` | boolean | `false` | Allow variables appearing later in a `justfile` to override earlier variables with the same name. |
990990
| `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. |
991991
| `dotenv-load` | boolean | `false` | Load a `.env` file, if present. |
992+
| `dotenv-override` | boolean | `false` | Override existing environment variables with values from the `.env` file. |
992993
| `dotenv-path` | string | - | Load a `.env` file from a custom path and error if not present. Overrides `dotenv-filename`. |
993994
| `dotenv-required` | boolean | `false` | Error if a `.env` file isn't found. |
994995
| `export` | boolean | `false` | Export all variables as environment variables. |
@@ -1060,8 +1061,9 @@ bar
10601061

10611062
#### Dotenv Settings
10621063

1063-
If any of `dotenv-load`, `dotenv-filename`, `dotenv-path`, or `dotenv-required`
1064-
are set, `just` will try to load environment variables from a file.
1064+
If any of `dotenv-load`, `dotenv-filename`, `dotenv-override`, `dotenv-path`,
1065+
or `dotenv-required` are set, `just` will try to load environment variables
1066+
from a file.
10651067

10661068
If `dotenv-path` is set, `just` will look for a file at the given path, which
10671069
may be absolute, or relative to the working directory.
@@ -1086,6 +1088,9 @@ It is not an error if an environment file is not found, unless
10861088
The loaded variables are environment variables, not `just` variables, and so
10871089
must be accessed using `$VARIABLE_NAME` in recipes and backticks.
10881090

1091+
If `dotenv-override` is set, variables from the environment file will override
1092+
existing environment variables.
1093+
10891094
For example, if your `.env` file contains:
10901095

10911096
```console
@@ -4452,7 +4457,9 @@ and checking the program's stdout, stderr, and exit code .
44524457

44534458
5. Implement the feature.
44544459

4455-
6. Run `just ci` to make sure that all tests, lints, and checks pass.
4460+
6. Run `just ci` to make sure that all tests, lints, and checks pass. Requires
4461+
[mdBook](https://github.com/rust-lang/mdBook) and
4462+
[mdbook-linkcheck](https://github.com/Michael-F-Bryan/mdbook-linkcheck).
44564463

44574464
7. Open a PR with the new code that is editable by maintainers. PRs often
44584465
require rebasing and minor tweaks. If the PR is not editable by maintainers,

src/keyword.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub(crate) enum Keyword {
99
Assert,
1010
DotenvFilename,
1111
DotenvLoad,
12+
DotenvOverride,
1213
DotenvPath,
1314
DotenvRequired,
1415
Else,

src/load_dotenv.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@ pub(crate) fn load_dotenv(
1616
.or(settings.dotenv_path.as_ref());
1717

1818
if !settings.dotenv_load
19+
&& !settings.dotenv_override
20+
&& !settings.dotenv_required
1921
&& dotenv_filename.is_none()
2022
&& dotenv_path.is_none()
21-
&& !settings.dotenv_required
2223
{
2324
return Ok(BTreeMap::new());
2425
}
2526

2627
if let Some(path) = dotenv_path {
2728
let path = working_directory.join(path);
2829
if path.is_file() {
29-
return load_from_file(&path);
30+
return load_from_file(&path, settings);
3031
}
3132
}
3233

@@ -35,7 +36,7 @@ pub(crate) fn load_dotenv(
3536
for directory in working_directory.ancestors() {
3637
let path = directory.join(filename);
3738
if path.is_file() {
38-
return load_from_file(&path);
39+
return load_from_file(&path, settings);
3940
}
4041
}
4142

@@ -46,12 +47,15 @@ pub(crate) fn load_dotenv(
4647
}
4748
}
4849

49-
fn load_from_file(path: &Path) -> RunResult<'static, BTreeMap<String, String>> {
50+
fn load_from_file(
51+
path: &Path,
52+
settings: &Settings,
53+
) -> RunResult<'static, BTreeMap<String, String>> {
5054
let iter = dotenvy::from_path_iter(path)?;
5155
let mut dotenv = BTreeMap::new();
5256
for result in iter {
5357
let (key, value) = result?;
54-
if env::var_os(&key).is_none() {
58+
if settings.dotenv_override || env::var_os(&key).is_none() {
5559
dotenv.insert(key, value);
5660
}
5761
}

src/node.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ impl<'src> Node<'src> for Set<'src> {
303303
Setting::AllowDuplicateRecipes(value)
304304
| Setting::AllowDuplicateVariables(value)
305305
| Setting::DotenvLoad(value)
306+
| Setting::DotenvOverride(value)
306307
| Setting::DotenvRequired(value)
307308
| Setting::Export(value)
308309
| Setting::Fallback(value)

src/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ impl<'run, 'src> Parser<'run, 'src> {
11191119
Some(Setting::AllowDuplicateVariables(self.parse_set_bool()?))
11201120
}
11211121
Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)),
1122+
Keyword::DotenvOverride => Some(Setting::DotenvOverride(self.parse_set_bool()?)),
11221123
Keyword::DotenvRequired => Some(Setting::DotenvRequired(self.parse_set_bool()?)),
11231124
Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)),
11241125
Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)),

src/setting.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub(crate) enum Setting<'src> {
66
AllowDuplicateVariables(bool),
77
DotenvFilename(StringLiteral<'src>),
88
DotenvLoad(bool),
9+
DotenvOverride(bool),
910
DotenvPath(StringLiteral<'src>),
1011
DotenvRequired(bool),
1112
Export(bool),
@@ -29,6 +30,7 @@ impl Display for Setting<'_> {
2930
Self::AllowDuplicateRecipes(value)
3031
| Self::AllowDuplicateVariables(value)
3132
| Self::DotenvLoad(value)
33+
| Self::DotenvOverride(value)
3234
| Self::DotenvRequired(value)
3335
| Self::Export(value)
3436
| Self::Fallback(value)

src/settings.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) struct Settings<'src> {
1111
pub(crate) allow_duplicate_variables: bool,
1212
pub(crate) dotenv_filename: Option<String>,
1313
pub(crate) dotenv_load: bool,
14+
pub(crate) dotenv_override: bool,
1415
pub(crate) dotenv_path: Option<PathBuf>,
1516
pub(crate) dotenv_required: bool,
1617
pub(crate) export: bool,
@@ -50,6 +51,9 @@ impl<'src> Settings<'src> {
5051
Setting::DotenvPath(path) => {
5152
settings.dotenv_path = Some(PathBuf::from(path.cooked));
5253
}
54+
Setting::DotenvOverride(dotenv_overrride) => {
55+
settings.dotenv_override = dotenv_overrride;
56+
}
5357
Setting::DotenvRequired(dotenv_required) => {
5458
settings.dotenv_required = dotenv_required;
5559
}

tests/dotenv.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ fn no_dotenv() {
362362
}
363363

364364
#[test]
365-
fn dotenv_env_var_override() {
365+
fn dotenv_env_var_default_no_override() {
366366
Test::new()
367367
.justfile(
368368
"
@@ -377,6 +377,41 @@ fn dotenv_env_var_override() {
377377
.run();
378378
}
379379

380+
#[test]
381+
fn dotenv_env_var_override() {
382+
Test::new()
383+
.justfile(
384+
"
385+
set dotenv-load
386+
set dotenv-override := true
387+
echo:
388+
echo $DOTENV_KEY
389+
",
390+
)
391+
.write(".env", "DOTENV_KEY=dotenv-value")
392+
.env("DOTENV_KEY", "not-the-dotenv-value")
393+
.stdout("dotenv-value\n")
394+
.stderr("echo $DOTENV_KEY\n")
395+
.run();
396+
}
397+
398+
#[test]
399+
fn dotenv_env_var_override_no_load() {
400+
Test::new()
401+
.justfile(
402+
"
403+
set dotenv-override := true
404+
echo:
405+
echo $DOTENV_KEY
406+
",
407+
)
408+
.write(".env", "DOTENV_KEY=dotenv-value")
409+
.env("DOTENV_KEY", "not-the-dotenv-value")
410+
.stdout("dotenv-value\n")
411+
.stderr("echo $DOTENV_KEY\n")
412+
.run();
413+
}
414+
380415
#[test]
381416
fn dotenv_path_usable_from_subdir() {
382417
Test::new()

tests/json.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct Settings<'a> {
7979
allow_duplicate_variables: bool,
8080
dotenv_filename: Option<&'a str>,
8181
dotenv_load: bool,
82+
dotenv_override: bool,
8283
dotenv_path: Option<&'a str>,
8384
dotenv_required: bool,
8485
export: bool,

0 commit comments

Comments
 (0)