Repository: casey/just
Pull request #784 (2021-03-31 00:22:33)

Warn if `.env` file loaded and `dotenv-load` unset


Patch:
diff --git a/src/analyzer.rs b/src/analyzer.rs
index c425da7d..542125e5 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -66,7 +66,7 @@ impl<'src> Analyzer<'src> {
     for (_, set) in self.sets {
       match set.value {
         Setting::DotenvLoad(dotenv_load) => {
-          settings.dotenv_load = dotenv_load;
+          settings.dotenv_load = Some(dotenv_load);
         },
         Setting::Export(export) => {
           settings.export = export;
diff --git a/src/justfile.rs b/src/justfile.rs
index d17ac5cc..7255c673 100644
--- a/src/justfile.rs
+++ b/src/justfile.rs
@@ -91,7 +91,7 @@ impl<'src> Justfile<'src> {
     }
 
     let dotenv = if config.load_dotenv {
-      load_dotenv(&search.working_directory, &self.settings)?
+      load_dotenv(&config, &self.settings, &search.working_directory)?
     } else {
       BTreeMap::new()
     };
diff --git a/src/load_dotenv.rs b/src/load_dotenv.rs
index d0c1b3dd..94037cca 100644
--- a/src/load_dotenv.rs
+++ b/src/load_dotenv.rs
@@ -1,14 +1,15 @@
 use crate::common::*;
 
 pub(crate) fn load_dotenv(
-  working_directory: &Path,
+  config: &Config,
   settings: &Settings,
+  working_directory: &Path,
 ) -> RunResult<'static, BTreeMap<String, String>> {
   // `dotenv::from_path_iter` should eventually be un-deprecated, see:
   // https://github.com/dotenv-rs/dotenv/issues/13
   #![allow(deprecated)]
 
-  if !settings.dotenv_load {
+  if !settings.dotenv_load.unwrap_or(true) {
     return Ok(BTreeMap::new());
   }
 
@@ -16,6 +17,14 @@ pub(crate) fn load_dotenv(
     let path = directory.join(".env");
 
     if path.is_file() {
+      if settings.dotenv_load.is_none() && config.verbosity.loud() {
+        if config.color.stderr().active() {
+          eprintln!("{:#}", Warning::DotenvLoad);
+        } else {
+          eprintln!("{}", Warning::DotenvLoad);
+        }
+      }
+
       let iter = dotenv::from_path_iter(&path)?;
       let mut dotenv = BTreeMap::new();
       for result in iter {
diff --git a/src/settings.rs b/src/settings.rs
index 2037d338..b86dc240 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -2,7 +2,7 @@ use crate::common::*;
 
 #[derive(Debug, PartialEq)]
 pub(crate) struct Settings<'src> {
-  pub(crate) dotenv_load: bool,
+  pub(crate) dotenv_load: Option<bool>,
   pub(crate) export:      bool,
   pub(crate) shell:       Option<setting::Shell<'src>>,
 }
@@ -10,7 +10,7 @@ pub(crate) struct Settings<'src> {
 impl<'src> Settings<'src> {
   pub(crate) fn new() -> Settings<'src> {
     Settings {
-      dotenv_load: true,
+      dotenv_load: None,
       export:      false,
       shell:       None,
     }
diff --git a/src/warning.rs b/src/warning.rs
index 4ea9b51a..23a08508 100644
--- a/src/warning.rs
+++ b/src/warning.rs
@@ -1,12 +1,15 @@
 use crate::common::*;
 
 #[derive(Debug, PartialEq)]
-pub(crate) enum Warning {}
+pub(crate) enum Warning {
+  DotenvLoad,
+}
 
 impl Warning {
   fn context(&self) -> Option<&Token> {
-    #![allow(clippy::unused_self)]
-    unreachable!()
+    match self {
+      Self::DotenvLoad => None,
+    }
   }
 }
 
@@ -17,6 +20,23 @@ impl Display for Warning {
 
     write!(f, "{} {}", warning.paint("warning:"), message.prefix())?;
 
+    match self {
+      Self::DotenvLoad => {
+        #[rustfmt::skip]
+        write!(f, "\
+A `.env` file was found and loaded, but this behavior will change in the future.
+To silence this warning and continue loading `.env` files, add:
+
+    set dotenv-load := true
+
+To silence this warning and stop loading `.env` files, add:
+
+    set dotenv-load := false
+
+See https://github.com/casey/just/issues/469 for more details.")?;
+      },
+    }
+
     write!(f, "{}", message.suffix())?;
 
     if let Some(token) = self.context() {


Test patch:
diff --git a/tests/dotenv.rs b/tests/dotenv.rs
index 50c5b1bc..145a2de5 100644
--- a/tests/dotenv.rs
+++ b/tests/dotenv.rs
@@ -37,6 +37,7 @@ test! {
   "#,
   stdout:   "undefined\n",
   stderr:   "if [ -n \"${DOTENV_KEY+1}\" ]; then echo defined; else echo undefined; fi\n",
+  dotenv_load: false,
 }
 
 test! {
@@ -49,6 +50,7 @@ test! {
   "#,
   stdout:   "dotenv-value\n",
   stderr:   "echo $DOTENV_KEY\n",
+  dotenv_load: false,
 }
 
 test! {
@@ -61,4 +63,28 @@ test! {
   "#,
   stdout:   "dotenv-value\n",
   stderr:   "echo $DOTENV_KEY\n",
+  dotenv_load: false,
+}
+
+test! {
+  name:     warning,
+  justfile: r#"
+    foo:
+      echo $DOTENV_KEY
+  "#,
+  stdout:   "dotenv-value\n",
+  stderr:   "
+    warning: A `.env` file was found and loaded, but this behavior will change in the future.
+    To silence this warning and continue loading `.env` files, add:
+
+        set dotenv-load := true
+
+    To silence this warning and stop loading `.env` files, add:
+
+        set dotenv-load := false
+
+    See https://github.com/casey/just/issues/469 for more details.
+    echo $DOTENV_KEY
+  ",
+  dotenv_load: false,
 }
diff --git a/tests/misc.rs b/tests/misc.rs
index bb957fd4..e9e3c175 100644
--- a/tests/misc.rs
+++ b/tests/misc.rs
@@ -2121,11 +2121,15 @@ default stdin = `cat justfile`:
 
     default stdin = `cat justfile`:
       echo {{stdin}}
+
+    set dotenv-load := true
   ",
   stderr:   "
     echo '
     default stdin = `cat justfile`:
-      echo '{{stdin}}''
+      echo '{{stdin}}'
+
+    set dotenv-load := true'
   ",
 }
 
diff --git a/tests/test.rs b/tests/test.rs
index 7b50de5f..3067e892 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -15,6 +15,7 @@ macro_rules! test {
     $(stderr:   $stderr:expr,)?
     $(status:   $status:expr,)?
     $(shell:    $shell:expr,)?
+    $(dotenv_load: $dotenv_load:expr,)?
   ) => {
     #[test]
     fn $name() {
@@ -31,6 +32,7 @@ macro_rules! test {
         $(stderr: $stderr,)?
         $(status: $status,)?
         $(shell: $shell,)?
+        $(dotenv_load: $dotenv_load,)?
         env,
         ..crate::test::Test::default()
       }.run();
@@ -39,27 +41,29 @@ macro_rules! test {
 }
 
 pub(crate) struct Test<'a> {
-  pub(crate) justfile: &'a str,
-  pub(crate) args:     &'a [&'a str],
-  pub(crate) env:      BTreeMap<String, String>,
-  pub(crate) stdin:    &'a str,
-  pub(crate) stdout:   &'a str,
-  pub(crate) stderr:   &'a str,
-  pub(crate) status:   i32,
-  pub(crate) shell:    bool,
+  pub(crate) justfile:    &'a str,
+  pub(crate) args:        &'a [&'a str],
+  pub(crate) env:         BTreeMap<String, String>,
+  pub(crate) stdin:       &'a str,
+  pub(crate) stdout:      &'a str,
+  pub(crate) stderr:      &'a str,
+  pub(crate) status:      i32,
+  pub(crate) shell:       bool,
+  pub(crate) dotenv_load: bool,
 }
 
 impl<'a> Default for Test<'a> {
   fn default() -> Test<'a> {
     Test {
-      justfile: "",
-      args:     &[],
-      env:      BTreeMap::new(),
-      stdin:    "",
-      stdout:   "",
-      stderr:   "",
-      status:   EXIT_SUCCESS,
-      shell:    true,
+      justfile:    "",
+      args:        &[],
+      env:         BTreeMap::new(),
+      stdin:       "",
+      stdout:      "",
+      stderr:      "",
+      status:      EXIT_SUCCESS,
+      shell:       true,
+      dotenv_load: true,
     }
   }
 }
@@ -68,7 +72,12 @@ impl<'a> Test<'a> {
   pub(crate) fn run(self) {
     let tmp = tempdir();
 
-    let justfile = unindent(self.justfile);
+    let mut justfile = unindent(self.justfile);
+
+    if self.dotenv_load {
+      justfile.push_str("\nset dotenv-load := true\n");
+    }
+
     let stdout = unindent(self.stdout);
     let stderr = unindent(self.stderr);