dotenv_vault/
lib.rs

1//! Extends the [dotenvy](https://crates.io/crates/dotenvy) crate with *.env.vault* file support.
2//! The extended standard lets you load encrypted secrets from your *.env.vault* file in production (and other) environments.
3
4mod errors;
5mod log;
6mod vault;
7
8pub use dotenvy;
9pub use errors::Error;
10
11use errors::Result;
12use vault::Vault;
13
14/// Loads the *.env.vault* file from [`env::current_dir`](std::env::current_dir) using the *DOTENV_KEY* environment
15/// variable.
16///
17/// If the key or vault cannot be found, a regular *.env* file is loaded instead.
18///
19/// If variables with the same names already exist in the environment, then their values will be
20/// preserved.
21///
22/// Where multiple declarations for the same environment variable exist in your *.env*
23/// file, the *first one* is applied.
24///
25/// If you wish to ensure all variables are loaded from your *.env.vault* file, ignoring variables
26/// already existing in the environment, then use [`dotenv_override`] instead.
27///
28/// An error will be returned if the file is not found.
29///
30/// # Examples
31/// ```no_run
32/// fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
33///     dotenv_vault::dotenv()?;
34///     Ok(())
35/// }
36/// ```
37pub fn dotenv() -> Result<()> {
38    Vault::new().load()
39}
40
41/// Loads all variables into the environment, overriding any existing environment variables of the
42/// same name.
43///
44/// If the key or vault cannot be found, a regular *.env* file is loaded instead.
45///
46/// Where multiple declarations for the same environment variable exist in your *.env* file, the
47/// *last one* is applied.
48///
49/// If you want the existing environment to take precedence,
50/// or if you want to be able to override environment variables on the command line,
51/// then use [`dotenv`] instead.
52///
53/// # Examples
54/// ```no_run
55/// fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
56///     dotenv_vault::dotenv_override()?;
57///     Ok(())
58/// }
59/// ```
60pub fn dotenv_override() -> Result<()> {
61    Vault::new().load_override()
62}
63
64#[cfg(test)]
65mod tests {
66    use serial_test::serial;
67    use std::{env, fs::File, io::prelude::*};
68    use tempfile::tempdir;
69
70    #[test]
71    #[serial] // Run serially due to env modifications
72    fn dotenv_ok() {
73        env::set_var("DOTENV_KEY", "dotenv://:key_ddcaa26504cd70a6fef9801901c3981538563a1767c297cb8416e8a38c62fe00@dotenv.local/vault/.env.vault?environment=production");
74
75        let tmp = tempdir().unwrap();
76        let vault_path = tmp.path().join(".env.vault");
77        let mut vault = File::create(vault_path).unwrap();
78        vault
79            .write_all("DOTENV_VAULT_PRODUCTION=\"s7NYXa809k/bVSPwIAmJhPJmEGTtU0hG58hOZy7I0ix6y5HP8LsHBsZCYC/gw5DDFy5DgOcyd18R\"".as_bytes())
80            .unwrap();
81        vault.sync_all().unwrap();
82
83        let cwd = env::current_dir().unwrap();
84        env::set_current_dir(&tmp).unwrap();
85
86        let result = super::dotenv();
87        assert!(result.is_ok());
88
89        let from_vault = env::var("ALPHA");
90        assert!(from_vault.is_ok());
91        assert!(from_vault.unwrap() == "zeta");
92
93        tmp.close().unwrap();
94        env::remove_var("DOTENV_KEY");
95        env::remove_var("ALPHA");
96        env::set_current_dir(cwd).unwrap();
97    }
98
99    #[test]
100    #[serial] // Run serially due to env modifications
101    fn dotenv_fallback_to_env() {
102        let tmp = tempdir().unwrap();
103        let env_path = tmp.path().join(".env");
104        let mut env = File::create(env_path).unwrap();
105        env.write_all("TESTKEY=\"from .env\"".as_bytes()).unwrap();
106        env.sync_all().unwrap();
107
108        let cwd = env::current_dir().unwrap();
109        env::set_current_dir(&tmp).unwrap();
110
111        let result = super::dotenv();
112        assert!(result.is_ok());
113
114        let from_env = env::var("TESTKEY");
115        assert!(from_env.is_ok());
116        assert!(from_env.unwrap() == "from .env");
117
118        tmp.close().unwrap();
119        env::remove_var("TESTKEY");
120        env::set_current_dir(cwd).unwrap();
121    }
122
123    #[test]
124    #[serial] // Run serially due to env modifications
125    fn dotenv_override_ok() {
126        env::set_var("DOTENV_KEY", "dotenv://:key_ddcaa26504cd70a6fef9801901c3981538563a1767c297cb8416e8a38c62fe00@dotenv.local/vault/.env.vault?environment=production");
127
128        let tmp = tempdir().unwrap();
129        let vault_path = tmp.path().join(".env.vault");
130        let mut vault = File::create(vault_path).unwrap();
131        vault
132            .write_all("DOTENV_VAULT_PRODUCTION=\"s7NYXa809k/bVSPwIAmJhPJmEGTtU0hG58hOZy7I0ix6y5HP8LsHBsZCYC/gw5DDFy5DgOcyd18R\"".as_bytes())
133            .unwrap();
134        vault.sync_all().unwrap();
135
136        let cwd = env::current_dir().unwrap();
137        env::set_current_dir(&tmp).unwrap();
138
139        env::set_var("ALPHA", "beta");
140
141        let result = super::dotenv_override();
142        assert!(result.is_ok());
143
144        let from_vault = env::var("ALPHA");
145        assert!(from_vault.is_ok());
146        assert!(from_vault.unwrap() == "zeta");
147
148        tmp.close().unwrap();
149        env::remove_var("DOTENV_KEY");
150        env::remove_var("ALPHA");
151        env::set_current_dir(cwd).unwrap();
152    }
153
154    #[test]
155    #[serial] // Run serially due to env modifications
156    fn dotenv_override_fallback_to_env() {
157        let tmp = tempdir().unwrap();
158        let env_path = tmp.path().join(".env");
159        let mut env = File::create(env_path).unwrap();
160        env.write_all("TESTKEY=\"from .env\"".as_bytes()).unwrap();
161        env.sync_all().unwrap();
162
163        let cwd = env::current_dir().unwrap();
164        env::set_current_dir(&tmp).unwrap();
165
166        env::set_var("TESTKEY", "helloworld");
167
168        let result = super::dotenv_override();
169        assert!(result.is_ok());
170
171        let from_env = env::var("TESTKEY");
172        assert!(from_env.is_ok());
173        assert!(from_env.unwrap() == "from .env");
174
175        tmp.close().unwrap();
176        env::remove_var("TESTKEY");
177        env::set_current_dir(cwd).unwrap();
178    }
179}