Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No more symlinks on Windows #1552

Closed
wants to merge 14 commits into from
692 changes: 346 additions & 346 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ smoke-tests = []
name = "volta-shim"
path = "src/volta-shim.rs"

[[bin]]
name = "volta-runner"
path = "src/volta-runner.rs"

[[bin]]
name = "volta-migrate"
path = "src/volta-migrate.rs"
Expand Down
2 changes: 2 additions & 0 deletions crates/volta-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ dunce = "1.0.4"
ci_info = "0.14.14"
headers = "0.4"
attohttpc = { version = "0.28", default-features = false, features = ["json", "compress", "tls-rustls-native-roots"] }
hyperx = "1.4.0"
chain-map = "0.1.0"
indexmap = "2.2.6"
retry = "2"
fs2 = "0.4.3"

[target.'cfg(windows)'.dependencies]
winreg = "0.52.0"
junction = "1.0.0"
15 changes: 1 addition & 14 deletions crates/volta-core/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,27 +118,14 @@ pub fn create_staging_dir() -> Fallible<TempDir> {
})
}

/// Create a file symlink. The `dst` path will be a symbolic link pointing to the `src` path.
pub fn symlink_file<S, D>(src: S, dest: D) -> io::Result<()>
where
S: AsRef<Path>,
D: AsRef<Path>,
{
#[cfg(windows)]
return std::os::windows::fs::symlink_file(src, dest);

#[cfg(unix)]
return std::os::unix::fs::symlink(src, dest);
}

