Refactor nu-cli/env* (#3041)

* Revert "History, more test coverage improvements, and refactorings. (#3217)"

This reverts commit 8fc8fc89aa.

* Add tests

* Refactor .nu-env

* Change logic of Config write to logic of read()

* Fix reload always appends to old vars

* Fix reload always takes last_modified of global config

* Add reload_config in evaluation context

* Reload config after writing to it in cfg set / cfg set_into

* Add --no-history to cli options

* Use --no-history in tests

* Add comment about maybe_print_errors

* Get ctrl_exit var from context.global_config

* Use context.global_config in command "config"

* Add Readme in engine how env vars are now handled

* Update docs from autoenv command

* Move history_path from engine to nu_data

* Move load history out of if

* No let before return

* Add import for indexmap
This commit is contained in:
Leonhard Kipp
2021-03-31 07:52:34 +02:00
committed by GitHub
parent 4faaa5310e
commit c42b588782
70 changed files with 1615 additions and 1887 deletions

View File

@ -2,49 +2,8 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use serde::Deserialize;
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::io::Read;
use std::path::{Path, PathBuf};
pub struct Autoenv;
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct Trusted {
pub files: IndexMap<String, Vec<u8>>,
}
impl Trusted {
pub fn new() -> Self {
Trusted {
files: IndexMap::new(),
}
}
}
pub fn file_is_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
let nufile = std::fs::canonicalize(nu_env_file)?;
let trusted = read_trusted()?;
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
}
pub fn read_trusted() -> Result<Trusted, ShellError> {
let config_path = config::default_path_for(&Some(PathBuf::from("nu-env.toml")))?;
let mut file = std::fs::OpenOptions::new()
.read(true)
.create(true)
.write(true)
.open(config_path)
.map_err(|_| ShellError::untagged_runtime_error("Couldn't open nu-env.toml"))?;
let mut doc = String::new();
file.read_to_string(&mut doc)?;
let allowed = toml::de::from_str(doc.as_str()).unwrap_or_else(|_| Trusted::new());
Ok(allowed)
}
#[async_trait]
impl WholeStreamCommand for Autoenv {
fn name(&self) -> &str {
@ -56,11 +15,12 @@ impl WholeStreamCommand for Autoenv {
fn extra_usage(&self) -> &str {
// "Mark a .nu-env file in a directory as trusted. Needs to be re-run after each change to the file or its filepath."
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell read it when entering the directory.
The file can contain several optional sections:
env: environment variables to set when visiting the directory. The variables are unset after leaving the directory and any overwritten values are restored.
scriptvars: environment variables that should be set to the return value of a script. After they have been set, they behave in the same way as variables set in the env section.
scripts: scripts to run when entering the directory or leaving it."#
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell load it when entering the directory.
The .nu-env file has the same format as your $HOME/nu/config.toml file. By loading a .nu-env file the following applies:
- environment variables (section \"[env]\") are loaded from the .nu-env file. Those env variables are only existend in this directory (and children directories)
- the \"startup\" commands are run when entering the directory
- the \"on_exit\" commands are run when leaving the directory
"#
}
fn signature(&self) -> Signature {
@ -76,15 +36,12 @@ The file can contain several optional sections:
vec![Example {
description: "Example .nu-env file",
example: r#"cat .nu-env
startup = ["echo ...entering the directory", "echo 1 2 3"]
on_exit = ["echo ...leaving the directory"]
[env]
mykey = "myvalue"
[scriptvars]
myscript = "echo myval"
[scripts]
entryscripts = ["touch hello.txt", "touch hello2.txt"]
exitscripts = ["touch bye.txt"]"#,
"#,
result: None,
}]
}

View File

@ -1,5 +1,5 @@
use super::autoenv::read_trusted;
use crate::prelude::*;
use nu_data::config::read_trusted;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::SyntaxShape;

View File

@ -1,5 +1,5 @@
use super::autoenv::Trusted;
use crate::prelude::*;
use nu_data::config::Trusted;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::SyntaxShape;

View File

@ -2,7 +2,7 @@ use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewC
use crate::prelude::*;
use crate::primitive::get_color_config;
use nu_data::value::format_leaf;
use nu_engine::{UnevaluatedCallInfo, WholeStreamCommand};
use nu_engine::{ConfigHolder, RunnableContext, UnevaluatedCallInfo, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
@ -27,16 +27,7 @@ impl WholeStreamCommand for Command {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
autoview(RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
})
.await
autoview(RunnableContext::from_command_args(args)).await
}
fn examples(&self) -> Vec<Example> {
@ -60,6 +51,7 @@ pub struct RunnableContextWithoutInput {
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub ctrl_c: Arc<AtomicBool>,
pub configs: Arc<Mutex<ConfigHolder>>,
pub scope: Scope,
pub name: Tag,
}
@ -70,6 +62,7 @@ impl RunnableContextWithoutInput {
shell_manager: context.shell_manager,
host: context.host,
ctrl_c: context.ctrl_c,
configs: context.configs,
current_errors: context.current_errors,
scope: context.scope,
name: context.name,
@ -288,6 +281,7 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
RawCommandArgs {
host: context.host.clone(),
ctrl_c: context.ctrl_c.clone(),
configs: context.configs.clone(),
current_errors: context.current_errors.clone(),
shell_manager: context.shell_manager.clone(),
call_info: UnevaluatedCallInfo {

View File

@ -509,7 +509,7 @@ mod tests {
#[cfg(feature = "which")]
use futures::executor::block_on;
#[cfg(feature = "which")]
use nu_engine::EvaluationContext;
use nu_engine::basic_evaluation_context;
#[cfg(feature = "which")]
use nu_errors::ShellError;
#[cfg(feature = "which")]
@ -534,7 +534,7 @@ mod tests {
let input = InputStream::empty();
let mut ctx =
EvaluationContext::basic().expect("There was a problem creating a basic context.");
basic_evaluation_context().expect("There was a problem creating a basic context.");
assert!(
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
@ -548,7 +548,7 @@ mod tests {
// async fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false)
// .await?
// .expect("There was a problem running the external command.");

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::CommandArgs;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_stream::OutputStream;
pub struct Command;
@ -22,20 +22,21 @@ impl WholeStreamCommand for Command {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let name = args.call_info.name_tag;
let path = match args.scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let result = nu_data::config::read(&name, &path)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)])
.to_output_stream())
if let Some(global_cfg) = &args.configs.lock().global_config {
let result = global_cfg.vars.clone();
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)])
.to_output_stream())
} else {
Ok(
futures::stream::iter(vec![ReturnSuccess::value(UntaggedValue::Error(
ShellError::untagged_runtime_error("No global config found!"),
))])
.to_output_stream(),
)
}
}
}

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
ColumnPath, ConfigPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct SubCommand;
@ -61,6 +61,7 @@ impl WholeStreamCommand for SubCommand {
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let scope = args.scope.clone();
let (
Arguments {
@ -93,6 +94,10 @@ pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
..
}) => {
config::write(&changes.entries, &path)?;
ctx.reload_config(&ConfigPath::Global(
path.expect("Global config path is always some"),
))
.await?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(changes).into_value(name),

View File

@ -1,7 +1,9 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
ConfigPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
pub struct SubCommand;
@ -44,6 +46,7 @@ impl WholeStreamCommand for SubCommand {
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let scope = args.scope.clone();
let (Arguments { set_into: v }, input) = args.process().await?;
@ -73,6 +76,10 @@ pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
result.insert(key, value.clone());
config::write(&result, &path)?;
ctx.reload_config(&ConfigPath::Global(
path.expect("Global config path is always some"),
))
.await?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
@ -84,6 +91,10 @@ pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
result.insert(key, value);
config::write(&result, &path)?;
ctx.reload_config(&ConfigPath::Global(
path.expect("Global config path is always some"),
))
.await?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),

View File

@ -1,9 +1,10 @@
use crate::prelude::*;
use nu_engine::basic_evaluation_context;
use nu_engine::whole_stream_command;
use nu_engine::EvaluationContext;
use std::error::Error;
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
let context = EvaluationContext::basic()?;
let context = basic_evaluation_context()?;
{
use crate::commands::*;

View File

@ -81,6 +81,7 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let shell_manager = raw_args.shell_manager.clone();
let head = raw_args.call_info.args.head.clone();
let ctrl_c = raw_args.ctrl_c.clone();
let configs = raw_args.configs.clone();
let current_errors = raw_args.current_errors.clone();
let host = raw_args.host.clone();
let tag = raw_args.call_info.name_tag.clone();
@ -132,6 +133,7 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let new_args = RawCommandArgs {
host,
ctrl_c,
configs,
current_errors,
shell_manager,
call_info: UnevaluatedCallInfo {

View File

@ -1,5 +1,5 @@
use crate::prelude::*;
use nu_data::config::{path::history as history_path, NuConfig};
use nu_data::config::{Conf, NuConfig};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
@ -33,11 +33,11 @@ impl WholeStreamCommand for History {
}
async fn history(args: CommandArgs) -> Result<OutputStream, ShellError> {
let config = NuConfig::new();
let config: Box<dyn Conf> = Box::new(NuConfig::new());
let tag = args.call_info.name_tag.clone();
let (Arguments { clear }, _) = args.process().await?;
let path = history_path(&config);
let path = nu_data::config::path::history_path(&config);
match clear {
Some(_) => {

View File

@ -29,19 +29,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
average,
)
.await
run_with_function(RunnableContext::from_command_args(args), average).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -23,15 +23,7 @@ impl WholeStreamCommand for SubCommand {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_numerical_functions_on_stream(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
RunnableContext::from_command_args(args),
ceil_big_int,
ceil_big_decimal,
ceil_default,

View File

@ -23,15 +23,7 @@ impl WholeStreamCommand for SubCommand {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_numerical_functions_on_stream(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
RunnableContext::from_command_args(args),
floor_big_int,
floor_big_decimal,
floor_default,

View File

@ -22,19 +22,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
maximum,
)
.await
run_with_function(RunnableContext::from_command_args(args), maximum).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -26,19 +26,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
median,
)
.await
run_with_function(RunnableContext::from_command_args(args), median).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -22,19 +22,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
minimum,
)
.await
run_with_function(RunnableContext::from_command_args(args), minimum).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -22,19 +22,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
mode,
)
.await
run_with_function(RunnableContext::from_command_args(args), mode).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -22,19 +22,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
product,
)
.await
run_with_function(RunnableContext::from_command_args(args), product).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -23,19 +23,7 @@ impl WholeStreamCommand for SubCommand {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
},
summation,
)
.await
run_with_function(RunnableContext::from_command_args(args), summation).await
}
fn examples(&self) -> Vec<Example> {

View File

@ -83,9 +83,9 @@ impl WholeStreamCommand for RunExternalCommand {
EvaluationContext {
scope: args.scope.clone(),
host: args.host.clone(),
user_recently_used_autoenv_untrust: Arc::new(AtomicBool::new(false)),
shell_manager: args.shell_manager.clone(),
ctrl_c: args.ctrl_c.clone(),
configs: args.configs.clone(),
current_errors: Arc::new(Mutex::new(vec![])),
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
}

View File

@ -168,6 +168,7 @@ async fn save(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let scope = raw_args.scope.clone();
let host = raw_args.host.clone();
let ctrl_c = raw_args.ctrl_c.clone();
let configs = raw_args.configs.clone();
let current_errors = raw_args.current_errors.clone();
let shell_manager = raw_args.shell_manager.clone();
@ -215,6 +216,7 @@ async fn save(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let new_args = RawCommandArgs {
host,
ctrl_c,
configs,
current_errors,
shell_manager: shell_manager.clone(),
call_info: UnevaluatedCallInfo {

View File

@ -6,12 +6,14 @@ mod stub_generate;
use double_echo::Command as DoubleEcho;
use double_ls::Command as DoubleLs;
use stub_generate::{mock_path, Command as StubOpen};
use nu_engine::basic_evaluation_context;
use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_protocol::hir::ClassifiedBlock;
use nu_protocol::{ShellTypeName, Value};
use nu_source::AnchorLocation;
use stub_generate::{mock_path, Command as StubOpen};
use crate::commands::{
Append, BuildString, Each, Echo, First, Get, Keep, Last, Let, Nth, Select, StrCollect, Wrap,
@ -24,7 +26,7 @@ use futures::executor::block_on;
pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples();
let base_context = EvaluationContext::basic()?;
let base_context = basic_evaluation_context()?;
base_context.add_commands(vec![
// Command Doubles
@ -90,7 +92,7 @@ pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
let examples = cmd.examples();
let base_context = EvaluationContext::basic()?;
let base_context = basic_evaluation_context()?;
base_context.add_commands(vec![
whole_stream_command(Echo {}),
@ -147,7 +149,7 @@ pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples();
let base_context = EvaluationContext::basic()?;
let base_context = basic_evaluation_context()?;
base_context.add_commands(vec![
// Minimal restricted commands to aid in testing
@ -228,10 +230,8 @@ async fn evaluate_block(
ctx: &mut EvaluationContext,
) -> Result<Vec<Value>, ShellError> {
let input_stream = InputStream::empty();
let env = ctx.get_env();
ctx.scope.enter_scope();
ctx.scope.add_env(env);
let result = run_block(&block.block, ctx, input_stream).await;

View File

@ -1,5 +1,17 @@
use std::io::{self, BufRead, Write};
/// Echo's value of env keys from args
/// Example: nu --testbin env_echo FOO BAR
/// If it it's not present echo's nothing
pub fn echo_env() {
let args = args();
for arg in args {
if let Ok(v) = std::env::var(arg) {
println!("{}", v);
}
}
}
pub fn cococo() {
let args: Vec<String> = args();