/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v2.7.2
cli/util/env.rs
308 строк
9 KB
David Sherret
refactor: consolidate `utils::watcher_env_tracker` to `utils::env` (#32419)
03 мар 2026, 12:48
Не верифицирован
03 мар 2026, 12:48
88bd1bc
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::borrow::Cow; use std::collections::HashMap; use std::collections::HashSet; use std::env; use std::ffi::OsString; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; use std::sync::OnceLock; use deno_terminal::colors; pub fn resolve_cwd( initial_cwd: Option<&Path>, ) -> Result<Cow<'_, Path>, std::io::Error> { match initial_cwd { Some(initial_cwd) => Ok(Cow::Borrowed(initial_cwd)), // ok because the lint recommends using this method #[allow(clippy::disallowed_methods)] None => std::env::current_dir().map(Cow::Owned).map_err(|err| { std::io::Error::new( err.kind(), format!("could not read current working directory: {err}"), ) }), } } #[derive(Debug, Clone)] struct WatchEnvTrackerInner { // Track all loaded variables and their values loaded_variables: HashSet<OsString>, // Track variables that are no longer present in any loaded file unused_variables: HashSet<OsString>, // Track original env vars that existed before we started original_env: HashMap<OsString, OsString>, } impl WatchEnvTrackerInner { fn new() -> Self { // Capture the original environment state let original_env: HashMap<OsString, OsString> = env::vars_os().collect(); Self { loaded_variables: HashSet::new(), unused_variables: HashSet::new(), original_env, } } } #[derive(Debug, Clone)] pub struct WatchEnvTracker { inner: Arc<Mutex<WatchEnvTrackerInner>>, } // Global singleton instance static WATCH_ENV_TRACKER: OnceLock<WatchEnvTracker> = OnceLock::new(); impl WatchEnvTracker { /// Get the global singleton instance pub fn snapshot() -> &'static WatchEnvTracker { WATCH_ENV_TRACKER.get_or_init(|| WatchEnvTracker { inner: Arc::new(Mutex::new(WatchEnvTrackerInner::new())), }) } // Internal method that accepts an already-acquired lock to avoid deadlocks fn load_env_file_inner( &self, file_path: PathBuf, log_level: Option<log::Level>, inner: &mut WatchEnvTrackerInner, ) { // Check if file exists if !file_path.exists() { // Only show warning if logging is enabled #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Info).unwrap_or(true) { eprintln!( "{} The environment file specified '{}' was not found.", colors::yellow("Warning"), file_path.display() ); } return; } match deno_dotenv::from_path_sanitized_iter(&file_path) { Ok(iter) => { for item in iter { match item { Ok((key, value)) => { // Convert to OsString for consistency let key_os = OsString::from(key); let value_os = OsString::from(value); // Process-level env vars should always take precedence over env files. if inner.original_env.contains_key(&key_os) { #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Debug).unwrap_or(false) { eprintln!( "{} Variable '{}' already exists in the process environment, skipping value from '{}'", colors::yellow("Debug"), key_os.to_string_lossy(), file_path.display() ); } continue; } // Check if this variable is already loaded from a previous file if inner.loaded_variables.contains(&key_os) { // Variable already exists from a previous file, skip it #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Debug).unwrap_or(false) { eprintln!( "{} Variable '{}' already loaded from '{}', skipping value from '{}'", colors::yellow("Debug"), key_os.to_string_lossy(), inner .loaded_variables .get(&key_os) .map(|k| k.to_string_lossy().to_string()) .unwrap_or_else(|| "unknown".to_string()), file_path.display() ); } continue; } // Set the environment variable // SAFETY: We're setting environment variables with valid UTF-8 strings // from the .env file. Both key and value are guaranteed to be valid strings. unsafe { env::set_var(&key_os, &value_os); } // Track this variable inner.loaded_variables.insert(key_os.clone()); inner.unused_variables.remove(&key_os); } Err(e) => { handle_dotenv_error(e, &file_path, log_level); } } } } Err(e) => { #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Info).unwrap_or(true) { eprintln!( "{} Failed to read {}: {}", colors::yellow("Warning"), file_path.display(), e ); } } } } /// Clean up variables that are no longer present in any loaded file fn _cleanup_removed_variables( &self, inner: &mut WatchEnvTrackerInner, log_level: Option<log::Level>, ) { for var_name in inner.unused_variables.iter() { if !inner.original_env.contains_key(var_name) { // SAFETY: We're removing an environment variable that we previously set unsafe { env::remove_var(var_name); } #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Debug).unwrap_or(false) { eprintln!( "{} Variable '{}' removed from environment as it's no longer present in any loaded file", colors::yellow("Debug"), var_name.to_string_lossy() ); } } else { let original_value = inner.original_env.get(var_name).unwrap(); // SAFETY: We're setting an environment variable to a value we control unsafe { env::set_var(var_name, original_value); } #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Debug).unwrap_or(false) { eprintln!( "{} Variable '{}' restored to original value as it's no longer present in any loaded file", colors::yellow("Debug"), var_name.to_string_lossy() ); } } } } // Load multiple env files in reverse order (later files take precedence over earlier ones) pub fn load_env_variables_from_env_files( &self, file_paths: Option<&Vec<PathBuf>>, log_level: Option<log::Level>, ) { let Some(env_file_names) = file_paths else { return; }; let mut inner = self.inner.lock().unwrap(); inner.unused_variables = std::mem::take(&mut inner.loaded_variables); inner.loaded_variables = HashSet::new(); for env_file_name in env_file_names.iter().rev() { self.load_env_file_inner( env_file_name.to_path_buf(), log_level, &mut inner, ); } self._cleanup_removed_variables(&mut inner, log_level); } } pub fn load_env_variables_from_env_files( filename: Option<&Vec<PathBuf>>, flags_log_level: Option<log::Level>, ) { let Some(env_file_names) = filename else { return; }; let original_env_keys: HashSet<OsString> = env::vars_os().map(|(key, _)| key).collect(); let mut loaded_keys = HashSet::new(); for env_file_name in env_file_names.iter().rev() { let iter = match deno_dotenv::from_path_sanitized_iter(env_file_name) { Ok(iter) => iter, Err(error) => { handle_dotenv_error(error, env_file_name, flags_log_level); continue; } }; for item in iter { let (key, value) = match item { Ok(pair) => pair, Err(error) => { handle_dotenv_error(error, env_file_name, flags_log_level); break; } }; let key_os = OsString::from(key); if original_env_keys.contains(&key_os) || loaded_keys.contains(&key_os) { continue; } // SAFETY: We're setting environment variables with sanitized key/value strings from a .env file. unsafe { env::set_var(&key_os, value); } loaded_keys.insert(key_os); } } } fn handle_dotenv_error( error: deno_dotenv::Error, file_path: &Path, log_level: Option<log::Level>, ) { #[allow(clippy::print_stderr)] if log_level.map(|l| l >= log::Level::Info).unwrap_or(true) { match error { deno_dotenv::Error::LineParse(line, index) => eprintln!( "{} Parsing failed within the specified environment file: {} at index: {} of the value: {}", colors::yellow("Warning"), file_path.display(), index, line ), deno_dotenv::Error::Io(e) => match e.kind() { std::io::ErrorKind::NotFound => eprintln!( "{} The `--env-file` flag was used, but the environment file specified '{}' was not found.", colors::yellow("Warning"), file_path.display(), ), _ => eprintln!( "{} Error reading from environment file '{}': {}.", colors::yellow("Warning"), file_path.display(), e, ), }, } } }