mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
feat: Add support for passing a key to Deno.env() (#2952)
This adds a new op to get a single env var.
This commit is contained in:
parent
c920c5f62a
commit
99eec73b4b
7 changed files with 144 additions and 9 deletions
|
@ -11,6 +11,7 @@ use hyper;
|
||||||
use reqwest;
|
use reqwest;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use std;
|
use std;
|
||||||
|
use std::env::VarError;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -136,6 +137,16 @@ impl GetErrorKind for ModuleResolutionError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GetErrorKind for VarError {
|
||||||
|
fn kind(&self) -> ErrorKind {
|
||||||
|
use VarError::*;
|
||||||
|
match self {
|
||||||
|
NotPresent => ErrorKind::NotFound,
|
||||||
|
NotUnicode(..) => ErrorKind::InvalidData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GetErrorKind for io::Error {
|
impl GetErrorKind for io::Error {
|
||||||
fn kind(&self) -> ErrorKind {
|
fn kind(&self) -> ErrorKind {
|
||||||
use io::ErrorKind::*;
|
use io::ErrorKind::*;
|
||||||
|
@ -294,6 +305,7 @@ impl GetErrorKind for dyn AnyError {
|
||||||
.or_else(|| self.downcast_ref::<StaticError>().map(Get::kind))
|
.or_else(|| self.downcast_ref::<StaticError>().map(Get::kind))
|
||||||
.or_else(|| self.downcast_ref::<uri::InvalidUri>().map(Get::kind))
|
.or_else(|| self.downcast_ref::<uri::InvalidUri>().map(Get::kind))
|
||||||
.or_else(|| self.downcast_ref::<url::ParseError>().map(Get::kind))
|
.or_else(|| self.downcast_ref::<url::ParseError>().map(Get::kind))
|
||||||
|
.or_else(|| self.downcast_ref::<VarError>().map(Get::kind))
|
||||||
.or_else(|| self.downcast_ref::<ReadlineError>().map(Get::kind))
|
.or_else(|| self.downcast_ref::<ReadlineError>().map(Get::kind))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self
|
self
|
||||||
|
|
|
@ -102,6 +102,25 @@ pub fn op_env(
|
||||||
Ok(JsonOp::Sync(json!(v)))
|
Ok(JsonOp::Sync(json!(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct GetEnv {
|
||||||
|
key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn op_get_env(
|
||||||
|
state: &ThreadSafeState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
let args: GetEnv = serde_json::from_value(args)?;
|
||||||
|
state.check_env()?;
|
||||||
|
let r = match env::var(args.key) {
|
||||||
|
Err(env::VarError::NotPresent) => json!([]),
|
||||||
|
v => json!([v?]),
|
||||||
|
};
|
||||||
|
Ok(JsonOp::Sync(r))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Exit {
|
struct Exit {
|
||||||
code: i32,
|
code: i32,
|
||||||
|
|
|
@ -63,6 +63,10 @@ impl Worker {
|
||||||
"set_env",
|
"set_env",
|
||||||
state_.cli_op(json_op(state_.stateful_op(os::op_set_env))),
|
state_.cli_op(json_op(state_.stateful_op(os::op_set_env))),
|
||||||
);
|
);
|
||||||
|
i.register_op(
|
||||||
|
"get_env",
|
||||||
|
state_.cli_op(json_op(state_.stateful_op(os::op_get_env))),
|
||||||
|
);
|
||||||
i.register_op(
|
i.register_op(
|
||||||
"home_dir",
|
"home_dir",
|
||||||
state_.cli_op(json_op(state_.stateful_op(os::op_home_dir))),
|
state_.cli_op(json_op(state_.stateful_op(os::op_home_dir))),
|
||||||
|
|
|
@ -11,6 +11,7 @@ export let OP_ENV: number;
|
||||||
export let OP_EXEC_PATH: number;
|
export let OP_EXEC_PATH: number;
|
||||||
export let OP_UTIME: number;
|
export let OP_UTIME: number;
|
||||||
export let OP_SET_ENV: number;
|
export let OP_SET_ENV: number;
|
||||||
|
export let OP_GET_ENV: number;
|
||||||
export let OP_HOME_DIR: number;
|
export let OP_HOME_DIR: number;
|
||||||
export let OP_START: number;
|
export let OP_START: number;
|
||||||
export let OP_APPLY_SOURCE_MAP: number;
|
export let OP_APPLY_SOURCE_MAP: number;
|
||||||
|
|
10
js/lib.deno_runtime.d.ts
vendored
10
js/lib.deno_runtime.d.ts
vendored
|
@ -44,6 +44,16 @@ declare namespace Deno {
|
||||||
export function env(): {
|
export function env(): {
|
||||||
[index: string]: string;
|
[index: string]: string;
|
||||||
};
|
};
|
||||||
|
/** Returns the value of an environment variable at invocation.
|
||||||
|
* If the variable is not present, `undefined` will be returned.
|
||||||
|
*
|
||||||
|
* const myEnv = Deno.env();
|
||||||
|
* console.log(myEnv.SHELL);
|
||||||
|
* myEnv.TEST_VAR = "HELLO";
|
||||||
|
* const newEnv = Deno.env();
|
||||||
|
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
|
||||||
|
*/
|
||||||
|
export function env(key: string): string | undefined;
|
||||||
/**
|
/**
|
||||||
* Returns the current user's home directory.
|
* Returns the current user's home directory.
|
||||||
* Requires the `--allow-env` flag.
|
* Requires the `--allow-env` flag.
|
||||||
|
|
14
js/os.ts
14
js/os.ts
|
@ -37,18 +37,30 @@ function setEnv(key: string, value: string): void {
|
||||||
sendSync(dispatch.OP_SET_ENV, { key, value });
|
sendSync(dispatch.OP_SET_ENV, { key, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEnv(key: string): string | undefined {
|
||||||
|
return sendSync(dispatch.OP_GET_ENV, { key })[0];
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns a snapshot of the environment variables at invocation. Mutating a
|
/** Returns a snapshot of the environment variables at invocation. Mutating a
|
||||||
* property in the object will set that variable in the environment for
|
* property in the object will set that variable in the environment for
|
||||||
* the process. The environment object will only accept `string`s
|
* the process. The environment object will only accept `string`s
|
||||||
* as values.
|
* as values.
|
||||||
*
|
*
|
||||||
|
* console.log(Deno.env("SHELL"));
|
||||||
* const myEnv = Deno.env();
|
* const myEnv = Deno.env();
|
||||||
* console.log(myEnv.SHELL);
|
* console.log(myEnv.SHELL);
|
||||||
* myEnv.TEST_VAR = "HELLO";
|
* myEnv.TEST_VAR = "HELLO";
|
||||||
* const newEnv = Deno.env();
|
* const newEnv = Deno.env();
|
||||||
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
|
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
|
||||||
*/
|
*/
|
||||||
export function env(): { [index: string]: string } {
|
export function env(): { [index: string]: string };
|
||||||
|
export function env(key: string): string | undefined;
|
||||||
|
export function env(
|
||||||
|
key?: string
|
||||||
|
): { [index: string]: string } | string | undefined {
|
||||||
|
if (key) {
|
||||||
|
return getEnv(key);
|
||||||
|
}
|
||||||
const env = sendSync(dispatch.OP_ENV);
|
const env = sendSync(dispatch.OP_ENV);
|
||||||
return new Proxy(env, {
|
return new Proxy(env, {
|
||||||
set(obj, prop: string, value: string): boolean {
|
set(obj, prop: string, value: string): boolean {
|
||||||
|
|
|
@ -14,21 +14,98 @@ testPerm({ env: true }, function envSuccess(): void {
|
||||||
env.test_var = "Hello World";
|
env.test_var = "Hello World";
|
||||||
const newEnv = Deno.env();
|
const newEnv = Deno.env();
|
||||||
assertEquals(env.test_var, newEnv.test_var);
|
assertEquals(env.test_var, newEnv.test_var);
|
||||||
|
assertEquals(Deno.env("test_var"), env.test_var);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function envFailure(): void {
|
testPerm({ env: true }, function envNotFound(): void {
|
||||||
let caughtError = false;
|
const r = Deno.env("env_var_does_not_exist!");
|
||||||
|
assertEquals(r, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function envPermissionDenied1(): void {
|
||||||
|
let err;
|
||||||
try {
|
try {
|
||||||
Deno.env();
|
Deno.env();
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
caughtError = true;
|
err = e;
|
||||||
assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
|
|
||||||
assertEquals(err.name, "PermissionDenied");
|
|
||||||
}
|
}
|
||||||
|
assertNotEquals(err, undefined);
|
||||||
assert(caughtError);
|
assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
|
||||||
|
assertEquals(err.name, "PermissionDenied");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(function envPermissionDenied2(): void {
|
||||||
|
let err;
|
||||||
|
try {
|
||||||
|
Deno.env("PATH");
|
||||||
|
} catch (e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
assertNotEquals(err, undefined);
|
||||||
|
assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
|
||||||
|
assertEquals(err.name, "PermissionDenied");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Deno.build.os === "win") {
|
||||||
|
// This test verifies that on Windows, environment variables are
|
||||||
|
// case-insensitive. Case normalization needs be done using the collation
|
||||||
|
// that Windows uses, rather than naively using String.toLowerCase().
|
||||||
|
testPerm({ env: true, run: true }, async function envCaseInsensitive() {
|
||||||
|
// Utility function that runs a Deno subprocess with the environment
|
||||||
|
// specified in `inputEnv`. The subprocess reads the environment variables
|
||||||
|
// which are in the keys of `expectedEnv` and writes them to stdout as JSON.
|
||||||
|
// It is then verified that these match with the values of `expectedEnv`.
|
||||||
|
const checkChildEnv = async (inputEnv, expectedEnv): Promise<void> => {
|
||||||
|
const src = `
|
||||||
|
console.log(
|
||||||
|
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env(k))
|
||||||
|
)`;
|
||||||
|
const proc = Deno.run({
|
||||||
|
args: [Deno.execPath(), "eval", src],
|
||||||
|
env: inputEnv,
|
||||||
|
stdout: "piped"
|
||||||
|
});
|
||||||
|
const status = await proc.status();
|
||||||
|
assertEquals(status.success, true);
|
||||||
|
const expectedValues = Object.values(expectedEnv);
|
||||||
|
const actualValues = JSON.parse(
|
||||||
|
new TextDecoder().decode(await proc.output())
|
||||||
|
);
|
||||||
|
assertEquals(actualValues, expectedValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
assertEquals(Deno.env("path"), Deno.env("PATH"));
|
||||||
|
assertEquals(Deno.env("Path"), Deno.env("PATH"));
|
||||||
|
|
||||||
|
// Check 'foo', 'Foo' and 'Foo' are case folded.
|
||||||
|
await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" });
|
||||||
|
|
||||||
|
// Check that 'µ' and 'Μ' are not case folded.
|
||||||
|
const lc1 = "µ";
|
||||||
|
const uc1 = lc1.toUpperCase();
|
||||||
|
assertNotEquals(lc1, uc1);
|
||||||
|
await checkChildEnv(
|
||||||
|
{ [lc1]: "mu", [uc1]: "MU" },
|
||||||
|
{ [lc1]: "mu", [uc1]: "MU" }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check that 'dž' and 'DŽ' are folded, but 'Dž' is preserved.
|
||||||
|
const c2 = "Dž";
|
||||||
|
const lc2 = c2.toLowerCase();
|
||||||
|
const uc2 = c2.toUpperCase();
|
||||||
|
assertNotEquals(c2, lc2);
|
||||||
|
assertNotEquals(c2, uc2);
|
||||||
|
await checkChildEnv(
|
||||||
|
{ [c2]: "Dz", [lc2]: "dz" },
|
||||||
|
{ [c2]: "Dz", [lc2]: "dz", [uc2]: "dz" }
|
||||||
|
);
|
||||||
|
await checkChildEnv(
|
||||||
|
{ [c2]: "Dz", [uc2]: "DZ" },
|
||||||
|
{ [c2]: "Dz", [uc2]: "DZ", [lc2]: "DZ" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test(function osPid(): void {
|
test(function osPid(): void {
|
||||||
console.log("pid", Deno.pid);
|
console.log("pid", Deno.pid);
|
||||||
assert(Deno.pid > 0);
|
assert(Deno.pid > 0);
|
||||||
|
|
Loading…
Reference in a new issue