Skip to content

Commit ff9f0dc

Browse files
authored
Merge pull request #1292 from untitaker/sanity-check-shims
Check volta shims for reachability during tool install
2 parents e40704c + 10a283b commit ff9f0dc

File tree

9 files changed

+102
-7
lines changed

9 files changed

+102
-7
lines changed

Cargo.lock

Lines changed: 14 additions & 1 deletion
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
@@ -52,6 +52,7 @@ chain-map = "0.1.0"
5252
indexmap = "2.2.6"
5353
retry = "2"
5454
fs2 = "0.4.3"
55+
which = "4.2.5"
5556

5657
[target.'cfg(windows)'.dependencies]
5758
winreg = "0.52.0"

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ use std::env;
22
use std::fmt::{self, Display};
33

44
use crate::error::{ErrorKind, Fallible};
5+
use crate::layout::volta_home;
56
use crate::session::Session;
67
use crate::style::{note_prefix, success_prefix, tool_version};
78
use crate::sync::VoltaLock;
89
use crate::version::VersionSpec;
910
use crate::VOLTA_FEATURE_PNPM;
11+
use cfg_if::cfg_if;
1012
use log::{debug, info};
1113

1214
pub mod node;
@@ -226,3 +228,36 @@ fn registry_fetch_error(
226228
let from_url = from_url.as_ref().to_string();
227229
|| ErrorKind::RegistryFetchError { tool, from_url }
228230
}
231+
232+
cfg_if!(
233+
if #[cfg(windows)] {
234+
const PATH_VAR_NAME: &str = "Path";
235+
} else {
236+
const PATH_VAR_NAME: &str = "PATH";
237+
}
238+
);
239+
240+
pub fn check_shim_reachable(shim_name: &str) {
241+
let home = match volta_home() {
242+
Ok(home) => home,
243+
Err(_) => return,
244+
};
245+
246+
let shim = home.shim_file(shim_name);
247+
let resolved = match which::which(shim_name) {
248+
Ok(resolved) => resolved,
249+
Err(_) => {
250+
info!(
251+
"{} cannot find command {}. Please ensure that {} is available on your {}.",
252+
note_prefix(),
253+
shim_name,
254+
home.shim_dir().display(),
255+
PATH_VAR_NAME,
256+
);
257+
return;
258+
}
259+
};
260+
if resolved != shim {
261+
info!("{} {} is shadowed by another binary of the same name at {}. To ensure your commands work as expected, please move {} to the start of your {}.", note_prefix(), shim_name, resolved.display(), home.shim_dir().display(), PATH_VAR_NAME);
262+
}
263+
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::fmt::{self, Display};
22

33
use super::{
4-
check_fetched, debug_already_fetched, info_fetched, info_installed, info_pinned,
5-
info_project_version, FetchStatus, Tool,
4+
check_fetched, check_shim_reachable, debug_already_fetched, info_fetched, info_installed,
5+
info_pinned, info_project_version, FetchStatus, Tool,
66
};
77
use crate::error::{ErrorKind, Fallible};
88
use crate::inventory::node_available;
@@ -243,6 +243,8 @@ impl Tool for Node {
243243
info_installed(node_version); // includes node and npm version
244244
}
245245

246+
check_shim_reachable("node");
247+
246248
if let Ok(Some(project)) = session.project_platform() {
247249
info_project_version(tool_version("node", &project.node));
248250
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use std::fmt::{self, Display};
22

33
use super::node::load_default_npm_version;
44
use super::{
5-
check_fetched, debug_already_fetched, info_fetched, info_installed, info_pinned,
6-
info_project_version, FetchStatus, Tool,
5+
check_fetched, check_shim_reachable, debug_already_fetched, info_fetched, info_installed,
6+
info_pinned, info_project_version, FetchStatus, Tool,
77
};
88
use crate::error::{Context, ErrorKind, Fallible};
99
use crate::inventory::npm_available;
@@ -64,6 +64,7 @@ impl Tool for Npm {
6464
.set_active_npm(Some(self.version.clone()))?;
6565

6666
info_installed(self);
67+
check_shim_reachable("npm");
6768

6869
if let Ok(Some(project)) = session.project_platform() {
6970
if let Some(npm) = &project.npm {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::error::{ErrorKind, Fallible};
66
use crate::layout::volta_home;
77
use crate::platform::{Image, PlatformSpec};
88
use crate::shim;
9+
use crate::tool::check_shim_reachable;
910

1011
/// Read the manifest for the package being installed
1112
pub(super) fn parse_manifest(
@@ -38,6 +39,7 @@ pub(super) fn write_config_and_shims(
3839
// Generate the shims and bin configs for each bin provided by the package
3940
for bin_name in &manifest.bin {
4041
shim::create(bin_name)?;
42+
check_shim_reachable(bin_name);
4143

4244
BinConfig {
4345
name: bin_name.clone(),

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::fmt::{self, Display};
22

33
use super::{
4-
check_fetched, debug_already_fetched, info_fetched, info_installed, info_pinned,
5-
info_project_version, FetchStatus, Tool,
4+
check_fetched, check_shim_reachable, debug_already_fetched, info_fetched, info_installed,
5+
info_pinned, info_project_version, FetchStatus, Tool,
66
};
77
use crate::error::{ErrorKind, Fallible};
88
use crate::inventory::yarn_available;
@@ -63,6 +63,7 @@ impl Tool for Yarn {
6363
.set_active_yarn(Some(self.version.clone()))?;
6464

6565
info_installed(self);
66+
check_shim_reachable("yarn");
6667

6768
if let Ok(Some(project)) = session.project_platform() {
6869
if let Some(yarn) = &project.yarn {

tests/acceptance/support/sandbox.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,23 @@ impl SandboxBuilder {
500500
self.add_exec_dir_to_path()
501501
}
502502

503+
/// Prepend executable directory to the beginning of the PATH (chainable)
504+
///
505+
/// This is useful to test binaries shadowing volta shims.
506+
///
507+
/// Cannot be used in combination with `add_exec_dir_to_path`, and will panic if called twice.
508+
/// No particular reason except it's likely a programming error.
509+
pub fn prepend_exec_dir_to_path(mut self) -> Self {
510+
if self.has_exec_path {
511+
panic!("need to call prepend_exec_dir_to_path before anything else");
512+
}
513+
514+
let exec_path = self.root().join("exec");
515+
self.path_dirs.insert(0, exec_path);
516+
self.has_exec_path = true;
517+
self
518+
}
519+
503520
/// Set a package config file for the sandbox (chainable)
504521
pub fn package_config(mut self, name: &str, contents: &str) -> Self {
505522
let package_cfg_file = package_config_file(name);

tests/acceptance/volta_install.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,26 @@ fn install_yarn_3_without_node_errors() {
383383
)
384384
);
385385
}
386+
387+
#[test]
388+
fn install_node_with_shadowed_binary() {
389+
#[cfg(windows)]
390+
const SCRIPT_FILENAME: &str = "node.bat";
391+
#[cfg(not(windows))]
392+
const SCRIPT_FILENAME: &str = "node";
393+
394+
let s = sandbox()
395+
.node_available_versions(NODE_VERSION_INFO)
396+
.distro_mocks::<NodeFixture>(&NODE_VERSION_FIXTURES)
397+
.env("VOLTA_LOGLEVEL", "info")
398+
.prepend_exec_dir_to_path()
399+
.executable_file(SCRIPT_FILENAME, "echo hello world")
400+
.build();
401+
402+
assert_that!(
403+
s.volta("install node"),
404+
execs()
405+
.with_status(ExitCode::Success as i32)
406+
.with_stdout_contains("[..]is shadowed by another binary of the same name at [..]")
407+
);
408+
}

0 commit comments

Comments
 (0)