Fix single binary mapping by manipulating the PATH variable

main
trivernis 1 year ago
parent 91f5570032
commit 6ede8a3df6
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -24,6 +24,7 @@ clap = { version = "4.1.1", features = ["derive"] }
crossterm = "0.25.0" crossterm = "0.25.0"
dialoguer = "0.10.3" dialoguer = "0.10.3"
dirs = "4.0.0" dirs = "4.0.0"
envmnt = "0.10.4"
futures = "0.3.25" futures = "0.3.25"
futures-util = "0.3.25" futures-util = "0.3.25"
indicatif = "0.17.3" indicatif = "0.17.3"

@ -2,6 +2,10 @@ use lazy_static::lazy_static;
use std::path::PathBuf; use std::path::PathBuf;
pub const NODE_DIST_URL: &str = "https://nodejs.org/dist"; pub const NODE_DIST_URL: &str = "https://nodejs.org/dist";
#[cfg(not(windows))]
pub const SEARCH_PATH_SEPARATOR: &str = ":";
#[cfg(windows)]
pub const SEARCH_PATH_SEPARATOR: &str = ";";
lazy_static! { lazy_static! {
pub static ref CFG_DIR: PathBuf = dirs::config_dir() pub static ref CFG_DIR: PathBuf = dirs::config_dir()

@ -1,4 +1,4 @@
use std::process; use std::{env, process};
use args::Args; use args::Args;
use clap::Parser; use clap::Parser;
@ -36,7 +36,7 @@ async fn main() -> Result<()> {
return Ok(()); return Ok(());
} }
let mut nenv = get_nenv(args.use_version.as_ref()).await?; let mut nenv = get_nenv(args.use_version.clone()).await?;
match args.command { match args.command {
args::Command::Install(v) => nenv.install(v.version).await, args::Command::Install(v) => nenv.install(v.version).await,
@ -63,7 +63,7 @@ fn print_version() {
println!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); println!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
} }
async fn get_nenv(version_override: Option<&NodeVersion>) -> Result<Nenv> { async fn get_nenv(version_override: Option<NodeVersion>) -> Result<Nenv> {
Nenv::init(version_override).await Nenv::init(version_override).await
} }

@ -1,4 +1,5 @@
use std::{ use std::{
env,
ffi::OsString, ffi::OsString,
path::PathBuf, path::PathBuf,
process::{ExitStatus, Stdio}, process::{ExitStatus, Stdio},
@ -8,6 +9,7 @@ use crate::error::CommandNotFoundError;
use miette::{Context, IntoDiagnostic, Result}; use miette::{Context, IntoDiagnostic, Result};
use tokio::process::Command; use tokio::process::Command;
#[derive(Debug)]
pub struct MappedCommand { pub struct MappedCommand {
name: String, name: String,
path: PathBuf, path: PathBuf,
@ -19,10 +21,11 @@ impl MappedCommand {
Self { name, path, args } Self { name, path, args }
} }
#[tracing::instrument(skip_all, level = "debug")] #[tracing::instrument(level = "debug")]
pub async fn run(mut self) -> Result<ExitStatus> { pub async fn run(mut self) -> Result<ExitStatus> {
self.adjust_path()?; self.adjust_path()?;
let exit_status = Command::new(self.path) let exit_status = Command::new(self.path)
.envs(env::vars_os())
.args(self.args) .args(self.args)
.stdin(Stdio::inherit()) .stdin(Stdio::inherit())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())

@ -40,7 +40,7 @@ impl NodeApp {
.context("Creating executable wrapper script") .context("Creating executable wrapper script")
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(windows))]
async fn write_wrapper_script(&self, path: &Path) -> Result<(), io::Error> { async fn write_wrapper_script(&self, path: &Path) -> Result<(), io::Error> {
fs::write(path, format!("#!/bin/sh\nnenv exec {} \"$@\"", self.name)).await?; fs::write(path, format!("#!/bin/sh\nnenv exec {} \"$@\"", self.name)).await?;
let src_metadata = self.path.metadata()?; let src_metadata = self.path.metadata()?;
@ -49,7 +49,7 @@ impl NodeApp {
Ok(()) Ok(())
} }
#[cfg(target_os = "windows")] #[cfg(windows)]
async fn write_wrapper_script(&self, path: &Path) -> Result<(), io::Error> { async fn write_wrapper_script(&self, path: &Path) -> Result<(), io::Error> {
fs::write( fs::write(
path.with_extension("bat"), path.with_extension("bat"),
@ -117,12 +117,12 @@ async fn get_applications(path: &Path) -> Result<Vec<NodeApp>> {
Ok(files) Ok(files)
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(windows))]
fn exclude_path(_path: &Path) -> bool { fn exclude_path(_path: &Path) -> bool {
false false
} }
#[cfg(target_os = "windows")] #[cfg(windows)]
fn exclude_path(path: &Path) -> bool { fn exclude_path(path: &Path) -> bool {
let Some(extension) = path.extension() else { let Some(extension) = path.extension() else {
return true; return true;

@ -1,8 +1,12 @@
use std::{ffi::OsString, process::ExitStatus}; use std::{env, ffi::OsString, process::ExitStatus};
use envmnt::ListOptions;
use tokio::fs; use tokio::fs;
use crate::{consts::BIN_DIR, repository::node_path::NodePath}; use crate::{
consts::{BIN_DIR, SEARCH_PATH_SEPARATOR},
repository::node_path::NodePath,
};
use self::{ use self::{
mapped_command::MappedCommand, mapped_command::MappedCommand,
@ -26,6 +30,7 @@ impl Mapper {
/// Executes a mapped command with the given node environment /// Executes a mapped command with the given node environment
#[tracing::instrument(level = "debug", skip(self))] #[tracing::instrument(level = "debug", skip(self))]
pub async fn exec(&self, command: String, args: Vec<OsString>) -> Result<ExitStatus> { pub async fn exec(&self, command: String, args: Vec<OsString>) -> Result<ExitStatus> {
self.set_env();
let executable = self.node_path.bin().join(&command); let executable = self.node_path.bin().join(&command);
let exit_status = MappedCommand::new(command, executable, args).run().await?; let exit_status = MappedCommand::new(command, executable, args).run().await?;
self.remap_additive().await?; self.remap_additive().await?;
@ -60,4 +65,18 @@ impl Mapper {
) )
.await .await
} }
fn set_env(&self) {
env::set_var(
"NODE_PATH",
self.node_path.node_modules().to_string_lossy().to_string(),
);
let list_options = ListOptions {
separator: Some(SEARCH_PATH_SEPARATOR.to_string()),
ignore_empty: true,
};
let mut path_env = envmnt::get_list_with_options("PATH", &list_options).unwrap_or_default();
path_env.insert(0, self.node_path.bin().to_string_lossy().to_string());
envmnt::set_list_with_options("PATH", &path_env, &list_options);
}
} }

@ -22,13 +22,13 @@ pub struct Nenv {
impl Nenv { impl Nenv {
#[tracing::instrument(level = "debug")] #[tracing::instrument(level = "debug")]
pub async fn init(version_override: Option<&NodeVersion>) -> Result<Self> { pub async fn init(version_override: Option<NodeVersion>) -> Result<Self> {
let config = ConfigAccess::load().await?; let config = ConfigAccess::load().await?;
let repo = Repository::init(config.clone()).await?; let repo = Repository::init(config.clone()).await?;
let default_version = { config.get().await.node.default_version.to_owned() }; let default_version = { config.get().await.node.default_version.to_owned() };
let active_version = if let Some(version) = version_override { let active_version = if let Some(version) = version_override {
version.to_owned() version
} else { } else {
Self::get_active_version().await.unwrap_or(default_version) Self::get_active_version().await.unwrap_or(default_version)
}; };
@ -265,7 +265,8 @@ impl Nenv {
for (bin, cfg) in &self.config.get().await.bins { for (bin, cfg) in &self.config.get().await.bins {
let path = self let path = self
.repo .repo
.get_version_path(&cfg.node_version)? .get_version_path(&cfg.node_version)
.await?
.ok_or_else(|| VersionError::not_installed(&cfg.node_version))?; .ok_or_else(|| VersionError::not_installed(&cfg.node_version))?;
binaries_with_path.push((bin.to_owned(), path)); binaries_with_path.push((bin.to_owned(), path));
} }

@ -12,7 +12,15 @@ impl NodePath {
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub fn bin(&self) -> PathBuf { pub fn bin(&self) -> PathBuf {
self.base.join("bin") self.base.join("bin").canonicalize().unwrap()
}
pub fn lib(&self) -> PathBuf {
self.base.join("lib").canonicalize().unwrap()
}
pub fn node_modules(&self) -> PathBuf {
self.lib().join("node_modules")
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]

Loading…
Cancel
Save