mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
feat(unstable): deno run --env
(#20300)
This change adds the `--env=[FILE]` flag to the `run`, `compile`, `eval`, `install` and `repl` subcommands. Environment variables set in the CLI overwrite those defined in the `.env` file.
This commit is contained in:
parent
53248e9bb3
commit
f8f4e77632
10 changed files with 134 additions and 4 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1029,6 +1029,7 @@ dependencies = [
|
|||
"deno_semver",
|
||||
"deno_task_shell",
|
||||
"dissimilar",
|
||||
"dotenvy",
|
||||
"dprint-plugin-json",
|
||||
"dprint-plugin-markdown",
|
||||
"dprint-plugin-typescript",
|
||||
|
@ -2085,6 +2086,12 @@ dependencies = [
|
|||
"syn 0.15.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "dprint-core"
|
||||
version = "0.62.1"
|
||||
|
|
|
@ -76,6 +76,7 @@ dashmap = "5.5.3"
|
|||
data-encoding.workspace = true
|
||||
data-url.workspace = true
|
||||
dissimilar = "=1.0.4"
|
||||
dotenvy = "0.15.7"
|
||||
dprint-plugin-json = "=0.19.0"
|
||||
dprint-plugin-markdown = "=0.16.2"
|
||||
dprint-plugin-typescript = "=0.88.3"
|
||||
|
|
|
@ -395,6 +395,7 @@ pub struct Flags {
|
|||
pub ext: Option<String>,
|
||||
pub ignore: Vec<PathBuf>,
|
||||
pub import_map_path: Option<String>,
|
||||
pub env_file: Option<String>,
|
||||
pub inspect_brk: Option<SocketAddr>,
|
||||
pub inspect_wait: Option<SocketAddr>,
|
||||
pub inspect: Option<SocketAddr>,
|
||||
|
@ -1030,6 +1031,7 @@ glob {*_,*.,}bench.{js,mjs,ts,mts,jsx,tsx}:
|
|||
.arg(watch_arg(false))
|
||||
.arg(no_clear_screen_arg())
|
||||
.arg(script_arg().last(true))
|
||||
.arg(env_file_arg())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1191,6 +1193,7 @@ supported in canary.
|
|||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(executable_ext_arg())
|
||||
.arg(env_file_arg())
|
||||
.arg(script_arg().required(true).trailing_var_arg(true))
|
||||
})
|
||||
}
|
||||
|
@ -1434,6 +1437,7 @@ This command has implicit access to all permissions (--allow-all).",
|
|||
.value_name("CODE_ARG")
|
||||
.required(true),
|
||||
)
|
||||
.arg(env_file_arg())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1667,6 +1671,7 @@ These must be added to the path manually if required.")
|
|||
.help("Forcefully overwrite existing installation")
|
||||
.action(ArgAction::SetTrue))
|
||||
)
|
||||
.arg(env_file_arg())
|
||||
}
|
||||
|
||||
fn jupyter_subcommand() -> Command {
|
||||
|
@ -1868,6 +1873,7 @@ fn repl_subcommand() -> Command {
|
|||
.help("Evaluates the provided code when the REPL starts.")
|
||||
.value_name("code"),
|
||||
))
|
||||
.arg(env_file_arg())
|
||||
}
|
||||
|
||||
fn run_subcommand() -> Command {
|
||||
|
@ -1882,6 +1888,7 @@ fn run_subcommand() -> Command {
|
|||
.required_unless_present("v8-flags")
|
||||
.trailing_var_arg(true),
|
||||
)
|
||||
.arg(env_file_arg())
|
||||
.about("Run a JavaScript or TypeScript program")
|
||||
.long_about(
|
||||
"Run a JavaScript or TypeScript program
|
||||
|
@ -2063,6 +2070,7 @@ Directory arguments are expanded to all contained files matching the glob
|
|||
.help("Select reporter to use. Default to 'pretty'.")
|
||||
.value_parser(["pretty", "dot", "junit", "tap"])
|
||||
)
|
||||
.arg(env_file_arg())
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2643,6 +2651,18 @@ fn import_map_arg() -> Arg {
|
|||
.value_hint(ValueHint::FilePath)
|
||||
}
|
||||
|
||||
fn env_file_arg() -> Arg {
|
||||
Arg::new("env")
|
||||
.long("env")
|
||||
.value_name("FILE")
|
||||
.help("Load .env file")
|
||||
.long_help("UNSTABLE: Load environment variables from local file. Only the first environment variable with a given key is used. Existing process environment variables are not overwritten.")
|
||||
.value_hint(ValueHint::FilePath)
|
||||
.default_missing_value(".env")
|
||||
.require_equals(true)
|
||||
.num_args(0..=1)
|
||||
}
|
||||
|
||||
fn reload_arg() -> Arg {
|
||||
Arg::new("reload")
|
||||
.short('r')
|
||||
|
@ -3708,6 +3728,7 @@ fn runtime_args_parse(
|
|||
v8_flags_arg_parse(flags, matches);
|
||||
seed_arg_parse(flags, matches);
|
||||
enable_testing_features_arg_parse(flags, matches);
|
||||
env_file_arg_parse(flags, matches);
|
||||
}
|
||||
|
||||
fn inspect_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||
|
@ -3745,6 +3766,10 @@ fn import_map_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
|||
flags.import_map_path = matches.remove_one::<String>("import-map");
|
||||
}
|
||||
|
||||
fn env_file_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||
flags.env_file = matches.remove_one::<String>("env");
|
||||
}
|
||||
|
||||
fn reload_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||
if let Some(cache_bl) = matches.remove_many::<String>("reload") {
|
||||
let raw_cache_blocklist: Vec<String> = cache_bl.collect();
|
||||
|
@ -5175,7 +5200,7 @@ mod tests {
|
|||
#[test]
|
||||
fn eval_with_flags() {
|
||||
#[rustfmt::skip]
|
||||
let r = flags_from_vec(svec!["deno", "eval", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "42"]);
|
||||
let r = flags_from_vec(svec!["deno", "eval", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--env=.example.env", "42"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
|
@ -5204,6 +5229,7 @@ mod tests {
|
|||
allow_write: Some(vec![]),
|
||||
allow_ffi: Some(vec![]),
|
||||
allow_hrtime: true,
|
||||
env_file: Some(".example.env".to_owned()),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -5273,7 +5299,7 @@ mod tests {
|
|||
#[test]
|
||||
fn repl_with_flags() {
|
||||
#[rustfmt::skip]
|
||||
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors"]);
|
||||
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
|
@ -5304,6 +5330,7 @@ mod tests {
|
|||
allow_write: Some(vec![]),
|
||||
allow_ffi: Some(vec![]),
|
||||
allow_hrtime: true,
|
||||
env_file: Some(".example.env".to_owned()),
|
||||
unsafely_ignore_certificate_errors: Some(vec![]),
|
||||
..Flags::default()
|
||||
}
|
||||
|
@ -6067,6 +6094,39 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_env_file_default() {
|
||||
let r = flags_from_vec(svec!["deno", "run", "--env", "script.ts"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Run(RunFlags {
|
||||
script: "script.ts".to_string(),
|
||||
watch: Default::default(),
|
||||
}),
|
||||
env_file: Some(".env".to_owned()),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_env_file_defined() {
|
||||
let r =
|
||||
flags_from_vec(svec!["deno", "run", "--env=.another_env", "script.ts"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Run(RunFlags {
|
||||
script: "script.ts".to_string(),
|
||||
watch: Default::default(),
|
||||
}),
|
||||
env_file: Some(".another_env".to_owned()),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_multiple() {
|
||||
let r =
|
||||
|
@ -6148,7 +6208,7 @@ mod tests {
|
|||
#[test]
|
||||
fn install_with_flags() {
|
||||
#[rustfmt::skip]
|
||||
let r = flags_from_vec(svec!["deno", "install", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--name", "file_server", "--root", "/foo", "--force", "https://deno.land/std/http/file_server.ts", "foo", "bar"]);
|
||||
let r = flags_from_vec(svec!["deno", "install", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--name", "file_server", "--root", "/foo", "--force", "--env=.example.env", "https://deno.land/std/http/file_server.ts", "foo", "bar"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
|
@ -6174,6 +6234,7 @@ mod tests {
|
|||
allow_net: Some(vec![]),
|
||||
unsafely_ignore_certificate_errors: Some(vec![]),
|
||||
allow_read: Some(vec![]),
|
||||
env_file: Some(".example.env".to_owned()),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -7557,7 +7618,7 @@ mod tests {
|
|||
#[test]
|
||||
fn compile_with_flags() {
|
||||
#[rustfmt::skip]
|
||||
let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--output", "colors", "https://deno.land/std/examples/colors.ts", "foo", "bar", "-p", "8080"]);
|
||||
let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--output", "colors", "--env=.example.env", "https://deno.land/std/examples/colors.ts", "foo", "bar", "-p", "8080"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
|
@ -7584,6 +7645,7 @@ mod tests {
|
|||
allow_net: Some(vec![]),
|
||||
v8_flags: svec!["--help", "--random-seed=1"],
|
||||
seed: Some(1),
|
||||
env_file: Some(".example.env".to_owned()),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
|
|
@ -52,6 +52,7 @@ use deno_runtime::deno_tls::rustls_pemfile;
|
|||
use deno_runtime::deno_tls::webpki_roots;
|
||||
use deno_runtime::inspector_server::InspectorServer;
|
||||
use deno_runtime::permissions::PermissionsOptions;
|
||||
use dotenvy::from_filename;
|
||||
use once_cell::sync::Lazy;
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::Deserialize;
|
||||
|
@ -651,6 +652,12 @@ impl CliOptions {
|
|||
let maybe_vendor_folder =
|
||||
resolve_vendor_folder(&initial_cwd, &flags, maybe_config_file.as_ref());
|
||||
|
||||
if let Some(env_file_name) = &flags.env_file {
|
||||
if (from_filename(env_file_name)).is_err() {
|
||||
bail!("Unable to load '{env_file_name}' environment variable file")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
flags,
|
||||
initial_cwd,
|
||||
|
|
|
@ -77,3 +77,16 @@ itest!(check_local_by_default2 {
|
|||
output: "eval/check_local_by_default2.out",
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(env_file {
|
||||
args: "eval --env=env console.log(Deno.env.get(\"ANOTHER_FOO\"))",
|
||||
output_str: Some("ANOTHER_BAR\n"),
|
||||
});
|
||||
|
||||
itest!(env_file_missing {
|
||||
args: "eval --env=missing console.log(Deno.env.get(\"ANOTHER_FOO\"))",
|
||||
output_str: Some(
|
||||
"error: Unable to load 'missing' environment variable file\n"
|
||||
),
|
||||
exit_code: 1,
|
||||
});
|
||||
|
|
|
@ -1058,3 +1058,19 @@ fn closed_file_pre_load_does_not_occur() {
|
|||
assert_contains!(console.all_output(), "Skipping document preload.",);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env_file() {
|
||||
TestContext::default()
|
||||
.new_command()
|
||||
.args_vec([
|
||||
"repl",
|
||||
"--env=env",
|
||||
"--allow-env",
|
||||
"--eval",
|
||||
"console.log(Deno.env.get('FOO'))",
|
||||
])
|
||||
.with_pty(|console| {
|
||||
assert_contains!(console.all_output(), "BAR",);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -808,6 +808,19 @@ fn permissions_cache() {
|
|||
});
|
||||
}
|
||||
|
||||
itest!(env_file {
|
||||
args: "run --env=env --allow-env run/env_file.ts",
|
||||
output: "run/env_file.out",
|
||||
});
|
||||
|
||||
itest!(env_file_missing {
|
||||
args: "run --env=missing --allow-env run/env_file.ts",
|
||||
output_str: Some(
|
||||
"error: Unable to load 'missing' environment variable file\n"
|
||||
),
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(_091_use_define_for_class_fields {
|
||||
args: "run --check run/091_use_define_for_class_fields.ts",
|
||||
output: "run/091_use_define_for_class_fields.ts.out",
|
||||
|
|
4
cli/tests/testdata/env
vendored
Normal file
4
cli/tests/testdata/env
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
FOO=BAR
|
||||
ANOTHER_FOO=ANOTHER_${FOO}
|
||||
MULTILINE="First Line
|
||||
Second Line"
|
4
cli/tests/testdata/run/env_file.out
vendored
Normal file
4
cli/tests/testdata/run/env_file.out
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
BAR
|
||||
ANOTHER_BAR
|
||||
First Line
|
||||
Second Line
|
3
cli/tests/testdata/run/env_file.ts
vendored
Normal file
3
cli/tests/testdata/run/env_file.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
console.log(Deno.env.get("FOO"));
|
||||
console.log(Deno.env.get("ANOTHER_FOO"));
|
||||
console.log(Deno.env.get("MULTILINE"));
|
Loading…
Reference in a new issue