From 0f80fb8707a9fdb11df21a426153d7c515b7c48a Mon Sep 17 00:00:00 2001 From: Daksh Date: Sat, 29 Nov 2025 04:21:53 +0530 Subject: [PATCH] feat(cli): add --allow-env-file flag --- .agent/workflows/setup_local_env.md | 86 +++++++++++++++++++++++++++++ cli/args/flags.rs | 34 ++++++++++++ cli/main.rs | 25 +++++++++ cli/util/watch_env_tracker.rs | 34 ++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 .agent/workflows/setup_local_env.md diff --git a/.agent/workflows/setup_local_env.md b/.agent/workflows/setup_local_env.md new file mode 100644 index 00000000000000..164c10967af9e1 --- /dev/null +++ b/.agent/workflows/setup_local_env.md @@ -0,0 +1,86 @@ +--- +description: Setup local development environment for Deno +--- + +# Setup Local Deno Development Environment + +This guide helps you set up your system to build and contribute to Deno. + +## Option 1: VS Code Dev Containers (Recommended) + +If you use VS Code, the easiest way is to use the provided Dev Container +configuration. + +1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop). +2. Install the + [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + in VS Code. +3. Open the project folder in VS Code. +4. Click "Reopen in Container" when prompted, or run the command + `Dev Containers: Reopen in Container`. + +This will automatically set up all dependencies (Rust, Python, CMake, Protobuf, +etc.). + +## Option 2: Manual Setup + +### 1. Install Prerequisites + +#### Rust + +Deno requires a specific version of Rust. + +```bash +# Install rustup if you haven't +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Install the specific version required by Deno (currently 1.90.0) +rustup install 1.90.0 +rustup default 1.90.0 +rustup component add rustfmt clippy +``` + +#### Python 3 + +Ensure you have Python 3 installed and accessible as `python` or `python3`. + +#### Protobuf Compiler + +- **Mac:** `brew install protobuf` +- **Linux:** `apt install -y protobuf-compiler` +- **Windows:** Download binary release from GitHub. + +#### CMake + +- **Mac:** `brew install cmake` +- **Linux:** `apt install -y cmake` + +#### Native Compilers + +- **Mac:** XCode Command Line Tools (`xcode-select --install`) +- **Linux:** `apt install -y build-essential libglib2.0-dev` + +### 2. Build Deno + +```bash +# Clone with submodules if you haven't already +git submodule update --init --recursive + +# Build +cargo build -vv +``` + +### 3. Verify Setup + +Run the tests to ensure everything is working: + +```bash +# Run unit tests +cargo test -vv + +# Format code +./tools/format.js + +# Lint code +./tools/lint.js +``` diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 7d0a95888483da..0192276798af0b 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -786,6 +786,7 @@ pub struct Flags { pub ignore: Vec, pub import_map_path: Option, pub env_file: Option>, + pub allow_env_file: Option>, pub inspect_brk: Option, pub inspect_wait: Option, pub inspect: Option, @@ -2216,6 +2217,7 @@ If you specify a directory instead of a file, the path is expanded to all contai .arg(no_clear_screen_arg()) .arg(script_arg().last(true)) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg(executable_ext_arg()) }) } @@ -2391,6 +2393,7 @@ Future runs of this module will trigger no downloads or compilation unless --rel .arg(allow_import_arg()) .arg(deny_import_arg()) .arg(env_file_arg()) + .arg(allow_env_file_arg()) }) } @@ -2570,6 +2573,7 @@ On the first invocation of `deno compile`, Deno will download the relevant binar ) .arg(executable_ext_arg()) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg( script_arg() .num_args(0..=1) @@ -2883,6 +2887,7 @@ This command has implicit access to all permissions. .required_unless_present("help"), ) .arg(env_file_arg()) + .arg(allow_env_file_arg()) }) } @@ -3233,6 +3238,7 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res .help("Install dependents of the specified entrypoint(s)"), ) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg(add_dev_arg().conflicts_with("entrypoint").conflicts_with("global")) .args(default_registry_args().into_iter().map(|arg| arg.conflicts_with("entrypoint").conflicts_with("global"))) }) @@ -3628,6 +3634,7 @@ TypeScript is supported, however it is not type-checked, only transpiled." [default: $DENO_DIR/deno_history.txt]")) }) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg( Arg::new("args") .num_args(0..) @@ -3651,6 +3658,7 @@ fn run_args(command: Command, top_level: bool) -> Command { script_arg().trailing_var_arg(true) }) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg(no_code_cache_arg()) .arg(coverage_arg()) .arg(tunnel_arg()) @@ -3729,6 +3737,7 @@ Start a server defined in server.ts, watching for changes and running on port 50 .trailing_var_arg(true), ) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg(no_code_cache_arg()) .arg(tunnel_arg()) } @@ -3992,6 +4001,7 @@ or **/__tests__/**: .action(ArgAction::SetTrue) ) .arg(env_file_arg()) + .arg(allow_env_file_arg()) .arg(executable_ext_arg()) ) } @@ -4755,6 +4765,21 @@ fn env_file_arg() -> Arg { .action(ArgAction::Append) } +fn allow_env_file_arg() -> Arg { + Arg::new("allow-env-file") + .long("allow-env-file") + .value_name("FILE") + .help(cstr!( + "Specify an environment file to load and allow reading its variables + This flag behaves like --env-file, but also grants read permissions to the variables defined in the file." + )) + .value_hint(ValueHint::FilePath) + .default_missing_value(".env") + .require_equals(true) + .num_args(0..=1) + .action(ArgAction::Append) +} + fn reload_arg() -> Arg { Arg::new("reload") .short('r') @@ -5502,6 +5527,7 @@ fn cache_parse( allow_scripts_arg_parse(flags, matches)?; allow_and_deny_import_parse(flags, matches)?; env_file_arg_parse(flags, matches); + allow_env_file_arg_parse(flags, matches); let files = matches.remove_many::("file").unwrap().collect(); flags.subcommand = DenoSubcommand::Cache(CacheFlags { files }); Ok(()) @@ -6181,6 +6207,7 @@ fn repl_parse( seed_arg_parse(flags, matches); enable_testing_features_arg_parse(flags, matches); env_file_arg_parse(flags, matches); + allow_env_file_arg_parse(flags, matches); trace_ops_parse(flags, matches); let eval_files = matches @@ -6875,6 +6902,7 @@ fn runtime_args_parse( seed_arg_parse(flags, matches); enable_testing_features_arg_parse(flags, matches); env_file_arg_parse(flags, matches); + allow_env_file_arg_parse(flags, matches); trace_ops_parse(flags, matches); eszip_arg_parse(flags, matches); Ok(()) @@ -6902,6 +6930,12 @@ fn env_file_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { .map(|values| values.cloned().collect()); } +fn allow_env_file_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { + flags.allow_env_file = matches + .get_many::("allow-env-file") + .map(|values| values.cloned().collect()); +} + fn reload_arg_parse( flags: &mut Flags, matches: &mut ArgMatches, diff --git a/cli/main.rs b/cli/main.rs index 8d3d2993b0d554..27ceeeede2bf69 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -69,6 +69,7 @@ use crate::util::display; use crate::util::v8::get_v8_flags_from_env; use crate::util::v8::init_v8_flags; use crate::util::watch_env_tracker::WatchEnvTracker; +use crate::util::watch_env_tracker::get_env_vars_from_env_file; use crate::util::watch_env_tracker::load_env_variables_from_env_files; #[cfg(feature = "dhat-heap")] @@ -715,6 +716,30 @@ async fn resolve_flags_and_init( .map(|files| files.iter().map(PathBuf::from).collect()); load_env_variables_from_env_files(env_file_paths.as_ref(), flags.log_level); + let allow_env_file_paths: Option> = flags + .allow_env_file + .as_ref() + .map(|files| files.iter().map(PathBuf::from).collect()); + + if let Some(paths) = &allow_env_file_paths { + let mut allowed_vars = std::collections::HashSet::new(); + for path in paths { + if let Some(vars) = get_env_vars_from_env_file(path, flags.log_level) { + for (key, _) in vars { + allowed_vars.insert(key); + } + } + } + if !flags.permissions.allow_all { + if let Some(ref mut env_perms) = flags.permissions.allow_env { + env_perms.extend(allowed_vars); + } else { + flags.permissions.allow_env = Some(allowed_vars.into_iter().collect()); + } + } + load_env_variables_from_env_files(Some(paths), flags.log_level); + } + if deno_lib::args::has_flag_env_var("DENO_CONNECTED") { flags.tunnel = true; } diff --git a/cli/util/watch_env_tracker.rs b/cli/util/watch_env_tracker.rs index 8790ba45b79172..c3061e04beeb69 100644 --- a/cli/util/watch_env_tracker.rs +++ b/cli/util/watch_env_tracker.rs @@ -256,3 +256,37 @@ pub fn load_env_variables_from_env_files( } } } + +pub fn get_env_vars_from_env_file( + file_path: &Path, + log_level: Option, +) -> Option> { + match dotenvy::from_path_iter(file_path) { + Ok(iter) => { + let mut vars = HashMap::new(); + for item in iter { + match item { + Ok((key, value)) => { + vars.insert(key, value); + } + Err(e) => { + WatchEnvTracker::handle_dotenvy_error(e, file_path, log_level); + } + } + } + Some(vars) + } + 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 + ); + } + None + } + } +}