Skip to content

Commit ca046de

Browse files
committed
Check volta shims for reachability during tool install
Fix #1268
1 parent 7167400 commit ca046de

File tree

10 files changed

+73
-1
lines changed

10 files changed

+73
-1
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/volta-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ chain-map = "0.1.0"
5353
indexmap = "1.9.1"
5454
retry = "1.3.1"
5555
fs2 = "0.4.3"
56+
which = "4.2.5"
5657

5758
[target.'cfg(windows)'.dependencies]
5859
winreg = "0.10.1"

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,11 @@ pub enum ErrorKind {
429429
name: String,
430430
},
431431

432+
/// Thrown when Volta is unable to find the shimmed command.
433+
ShimResolveError {
434+
name: String,
435+
},
436+
432437
/// Thrown when serializnig a bin config to JSON fails
433438
StringifyBinConfigError,
434439

@@ -1260,6 +1265,13 @@ at {}
12601265
{}"#,
12611266
name, PERMISSIONS_CTA
12621267
),
1268+
ErrorKind::ShimResolveError { name } => write!(
1269+
f,
1270+
r#"Could not find shim for "{}"
1271+
1272+
{}"#,
1273+
name, PERMISSIONS_CTA,
1274+
),
12631275
ErrorKind::StringifyBinConfigError => write!(
12641276
f,
12651277
"Could not serialize executable configuration.
@@ -1512,6 +1524,7 @@ impl ErrorKind {
15121524
ErrorKind::SetToolExecutable { .. } => ExitCode::FileSystemError,
15131525
ErrorKind::ShimCreateError { .. } => ExitCode::FileSystemError,
15141526
ErrorKind::ShimRemoveError { .. } => ExitCode::FileSystemError,
1527+
ErrorKind::ShimResolveError { .. } => ExitCode::FileSystemError,
15151528
ErrorKind::StringifyBinConfigError => ExitCode::UnknownError,
15161529
ErrorKind::StringifyPackageConfigError => ExitCode::UnknownError,
15171530
ErrorKind::StringifyPlatformError => ExitCode::UnknownError,

crates/volta-core/src/shim.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::error::{Context, ErrorKind, Fallible, VoltaError};
99
use crate::fs::{read_dir_eager, symlink_file};
1010
use crate::layout::{volta_home, volta_install};
1111
use crate::sync::VoltaLock;
12-
use log::debug;
12+
use log::{debug, warn};
1313

1414
pub fn regenerate_shims_for_dir(dir: &Path) -> Fallible<()> {
1515
// Acquire a lock on the Volta directory, if possible, to prevent concurrent changes
@@ -89,6 +89,23 @@ pub fn create(shim_name: &str) -> Fallible<ShimResult> {
8989
}
9090
}
9191

92+
pub fn check_reachable(shim_name: &str) -> Fallible<()> {
93+
let shim = volta_home()?.shim_file(shim_name);
94+
let resolved = which::which(shim_name).map_err(|e| {
95+
VoltaError::from_source(
96+
e,
97+
ErrorKind::ShimResolveError {
98+
name: shim_name.to_string(),
99+
},
100+
)
101+
})?;
102+
if resolved != shim {
103+
warn!("{} is shadowed by another binary of the same name at {}. Please ensure that {} is at the front of your PATH.", shim_name, resolved.display(), shim.display());
104+
}
105+
106+
Ok(())
107+
}
108+
92109
pub fn delete(shim_name: &str) -> Fallible<ShimResult> {
93110
let shim = volta_home()?.shim_file(shim_name);
94111

crates/volta-core/src/tool/node/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::{
77
use crate::error::{ErrorKind, Fallible};
88
use crate::inventory::node_available;
99
use crate::session::Session;
10+
use crate::shim;
1011
use crate::style::{note_prefix, tool_version};
1112
use crate::sync::VoltaLock;
1213
use cfg_if::cfg_if;
@@ -207,6 +208,8 @@ impl Tool for Node {
207208
info_installed(node_version); // includes node and npm version
208209
}
209210

211+
shim::check_reachable("node")?;
212+
210213
if let Ok(Some(project)) = session.project_platform() {
211214
info_project_version(tool_version("node", &project.node));
212215
}

crates/volta-core/src/tool/npm/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use super::{
88
use crate::error::{Context, ErrorKind, Fallible};
99
use crate::inventory::npm_available;
1010
use crate::session::Session;
11+
use crate::shim;
1112
use crate::style::{success_prefix, tool_version};
1213
use crate::sync::VoltaLock;
1314
use log::info;
@@ -64,6 +65,7 @@ impl Tool for Npm {
6465
.set_active_npm(Some(self.version.clone()))?;
6566

6667
info_installed(self);
68+
shim::check_reachable("npm")?;
6769

6870
if let Ok(Some(project)) = session.project_platform() {
6971
if let Some(npm) = &project.npm {

crates/volta-core/src/tool/package/configure.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub(super) fn write_config_and_shims(
3737
// Generate the shims and bin configs for each bin provided by the package
3838
for bin_name in &manifest.bin {
3939
shim::create(bin_name)?;
40+
shim::check_reachable(bin_name)?;
4041

4142
BinConfig {
4243
name: bin_name.clone(),

crates/volta-core/src/tool/yarn/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::{
77
use crate::error::{ErrorKind, Fallible};
88
use crate::inventory::yarn_available;
99
use crate::session::Session;
10+
use crate::shim;
1011
use crate::style::tool_version;
1112
use crate::sync::VoltaLock;
1213
use semver::Version;
@@ -63,6 +64,7 @@ impl Tool for Yarn {
6364
.set_active_yarn(Some(self.version.clone()))?;
6465

6566
info_installed(self);
67+
shim::check_reachable("yarn")?;
6668

6769
if let Ok(Some(project)) = session.project_platform() {
6870
if let Some(yarn) = &project.yarn {

tests/acceptance/support/sandbox.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,20 @@ impl SandboxBuilder {
460460
self.add_exec_dir_to_path()
461461
}
462462

463+
/// Add an arbitrary file to the test project within the sandbox,
464+
/// give it executable permissions,
465+
/// and add its directory to the *front* of PATH, shadowing any volta binaries.
466+
pub fn prepend_exec_dir_to_path(mut self) -> Self {
467+
if self.has_exec_path {
468+
panic!("need to call prepend_exec_dir_to_path before anything else");
469+
}
470+
471+
let exec_path = self.root().join("exec");
472+
self.path_dirs.insert(0, exec_path);
473+
self.has_exec_path = true;
474+
self
475+
}
476+
463477
/// Set a package config file for the sandbox (chainable)
464478
pub fn package_config(mut self, name: &str, contents: &str) -> Self {
465479
let package_cfg_file = package_config_file(name);

tests/acceptance/volta_install.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,21 @@ fn install_yarn_3_without_node_errors() {
333333
)
334334
);
335335
}
336+
337+
#[test]
338+
fn install_node_with_shadowed_binary() {
339+
let s = sandbox()
340+
.node_available_versions(NODE_VERSION_INFO)
341+
.distro_mocks::<NodeFixture>(&NODE_VERSION_FIXTURES)
342+
.env("VOLTA_LOGLEVEL", "info")
343+
.prepend_exec_dir_to_path()
344+
.executable_file("node", "echo hello world")
345+
.build();
346+
347+
assert_that!(
348+
s.volta("install node"),
349+
execs()
350+
.with_status(ExitCode::Success as i32)
351+
.with_stderr_contains("[..]is shadowed by another binary of the same name at [..]")
352+
);
353+
}

0 commit comments

Comments
 (0)