forked from extern/nushell
Add with-env command (#1717)
This commit is contained in:
parent
22e70478a4
commit
b37e420c7c
@ -261,6 +261,7 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(Which),
|
whole_stream_command(Which),
|
||||||
whole_stream_command(Debug),
|
whole_stream_command(Debug),
|
||||||
whole_stream_command(Alias),
|
whole_stream_command(Alias),
|
||||||
|
whole_stream_command(WithEnv),
|
||||||
// Statistics
|
// Statistics
|
||||||
whole_stream_command(Size),
|
whole_stream_command(Size),
|
||||||
whole_stream_command(Count),
|
whole_stream_command(Count),
|
||||||
@ -856,8 +857,8 @@ async fn process_line(
|
|||||||
classified_block.block.expand_it_usage();
|
classified_block.block.expand_it_usage();
|
||||||
|
|
||||||
trace!("{:#?}", classified_block);
|
trace!("{:#?}", classified_block);
|
||||||
|
let env = ctx.get_env();
|
||||||
match run_block(&classified_block.block, ctx, input_stream, &Scope::empty()).await {
|
match run_block(&classified_block.block, ctx, input_stream, &Scope::env(env)).await {
|
||||||
Ok(input) => {
|
Ok(input) => {
|
||||||
// Running a pipeline gives us back a stream that we can then
|
// Running a pipeline gives us back a stream that we can then
|
||||||
// work through. At the top level, we just want to pull on the
|
// work through. At the top level, we just want to pull on the
|
||||||
|
@ -119,6 +119,7 @@ pub(crate) mod version;
|
|||||||
pub(crate) mod what;
|
pub(crate) mod what;
|
||||||
pub(crate) mod where_;
|
pub(crate) mod where_;
|
||||||
pub(crate) mod which_;
|
pub(crate) mod which_;
|
||||||
|
pub(crate) mod with_env;
|
||||||
pub(crate) mod wrap;
|
pub(crate) mod wrap;
|
||||||
|
|
||||||
pub(crate) use autoview::Autoview;
|
pub(crate) use autoview::Autoview;
|
||||||
@ -241,4 +242,5 @@ pub(crate) use version::Version;
|
|||||||
pub(crate) use what::What;
|
pub(crate) use what::What;
|
||||||
pub(crate) use where_::Where;
|
pub(crate) use where_::Where;
|
||||||
pub(crate) use which_::Which;
|
pub(crate) use which_::Which;
|
||||||
|
pub(crate) use with_env::WithEnv;
|
||||||
pub(crate) use wrap::Wrap;
|
pub(crate) use wrap::Wrap;
|
||||||
|
@ -159,7 +159,7 @@ fn run_with_stdin(
|
|||||||
})
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
spawn(&command, &path, &process_args[..], input, is_last)
|
spawn(&command, &path, &process_args[..], input, is_last, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(
|
fn spawn(
|
||||||
@ -168,6 +168,7 @@ fn spawn(
|
|||||||
args: &[String],
|
args: &[String],
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
is_last: bool,
|
is_last: bool,
|
||||||
|
scope: &Scope,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
let command = command.clone();
|
let command = command.clone();
|
||||||
|
|
||||||
@ -197,6 +198,9 @@ fn spawn(
|
|||||||
process.current_dir(path);
|
process.current_dir(path);
|
||||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||||
|
|
||||||
|
process.env_clear();
|
||||||
|
process.envs(scope.env.iter());
|
||||||
|
|
||||||
// We want stdout regardless of what
|
// We want stdout regardless of what
|
||||||
// we are doing ($it case or pipe stdin)
|
// we are doing ($it case or pipe stdin)
|
||||||
if !is_last {
|
if !is_last {
|
||||||
|
@ -33,6 +33,7 @@ pub(crate) fn run_internal_command(
|
|||||||
|
|
||||||
let mut result = trace_out_stream!(target: "nu::trace_stream::internal", "output" = result);
|
let mut result = trace_out_stream!(target: "nu::trace_stream::internal", "output" = result);
|
||||||
let mut context = context.clone();
|
let mut context = context.clone();
|
||||||
|
let scope = scope.clone();
|
||||||
|
|
||||||
let stream = async_stream! {
|
let stream = async_stream! {
|
||||||
let mut soft_errs: Vec<ShellError> = vec![];
|
let mut soft_errs: Vec<ShellError> = vec![];
|
||||||
@ -67,7 +68,7 @@ pub(crate) fn run_internal_command(
|
|||||||
is_last: false,
|
is_last: false,
|
||||||
},
|
},
|
||||||
name_tag: Tag::unknown_anchor(command.name_span),
|
name_tag: Tag::unknown_anchor(command.name_span),
|
||||||
scope: Scope::empty(),
|
scope: scope.clone(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &context.registry);
|
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &context.registry);
|
||||||
|
@ -6,7 +6,7 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{hir::Block, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{hir::Block, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
pub struct Merge;
|
pub struct Merge;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -48,6 +48,7 @@ fn merge(
|
|||||||
let block = merge_args.block;
|
let block = merge_args.block;
|
||||||
let registry = context.registry.clone();
|
let registry = context.registry.clone();
|
||||||
let mut input = context.input;
|
let mut input = context.input;
|
||||||
|
let scope = raw_args.call_info.scope.clone();
|
||||||
|
|
||||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ fn merge(
|
|||||||
let table: Option<Vec<Value>> = match run_block(&block,
|
let table: Option<Vec<Value>> = match run_block(&block,
|
||||||
&mut context,
|
&mut context,
|
||||||
InputStream::empty(),
|
InputStream::empty(),
|
||||||
&Scope::empty()).await {
|
&scope).await {
|
||||||
Ok(mut stream) => Some(stream.drain_vec().await),
|
Ok(mut stream) => Some(stream.drain_vec().await),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
yield Err(err);
|
yield Err(err);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
|
use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
@ -175,6 +175,7 @@ fn save(
|
|||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let mut full_path = PathBuf::from(shell_manager.path());
|
let mut full_path = PathBuf::from(shell_manager.path());
|
||||||
let name_tag = name.clone();
|
let name_tag = name.clone();
|
||||||
|
let scope = raw_args.call_info.scope.clone();
|
||||||
|
|
||||||
let stream = async_stream! {
|
let stream = async_stream! {
|
||||||
let input: Vec<Value> = input.collect().await;
|
let input: Vec<Value> = input.collect().await;
|
||||||
@ -236,7 +237,7 @@ fn save(
|
|||||||
is_last: false,
|
is_last: false,
|
||||||
},
|
},
|
||||||
name_tag: raw_args.call_info.name_tag,
|
name_tag: raw_args.call_info.name_tag,
|
||||||
scope: Scope::empty(), // FIXME?
|
scope,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut result = converter.run(new_args.with_input(input), ®istry);
|
let mut result = converter.run(new_args.with_input(input), ®istry);
|
||||||
|
87
crates/nu-cli/src/commands/with_env.rs
Normal file
87
crates/nu-cli/src/commands/with_env.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use crate::commands::classified::block::run_block;
|
||||||
|
use crate::commands::WholeStreamCommand;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{hir::Block, ReturnSuccess, Signature, SyntaxShape};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
|
pub struct WithEnv;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct WithEnvArgs {
|
||||||
|
variable: (Tagged<String>, Tagged<String>),
|
||||||
|
block: Block,
|
||||||
|
}
|
||||||
|
impl WholeStreamCommand for WithEnv {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"with-env"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("with-env")
|
||||||
|
.required(
|
||||||
|
"variable",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"the environment variable to temporarily set",
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block,
|
||||||
|
"the block to run once the variable is set",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Runs a block with an environment set. Eg) with-env [NAME 'foo'] { echo $nu.env.NAME }"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
Ok(args.process_raw(registry, with_env)?.run())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_env(
|
||||||
|
WithEnvArgs { variable, block }: WithEnvArgs,
|
||||||
|
context: RunnableContext,
|
||||||
|
raw_args: RawCommandArgs,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let scope = raw_args
|
||||||
|
.call_info
|
||||||
|
.scope
|
||||||
|
.clone()
|
||||||
|
.set_env_var(variable.0.item, variable.1.item);
|
||||||
|
let registry = context.registry.clone();
|
||||||
|
let input = context.input;
|
||||||
|
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||||
|
|
||||||
|
let stream = async_stream! {
|
||||||
|
let result = run_block(
|
||||||
|
&block,
|
||||||
|
&mut context,
|
||||||
|
input,
|
||||||
|
&scope.clone(),
|
||||||
|
).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(mut stream) => {
|
||||||
|
while let Some(result) = stream.next().await {
|
||||||
|
yield Ok(ReturnSuccess::Value(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
let errors = context.get_errors();
|
||||||
|
if let Some(error) = errors.first() {
|
||||||
|
yield Err(error.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
yield Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(stream.to_output_stream())
|
||||||
|
}
|
@ -258,4 +258,12 @@ impl Context {
|
|||||||
input,
|
input,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_env(&self) -> IndexMap<String, String> {
|
||||||
|
let mut output = IndexMap::new();
|
||||||
|
for (var, value) in self.host.lock().vars() {
|
||||||
|
output.insert(var, value);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ fn evaluate_reference(name: &hir::Variable, scope: &Scope, tag: Tag) -> Result<V
|
|||||||
match name {
|
match name {
|
||||||
hir::Variable::It(_) => Ok(scope.it.value.clone().into_value(tag)),
|
hir::Variable::It(_) => Ok(scope.it.value.clone().into_value(tag)),
|
||||||
hir::Variable::Other(name, _) => match name {
|
hir::Variable::Other(name, _) => match name {
|
||||||
x if x == "$nu" => crate::evaluate::variables::nu(tag),
|
x if x == "$nu" => crate::evaluate::variables::nu(scope, tag),
|
||||||
x if x == "$true" => Ok(Value {
|
x if x == "$true" => Ok(Value {
|
||||||
value: UntaggedValue::boolean(true),
|
value: UntaggedValue::boolean(true),
|
||||||
tag,
|
tag,
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use crate::cli::History;
|
use crate::cli::History;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
|
use nu_protocol::{Scope, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::Tag;
|
use nu_source::Tag;
|
||||||
|
|
||||||
pub fn nu(tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
pub fn nu(scope: &Scope, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
|
|
||||||
let mut nu_dict = TaggedDictBuilder::new(&tag);
|
let mut nu_dict = TaggedDictBuilder::new(&tag);
|
||||||
|
|
||||||
let mut dict = TaggedDictBuilder::new(&tag);
|
let mut dict = TaggedDictBuilder::new(&tag);
|
||||||
for v in std::env::vars() {
|
for v in scope.env.iter() {
|
||||||
if v.0 != "PATH" && v.0 != "Path" {
|
if v.0 != "PATH" && v.0 != "Path" {
|
||||||
dict.insert_untagged(v.0, UntaggedValue::string(v.1));
|
dict.insert_untagged(v.0, UntaggedValue::string(v.1));
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,5 @@ mod touch;
|
|||||||
mod trim;
|
mod trim;
|
||||||
mod uniq;
|
mod uniq;
|
||||||
mod where_;
|
mod where_;
|
||||||
|
mod with_env;
|
||||||
mod wrap;
|
mod wrap;
|
||||||
|
11
crates/nu-cli/tests/commands/with_env.rs
Normal file
11
crates/nu-cli/tests/commands/with_env.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_env_extends_environment() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats",
|
||||||
|
"with-env [FOO BARRRR] {echo $nu.env} | get FOO"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual, "BARRRR");
|
||||||
|
}
|
@ -10,6 +10,7 @@ use std::fmt::Debug;
|
|||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
pub it: Value,
|
pub it: Value,
|
||||||
pub vars: IndexMap<String, Value>,
|
pub vars: IndexMap<String, Value>,
|
||||||
|
pub env: IndexMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
@ -18,6 +19,7 @@ impl Scope {
|
|||||||
Scope {
|
Scope {
|
||||||
it,
|
it,
|
||||||
vars: IndexMap::new(),
|
vars: IndexMap::new(),
|
||||||
|
env: IndexMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,6 +30,7 @@ impl Scope {
|
|||||||
Scope {
|
Scope {
|
||||||
it: UntaggedValue::Primitive(Primitive::Nothing).into_untagged_value(),
|
it: UntaggedValue::Primitive(Primitive::Nothing).into_untagged_value(),
|
||||||
vars: IndexMap::new(),
|
vars: IndexMap::new(),
|
||||||
|
env: IndexMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +39,15 @@ impl Scope {
|
|||||||
Scope {
|
Scope {
|
||||||
it: value,
|
it: value,
|
||||||
vars: IndexMap::new(),
|
vars: IndexMap::new(),
|
||||||
|
env: IndexMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env(env: IndexMap<String, String>) -> Scope {
|
||||||
|
Scope {
|
||||||
|
it: UntaggedValue::Primitive(Primitive::Nothing).into_untagged_value(),
|
||||||
|
vars: IndexMap::new(),
|
||||||
|
env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +55,7 @@ impl Scope {
|
|||||||
Scope {
|
Scope {
|
||||||
it: value,
|
it: value,
|
||||||
vars: self.vars,
|
vars: self.vars,
|
||||||
|
env: self.env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +65,17 @@ impl Scope {
|
|||||||
Scope {
|
Scope {
|
||||||
it: self.it,
|
it: self.it,
|
||||||
vars: new_vars,
|
vars: new_vars,
|
||||||
|
env: self.env,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_env_var(self, variable: String, value: String) -> Scope {
|
||||||
|
let mut new_env_vars = self.env.clone();
|
||||||
|
new_env_vars.insert(variable, value);
|
||||||
|
Scope {
|
||||||
|
it: self.it,
|
||||||
|
vars: self.vars,
|
||||||
|
env: new_env_vars,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user