remove the deprecated register command (#13297)

# Description

This PR removes the `register` command which has been
[deprecated](https://www.nushell.sh/blog/2024-04-30-nushell_0_93_0.html#register-toc)
in favor of [`plugin
add`](https://www.nushell.sh/blog/2024-04-30-nushell_0_93_0.html#redesigned-plugin-management-commands-toc)

# User-Facing Changes

`register` is no longer available
This commit is contained in:
Andy Gayton 2024-07-05 08:16:50 -04:00 committed by GitHub
parent afaa019fae
commit b27cd70fd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 24 additions and 447 deletions

View file

@ -1,5 +1,3 @@
mod plugin;
mod register;
pub use plugin::*;
pub use register::Register;

View file

@ -72,7 +72,7 @@ impl Command for PluginStop {
error: format!("Failed to stop the `{}` plugin", name.item),
msg: "couldn't find a plugin with this name".into(),
span: Some(name.span),
help: Some("you may need to `register` the plugin first".into()),
help: Some("you may need to `plugin add` the plugin first".into()),
inner: vec![],
})
}

View file

@ -1,80 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::engine::CommandType;
#[derive(Clone)]
pub struct Register;
impl Command for Register {
fn name(&self) -> &str {
"register"
}
fn usage(&self) -> &str {
"Register a plugin."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("register")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"plugin",
SyntaxShape::Filepath,
"Path of executable for plugin.",
)
.optional(
"signature",
SyntaxShape::Any,
"Block with signature description as json object.",
)
.named(
"shell",
SyntaxShape::Filepath,
"path of shell used to run plugin (cmd, sh, python, etc)",
Some('s'),
)
.category(Category::Plugin)
}
fn extra_usage(&self) -> &str {
r#"
Deprecated in favor of `plugin add` and `plugin use`.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html
"#
.trim()
}
fn search_terms(&self) -> Vec<&str> {
vec!["add"]
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Register `nu_plugin_query` plugin from ~/.cargo/bin/ dir",
example: r#"register ~/.cargo/bin/nu_plugin_query"#,
result: None,
},
Example {
description: "Register `nu_plugin_query` plugin from `nu -c` (writes/updates $nu.plugin-path)",
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
result: None,
},
]
}
}

View file

@ -18,7 +18,6 @@ pub fn add_plugin_command_context(mut engine_state: EngineState) -> EngineState
PluginRm,
PluginStop,
PluginUse,
Register,
);
working_set.render()

View file

@ -24,6 +24,3 @@ pub use parser::{
is_math_expression_like, parse, parse_block, parse_expression, parse_external_call,
parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string, DURATION_UNIT_GROUPS,
};
#[cfg(feature = "plugin")]
pub use parse_keywords::parse_register;

View file