/// Create a directory symlink. The `dst` path will be a symbolic link pointing to the `src` path
pub fn symlink_dir<S, D>(src: S, dest: D) -> io::Result<()>
where
S: AsRef<Path>,
D: AsRef<Path>,
{
#[cfg(windows)]
return std::os::windows::fs::symlink_dir(src, dest);
return junction::create(src, dest);

#[cfg(unix)]
return std::os::unix::fs::symlink(src, dest);
Expand Down
2 changes: 2 additions & 0 deletions crates/volta-core/src/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ fn get_executor(

/// Determine the name of the command to run by inspecting the first argument to the active process
fn get_tool_name(args: &mut ArgsOs) -> Fallible<OsString> {
args.next();

args.next()
.and_then(|arg0| Path::new(&arg0).file_name().map(tool_name_from_file_name))
.ok_or_else(|| ErrorKind::CouldNotDetermineTool.into())
Expand Down
15 changes: 12 additions & 3 deletions crates/volta-core/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::io;
use std::path::Path;

use crate::error::{Context, ErrorKind, Fallible, VoltaError};
use crate::fs::{read_dir_eager, symlink_file};
use crate::fs::read_dir_eager;
use crate::layout::{volta_home, volta_install};
use crate::sync::VoltaLock;
use log::debug;
Expand Down Expand Up @@ -71,10 +71,19 @@ pub fn create(shim_name: &str) -> Fallible<ShimResult> {
let executable = volta_install()?.shim_executable();
let shim = volta_home()?.shim_file(shim_name);

let shim_result;
#[cfg(windows)]
windows::create_git_bash_script(shim_name)?;
{
windows::create_git_bash_script(shim_name)?;
shim_result = std::fs::copy(executable, shim);
}

#[cfg(unix)]
{
shim_result = std::os::unix::fs::symlink(executable, shim);
}

match symlink_file(executable, shim) {
match shim_result {
Ok(_) => Ok(ShimResult::Created),
Err(err) => {
if err.kind() == io::ErrorKind::AlreadyExists {
Expand Down
35 changes: 35 additions & 0 deletions src/volta-runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
mod common;

use common::{ensure_layout, Error, IntoResult};
use volta_core::error::{report_error, ExitCode};
use volta_core::log::{LogContext, LogVerbosity, Logger};
use volta_core::run::execute_shim;
use volta_core::session::{ActivityKind, Session};
use volta_core::signal::setup_signal_handler;

pub fn main() {
Logger::init(LogContext::Shim, LogVerbosity::Default)
.expect("Only a single Logger should be initialized");
setup_signal_handler();

let mut session = Session::init();
session.add_event_start(ActivityKind::Tool);

let result = ensure_layout().and_then(|()| execute_shim(&mut session).into_result());
match result {
Ok(()) => {
session.add_event_end(ActivityKind::Tool, ExitCode::Success);
session.exit(ExitCode::Success);
}
Err(Error::Tool(code)) => {
session.add_event_tool_end(ActivityKind::Tool, code);
session.exit_tool(code);
}
Err(Error::Volta(err)) => {
report_error(env!("CARGO_PKG_VERSION"), &err);
session.add_event_error(ActivityKind::Tool, &err);
session.add_event_end(ActivityKind::Tool, err.exit_code());
session.exit(ExitCode::ExecutionFailure);
}
}
}
39 changes: 8 additions & 31 deletions src/volta-shim.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
mod common;
use std::process::{Command, ExitCode};

use common::{ensure_layout, Error, IntoResult};
use volta_core::error::{report_error, ExitCode};
use volta_core::log::{LogContext, LogVerbosity, Logger};
use volta_core::run::execute_shim;
use volta_core::session::{ActivityKind, Session};
use volta_core::signal::setup_signal_handler;

pub fn main() {
Logger::init(LogContext::Shim, LogVerbosity::Default)
.expect("Only a single Logger should be initialized");
setup_signal_handler();

let mut session = Session::init();
session.add_event_start(ActivityKind::Tool);

let result = ensure_layout().and_then(|()| execute_shim(&mut session).into_result());
pub fn main() -> ExitCode {
let result = Command::new("volta-runner").args(std::env::args()).status();
charlespierce marked this conversation as resolved.
Show resolved Hide resolved
match result {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still a little unsure about making this the behavior for all platforms. It's relatively minor, but it is another indirection and since Rust std::process::Command doesn't use exec, it may cause unexpected issues on top of any small performance hit.

One that comes to mind is signals (e.g. pressing Ctrl-C in the terminal while a process is running). We don't handle them perfectly right now, but I know a lot of work went into making sure that Ctrl-C would cancel the underlying Node process (as a user would expect), not only the wrapper process.

Copy link
Author

@rcsilva83 rcsilva83 May 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Charles!

Thank you for the feedback.

So you prefer this change is changed only for Windows, right? We can make this condition, no problem.

About the volta run and .cmd idea, let me know if you prefer that approach and I'll try to implement it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if we move forward here, we should limit the scope to only for Windows, so that we have the smallest possible blast radius.

For the volta run idea, I want to do a quick prototype to see if it's feasible, then we can look at which approach makes the most sense.

And thank you again for persisting with this change, despite our busy schedules making the lead times really long! It will be a big improvement in Windows behavior when we don't need the "Developer Mode" warning 💥

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! So I'll wait for you before making both changes.

Ok(()) => {
session.add_event_end(ActivityKind::Tool, ExitCode::Success);
session.exit(ExitCode::Success);
}
Err(Error::Tool(code)) => {
session.add_event_tool_end(ActivityKind::Tool, code);
session.exit_tool(code);
}
Err(Error::Volta(err)) => {
report_error(env!("CARGO_PKG_VERSION"), &err);
session.add_event_error(ActivityKind::Tool, &err);
session.add_event_end(ActivityKind::Tool, err.exit_code());
session.exit(ExitCode::ExecutionFailure);
}
Ok(exit_status) => match exit_status.code() {
None => ExitCode::FAILURE,
Some(code) => ExitCode::from(code as u8),
},
Err(_) => ExitCode::FAILURE,
}
}
6 changes: 4 additions & 2 deletions tests/acceptance/support/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use headers::{Expires, Header};
use mockito::{self, mock, Matcher};
use node_semver::Version;
use test_support::{self, ok_or_panic, paths, paths::PathExt, process::ProcessBuilder};
use volta_core::fs::{set_executable, symlink_file};
use volta_core::fs::set_executable;
use volta_core::tool::{Node, Pnpm, Yarn, NODE_DISTRO_ARCH, NODE_DISTRO_EXTENSION, NODE_DISTRO_OS};

// version cache for node and yarn
Expand Down Expand Up @@ -135,7 +135,9 @@ impl ShimBuilder {
}

fn build(&self) {
ok_or_panic! { symlink_file(shim_exe(), shim_file(&self.name)) };
ok_or_panic! {
std::os::unix::fs::symlink(shim_exe(), shim_file(&self.name))
}
}
}

Expand Down
Loading