Skip to content

Commit d6587e6

Browse files
committed
Add error for volta install 10
1 parent a6a275f commit d6587e6

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

Cargo.lock

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/volta-core/src/error/kind.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ pub enum ErrorKind {
163163
version: String,
164164
},
165165

166+
/// Thrown when a user does e.g. `volta install 12` instead of
167+
/// `volta install node@12`.
168+
InvalidInvocationOfBareVersion {
169+
action: String,
170+
version: String,
171+
},
172+
166173
/// Thrown when a tool name is invalid per npm's rules.
167174
InvalidToolName {
168175
name: String,
@@ -770,6 +777,32 @@ To {action} the packages '{name}' and '{version}', please {action} them in separ
770777
write!(f, "{}\n\n{}", error, wrapped_cta)
771778
}
772779

780+
ErrorKind::InvalidInvocationOfBareVersion {
781+
action,
782+
version,
783+
} => {
784+
let error = format!(
785+
"`volta {action} {version}` is not supported.",
786+
action = action,
787+
version = version
788+
);
789+
790+
let call_to_action = format!(
791+
"To {action} node version '{version}', please run `volta {action} {formatted}`. \
792+
To {action} the package '{version}', please use an explicit version such as '{version}@lts'.",
793+
action=action,
794+
version=version,
795+
formatted=tool_version("node", version)
796+
);
797+
798+
let wrapped_cta = match text_width() {
799+
Some(width) => fill(&call_to_action, width),
800+
None => call_to_action,
801+
};
802+
803+
write!(f, "{}\n\n{}", error, wrapped_cta)
804+
}
805+
773806
ErrorKind::InvalidToolName { name, errors } => {
774807
let indentation = " ";
775808
let wrapped = match text_width() {
@@ -1383,6 +1416,7 @@ impl ErrorKind {
13831416
ErrorKind::InvalidHookCommand { .. } => ExitCode::ExecutableNotFound,
13841417
ErrorKind::InvalidHookOutput { .. } => ExitCode::ExecutionFailure,
13851418
ErrorKind::InvalidInvocation { .. } => ExitCode::InvalidArguments,
1419+
ErrorKind::InvalidInvocationOfBareVersion { .. } => ExitCode::InvalidArguments,
13861420
ErrorKind::InvalidToolName { .. } => ExitCode::InvalidArguments,
13871421
ErrorKind::LockAcquireError => ExitCode::FileSystemError,
13881422
ErrorKind::NoBundledNpm { .. } => ExitCode::ConfigurationError,

crates/volta-core/src/tool/serial.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,44 @@ impl Spec {
8383
Ok(tools)
8484
}
8585

86-
/// Check the args for the bad pattern of `volta install <tool> <number>`.
86+
/// Check the args for the bad patterns of
87+
/// - `volta install <number>`
88+
/// - `volta install <tool> <number>`
8789
fn check_args<T>(args: &[T], action: &str) -> Fallible<()>
8890
where
8991
T: AsRef<str>,
9092
{
91-
let mut args = args.iter();
93+
let mut args_iter = args.iter();
9294

93-
// The case we are concerned with is where we have `<tool> <number>`.
95+
// The case we are concerned with here is where we have `<number>`.
96+
// That is, exactly one argument, which is a valid version specifier.
97+
//
98+
// - `volta install node@12` is allowed.
99+
// - `volta install 12` is an error.
100+
// - `volta install lts` is an error.
101+
if let (Some(maybe_version), None) = (args_iter.next(), args_iter.next()) {
102+
if is_version_like(maybe_version.as_ref()) {
103+
return Err(ErrorKind::InvalidInvocationOfBareVersion {
104+
action: action.to_string(),
105+
version: maybe_version.as_ref().to_string(),
106+
}
107+
.into());
108+
}
109+
}
110+
111+
args_iter = args.iter();
112+
113+
// The case we are concerned with here is where we have `<tool> <number>`.
94114
// This is only interesting if there are exactly two args. Then we care
95115
// whether the two items are a bare name (with no `@version`), followed
96116
// by a valid version specifier (ignoring custom tags). That is:
97117
//
98118
// - `volta install node@lts latest` is allowed.
99119
// - `volta install node latest` is an error.
100120
// - `volta install node latest yarn` is allowed.
101-
if let (Some(name), Some(maybe_version), None) = (args.next(), args.next(), args.next()) {
121+
if let (Some(name), Some(maybe_version), None) =
122+
(args_iter.next(), args_iter.next(), args_iter.next())
123+
{
102124
if !HAS_VERSION.is_match(name.as_ref()) && is_version_like(maybe_version.as_ref()) {
103125
return Err(ErrorKind::InvalidInvocation {
104126
action: action.to_string(),
@@ -358,6 +380,23 @@ mod tests {
358380

359381
static PIN: &str = "pin";
360382

383+
#[test]
384+
fn special_cases_just_number() {
385+
let version = "1.2.3";
386+
let args: Vec<String> = vec![version.into()];
387+
388+
let err = Spec::from_strings(&args, PIN).unwrap_err();
389+
390+
assert_eq!(
391+
err.kind(),
392+
&ErrorKind::InvalidInvocationOfBareVersion {
393+
action: PIN.into(),
394+
version: version.into()
395+
},
396+
"`volta <action> number` results in the correct error"
397+
);
398+
}
399+
361400
#[test]
362401
fn special_cases_tool_space_number() {
363402
let name = "potato";
@@ -393,6 +432,15 @@ mod tests {
393432
"when there is only one arg"
394433
);
395434

435+
let one_with_explicit_verson = ["10@latest".to_owned()];
436+
assert_eq!(
437+
Spec::from_strings(&one_with_explicit_verson, PIN)
438+
.expect("is ok")
439+
.len(),
440+
only_one.len(),
441+
"when the sole arg is version-like but has an explicit version"
442+
);
443+
396444
let two_but_unmistakable = ["12".to_owned(), "node".to_owned()];
397445
assert_eq!(
398446
Spec::from_strings(&two_but_unmistakable, PIN)

0 commit comments

Comments
 (0)