@ -82,7 +82,6 @@ pub const UNALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
b"source-env",
b"source",
b"where",
b"register",
b"plugin use",
];
@ -3627,244 +3626,6 @@ pub fn parse_where(working_set: &mut StateWorkingSet, lite_command: &LiteCommand
}
}
/// `register` is deprecated and will be removed in 0.94. Use `plugin add` and `plugin use` instead.
#[cfg(feature = "plugin")]
pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
use nu_plugin_engine::PluginDeclaration;
use nu_protocol::{
engine::Stack, ErrSpan, ParseWarning, PluginIdentity, PluginRegistryItem, PluginSignature,
RegisteredPlugin,
};
let spans = &lite_command.parts;
#[allow(deprecated)]
let cwd = working_set.get_cwd();
// Checking that the function is used with the correct name
// Maybe this is not necessary but it is a sanity check
if working_set.get_span_contents(spans[0]) != b"register" {
working_set.error(ParseError::UnknownState(
"internal error: Wrong call name for 'register' function".into(),
Span::concat(spans),
));
return garbage_pipeline(working_set, spans);
}
if let Some(redirection) = lite_command.redirection.as_ref() {
working_set.error(redirecting_builtin_error("register", redirection));
return garbage_pipeline(working_set, spans);
}
// Parsing the spans and checking that they match the register signature
// Using a parsed call makes more sense than checking for how many spans are in the call
// Also, by creating a call, it can be checked if it matches the declaration signature
let (call, call_span) = match working_set.find_decl(b"register") {
None => {
working_set.error(ParseError::UnknownState(
"internal error: Register declaration not found".into(),
Span::concat(spans),
));
return garbage_pipeline(working_set, spans);
}
Some(decl_id) => {
let ParsedInternalCall { call, output } =
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
let decl = working_set.get_decl(decl_id);
let call_span = Span::concat(spans);
let starting_error_count = working_set.parse_errors.len();
check_call(working_set, call_span, &decl.signature(), &call);
let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
return garbage_pipeline(working_set, spans);
};
if starting_error_count != working_set.parse_errors.len() || is_help {
return Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
call_span,
output,
)]);
}
(call, call_span)
}
};
// Now that the call is parsed, add the deprecation warning
working_set
.parse_warnings
.push(ParseWarning::DeprecatedWarning {
old_command: "register".into(),
new_suggestion: "use `plugin add` and `plugin use`".into(),
span: call.head,
url: "https://www.nushell.sh/book/plugins.html".into(),
});
// Extracting the required arguments from the call and keeping them together in a tuple
let arguments = call
.positional_nth(0)
.map(|expr| {
let val =
eval_constant(working_set, expr).map_err(|err| err.wrap(working_set, call.head))?;
let filename = val
.coerce_into_string()
.map_err(|err| err.wrap(working_set, call.head))?;
let Some(path) = find_in_dirs(&filename, working_set, &cwd, Some(PLUGIN_DIRS_VAR))
else {
return Err(ParseError::RegisteredFileNotFound(filename, expr.span));
};
if path.exists() && path.is_file() {
Ok((path, expr.span))
} else {
Err(ParseError::RegisteredFileNotFound(filename, expr.span))
}
})
.expect("required positional has being checked");
// Signature is an optional value from the call and will be used to decide if
// the plugin is called to get the signatures or to use the given signature
let signature = call.positional_nth(1).map(|expr| {
let signature = working_set.get_span_contents(expr.span);
serde_json::from_slice::<PluginSignature>(signature).map_err(|e| {
ParseError::LabeledError(
"Signature deserialization error".into(),
format!("unable to deserialize signature: {e}"),
spans[0],
)
})
});
// Shell is another optional value used as base to call shell to plugins
let shell = call.get_flag_expr("shell").map(|expr| {
let shell_expr = working_set.get_span_contents(expr.span);
String::from_utf8(shell_expr.to_vec())
.map_err(|_| ParseError::NonUtf8(expr.span))
.and_then(|name| {
canonicalize_with(&name, cwd)
.map_err(|_| ParseError::RegisteredFileNotFound(name, expr.span))
})
.and_then(|path| {
if path.exists() & path.is_file() {
Ok(path)
} else {
Err(ParseError::RegisteredFileNotFound(
format!("{path:?}"),
expr.span,
))
}
})
});
let shell = match shell {
None => None,
Some(path) => match path {
Ok(path) => Some(path),
Err(err) => {
working_set.error(err);
return Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
call_span,
Type::Any,
)]);
}
},
};
// We need the current environment variables for `python` based plugins
// Or we'll likely have a problem when a plugin is implemented in a virtual Python environment.
let get_envs = || {
let stack = Stack::new().capture();
nu_engine::env::env_to_strings(working_set.permanent_state, &stack)
};
let error = arguments.and_then(|(path, path_span)| {
let path = path.path_buf();
// Create the plugin identity. This validates that the plugin name starts with `nu_plugin_`
let identity = PluginIdentity::new(path, shell).err_span(path_span)?;
let plugin = nu_plugin_engine::add_plugin_to_working_set(working_set, &identity)
.map_err(|err| err.wrap(working_set, call.head))?;
let signatures = signature.map_or_else(
|| {
// It's important that the plugin is restarted if we're going to get signatures
//
// The user would expect that `register` would always run the binary to get new
// signatures, in case it was replaced with an updated binary
plugin.reset().map_err(|err| {
ParseError::LabeledError(
"Failed to restart plugin to get new signatures".into(),
err.to_string(),
spans[0],
)
})?;
let metadata_and_signatures = plugin
.clone()
.get(get_envs)
.and_then(|p| {
let meta = p.get_metadata()?;
let sigs = p.get_signature()?;
Ok((meta, sigs))
})
.map_err(|err| {
log::warn!("Error getting metadata and signatures: {err:?}");
ParseError::LabeledError(
"Error getting metadata and signatures".into(),
err.to_string(),
spans[0],
)
});
match metadata_and_signatures {
Ok((meta, sigs)) => {
// Set the metadata on the plugin
plugin.set_metadata(Some(meta.clone()));
// Add the loaded plugin to the delta
working_set.update_plugin_registry(PluginRegistryItem::new(
&identity,
meta,
sigs.clone(),
));
Ok(sigs)
}
Err(err) => Err(err),
}
},
|sig| sig.map(|sig| vec![sig]),
)?;
for signature in signatures {
// create plugin command declaration (need struct impl Command)
// store declaration in working set
let plugin_decl = PluginDeclaration::new(plugin.clone(), signature);
working_set.add_decl(Box::new(plugin_decl));
}
Ok(())
});
if let Err(err) = error {
working_set.error(err);
}
Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
call_span,
Type::Nothing,
)])
}
#[cfg(feature = "plugin")]
pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
use nu_protocol::{FromValue, PluginRegistryFile};

View file

@ -5275,15 +5275,6 @@ pub fn parse_expression(working_set: &mut StateWorkingSet, spans: &[Span]) -> Ex
}
b"where" => parse_where_expr(working_set, &spans[pos..]),
#[cfg(feature = "plugin")]
b"register" => {
working_set.error(ParseError::BuiltinCommandInPipeline(
"register".into(),
spans[0],
));
parse_call(working_set, &spans[pos..], spans[0])
}
#[cfg(feature = "plugin")]
b"plugin" => {
if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"use" {
// only 'plugin use' is banned
@ -5419,8 +5410,6 @@ pub fn parse_builtin_commands(
b"export" => parse_export_in_block(working_set, lite_command),
b"hide" => parse_hide(working_set, lite_command),
b"where" => parse_where(working_set, lite_command),
#[cfg(feature = "plugin")]
b"register" => parse_register(working_set, lite_command),
// Only "plugin use" is a keyword
#[cfg(feature = "plugin")]
b"plugin"

View file

@ -95,8 +95,8 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
fn get_plugin_config(&self) -> Result<Option<Value>, ShellError> {
// Fetch the configuration for a plugin
//
// The `plugin` must match the registered name of a plugin. For
// `register nu_plugin_example` the plugin config lookup uses `"example"`
// The `plugin` must match the registered name of a plugin. For `plugin add
// nu_plugin_example` the plugin config lookup uses `"example"`
Ok(self
.get_config()?
.plugins

View file

@ -340,7 +340,7 @@ where
/// Build a [`PluginSignature`] from the signature-related methods on [`PluginCommand`].
///
/// This is sent to the engine on `register`.
/// This is sent to the engine on `plugin add`.
///
/// This is not a public API.
#[doc(hidden)]

View file

@ -38,7 +38,7 @@ pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
/// The API for a Nushell plugin
///
/// A plugin defines multiple commands, which are added to the engine when the user calls
/// `register`.
/// `plugin add`.
///
/// The plugin must be able to be safely shared between threads, so that multiple invocations can
/// be run in parallel. If interior mutability is desired, consider synchronization primitives such

View file

@ -104,7 +104,7 @@ pub struct Config {
/// Configuration for plugins.
///
/// Users can provide configuration for a plugin through this entry. The entry name must
/// match the registered plugin name so `register nu_plugin_example` will be able to place
/// match the registered plugin name so `plugin add nu_plugin_example` will be able to place
/// its configuration under a `nu_plugin_example` column.
pub plugins: HashMap<String, Value>,
/// Configuration for plugin garbage collection.

View file

@ -741,8 +741,8 @@ impl EngineState {
/// Fetch the configuration for a plugin
///
/// The `plugin` must match the registered name of a plugin. For `register nu_plugin_example`
/// the plugin name to use will be `"example"`
/// The `plugin` must match the registered name of a plugin. For `plugin add
/// nu_plugin_example` the plugin name to use will be `"example"`
pub fn get_plugin_config(&self, plugin: &str) -> Option<&Value> {
self.config.plugins.get(plugin)
}

View file

@ -26,7 +26,7 @@ pub trait RegisteredPlugin: Send + Sync {
fn stop(&self) -> Result<(), ShellError>;
/// Stop the plugin and reset any state so that we don't make any assumptions about the plugin
/// next time it launches. This is used on `register`.
/// next time it launches. This is used on `plugin add`.
fn reset(&self) -> Result<(), ShellError>;
/// Cast the pointer to an [`Any`] so that its concrete type can be retrieved.

View file

@ -7,10 +7,12 @@ in order to create a binary that can be registered into nushell declaration list
This subcommand demonstrates sending configuration from the nushell `$env.config` to a plugin.
To register from after building `nushell` run:
To make use of the plugin after building `nushell` run:
```nushell
register target/debug/nu_plugin_example
plugin add target/debug/nu_plugin_example
# or then either restart your current nushell session or run:
plugin use target/debug/nu_plugin_example
```
The configuration for the plugin lives in `$env.config.plugins.example`:

View file

@ -12,7 +12,7 @@ A nushell plugin to convert data to nushell tables.
# Usage
1. compile the binary: `cargo build`
2. register plugin(assume it's compiled in ./target/debug/):
2. plugin add plugin(assume it's compiled in ./target/debug/):
```
register ./target/debug/nu_plugin_formats
plugin add ./target/debug/nu_plugin_formats
```

View file

@ -8,6 +8,6 @@ To install:
> cargo install --path .
```
To register (from inside Nushell):
To add the plugin (from inside Nushell):
```
> register <path to installed plugin>
> plugin add <path to installed plugin>

View file

@ -8,6 +8,6 @@ To install:
cargo install --path .
```
To register (from inside Nushell):
To add the plugin (from inside Nushell):
```
register <path to installed plugin>
> plugin add <path to installed plugin>

View file

@ -7,7 +7,7 @@
# decode and encode information that is read and written to stdin and stdout
#
# To register the plugin use:
# register <path-to-py-file>
# plugin add <path-to-py-file>
#
# Be careful with the spans. Miette will crash if a span is outside the
# size of the contents vector. We strongly suggest using the span found in the
@ -258,4 +258,4 @@ if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "--stdio":
plugin()
else:
print("Run me from inside nushell!")
print("Run me from inside nushell!")

View file

@ -8,6 +8,6 @@ To install:
> cargo install --path .
```
To register (from inside Nushell):
To add the plugin (from inside Nushell):
```
> register <path to installed plugin>
> plugin add <path to installed plugin>

View file

@ -24,7 +24,7 @@ RUN echo '/usr/bin/nu' >> /etc/shells \
&& chmod +x /usr/bin/nu \
&& chown -R nushell:nushell /home/nushell/.config/nushell \
&& ls /usr/bin/nu_plugin* \
| xargs -I{} su -c 'register {}' nushell \
| xargs -I{} su -c 'plugin add {}' nushell \
&& rm -rf /tmp/*
USER nushell

View file

@ -23,5 +23,4 @@
| `coverage-local.sh` | x | x | x |
| `install-all.ps1` | ? | x | ? |
| `install-all.sh` | x | x | x |
| `register-plugins.nu` | x | x | x |
| `uninstall-all.sh` | x | x | x |

View file

@ -1,55 +0,0 @@
use std log warning
warning "./scripts/register-plugin.nu will be deprecated, please use the `toolkit plugin register` command instead"
# are we on windows or not?
def windows? [] {
$nu.os-info.name == windows
}
# filter out files that end in .d
def keep-plugin-executables [] {
if (windows?) { where name ends-with '.exe' } else { where name !~ '\.d' }
}
# get list of all plugin files from their installed directory
let plugins = (ls ((which nu).path.0 | path dirname) | where name =~ nu_plugin | keep-plugin-executables)
for plugin in $plugins {
print -n $"registering ($plugin.name), "
nu -c $"register '($plugin.name)'"
print "success!"
}
# print helpful message
print "\nplugins registered, please restart nushell"
# Plugin Location
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_custom_values
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_example
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_gstat
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_inc
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_python
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_query
# https://github.com/fdncred/nu_plugin_from_parquet
# https://github.com/fdncred/nu_plugin_from_regex
# https://github.com/fdncred/nu_plugin_pnet
# https://github.com/JosephTLyons/nu_plugin_periodic_table
# https://github.com/Euphrasiologist/nu_plugin_bio
# https://github.com/realcundo/nu_plugin_dcm
# https://github.com/enerdgumen/nu_plugin_dotenv
# https://github.com/bluk/nu_plugin_from_bencode
# Older plugins
# https://github.com/notryanb/nu_plugin_id3
# https://github.com/notryanb/nu_plugin_weather
# https://github.com/tiffany352/nu-plugins/tree/main/from_nbt
# https://github.com/tiffany352/nu-plugins/tree/main/file_exists
# https://github.com/potan/nu_plugin_wifiscan
# https://github.com/autophagy/nu_plugin_from_dhall
# https://github.com/yanganto/nu_plugin_s3
# https://github.com/lukasreuter/nu_plugin_unity
# https://github.com/filaretov/nu_plugin_path_temp
# https://github.com/cdecompilador/nu_plugin_bg
# https://github.com/aJuvan/nu_plugin_kubectl
# https://github.com/hedonihilist/nu_plugin_df

View file

@ -572,39 +572,6 @@ fn unbalanced_parens2() -> TestResult {
fail_test(r#"("("))"#, "unbalanced ( and )")
}
#[test]
fn register_with_string_literal() -> TestResult {
fail_test(r#"register 'nu-plugin-math'"#, "File not found")
}
#[test]
fn register_with_string_constant() -> TestResult {
let input = "\
const file = 'nu-plugin-math'
register $file
";
// should not fail with `not a constant`
fail_test(input, "File not found")
}
#[test]
fn register_with_string_variable() -> TestResult {
let input = "\
let file = 'nu-plugin-math'
register $file
";
fail_test(input, "Value is not a parse-time constant")
}
#[test]
fn register_with_non_string_constant() -> TestResult {
let input = "\
const file = 6
register $file
";
fail_test(input, "expected string, found int")
}
#[test]
fn plugin_use_with_string_literal() -> TestResult {
fail_test(