From 6aeedd8602ef0b58dee49641b905571fd863dc94 Mon Sep 17 00:00:00 2001 From: daimond113 Date: Sat, 26 Apr 2025 20:45:27 +0200 Subject: [PATCH] feat: add PESDE_HOME variable Adds the PESDE_HOME env variable to allow changing where pesde will store data like engines, bins, etc. to support e.g. small OS disks & large data disks. --- CHANGELOG.md | 1 + src/cli/commands/self_install.rs | 11 ++++++----- src/cli/config.rs | 8 +++++--- src/cli/install.rs | 5 ++++- src/cli/mod.rs | 34 +++++++++++++++++++++----------- src/cli/version.rs | 27 ++++++++++++++++--------- src/main.rs | 30 ++++++---------------------- 7 files changed, 62 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8691102..18df81e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Add dev only installs in #32 by @Stefanuk12 +- Add `PESDE_HOME` variable (defaults to $HOME/.pesde) to override pesde's directory's location by @daimond113 ### Fixed - Download engines in install step rather than lazily by @daimond113 diff --git a/src/cli/commands/self_install.rs b/src/cli/commands/self_install.rs index 20b8163..24b5fa7 100644 --- a/src/cli/commands/self_install.rs +++ b/src/cli/commands/self_install.rs @@ -1,7 +1,6 @@ use crate::cli::{ style::{ADDED_STYLE, CLI_STYLE}, version::replace_pesde_bin_exe, - HOME_DIR, }; use anyhow::Context as _; use clap::Args; @@ -25,16 +24,18 @@ impl SelfInstallCommand { use anyhow::Context as _; use windows_registry::CURRENT_USER; - let bin_dir = crate::cli::bin_dir().await?; + let bin_dir = crate::cli::bin_dir()?; let env = CURRENT_USER .create("Environment") .context("failed to open Environment key")?; let path = env.get_string("Path").context("failed to get Path value")?; - let bin_dir = bin_dir.to_string_lossy(); + let bin_dir = bin_dir + .to_str() + .context("bin directory path contains invalid characters")?; - let exists = path.split(';').any(|part| *part == bin_dir); + let exists = path.split(';').any(|part| part == bin_dir); if !exists { let new_path = format!("{path};{bin_dir}"); @@ -43,7 +44,7 @@ impl SelfInstallCommand { println!( "\nin order to allow proper functionality {} was added to PATH.\n\n{}", - style(format!("`~/{HOME_DIR}/bin`")).green(), + style(format!("`{bin_dir}`")).green(), WARN_STYLE.apply_to("please restart your shell for this to take effect") ); } diff --git a/src/cli/config.rs b/src/cli/config.rs index d1da51e..f08de75 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -1,9 +1,11 @@ -use crate::cli::{auth::Tokens, home_dir}; +use crate::cli::auth::Tokens; use anyhow::Context as _; use fs_err::tokio as fs; use serde::{Deserialize, Serialize}; use tracing::instrument; +use super::config_path; + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] pub struct CliConfig { @@ -33,7 +35,7 @@ impl Default for CliConfig { #[instrument(level = "trace")] pub async fn read_config() -> anyhow::Result { - let config_string = match fs::read_to_string(home_dir()?.join("config.toml")).await { + let config_string = match fs::read_to_string(config_path()?).await { Ok(config_string) => config_string, Err(e) if e.kind() == std::io::ErrorKind::NotFound => { return Ok(CliConfig::default()); @@ -49,7 +51,7 @@ pub async fn read_config() -> anyhow::Result { #[instrument(level = "trace")] pub async fn write_config(config: &CliConfig) -> anyhow::Result<()> { let config_string = toml::to_string(config).context("failed to serialize config")?; - fs::write(home_dir()?.join("config.toml"), config_string) + fs::write(config_path()?, config_string) .await .context("failed to write config file")?; diff --git a/src/cli/install.rs b/src/cli/install.rs index 0dd2c29..a5bda1a 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -301,8 +301,11 @@ pub async fn install( root_progress.set_message("download"); root_progress.set_style(reporters::root_progress_style_with_progress()); + let bin_dir = bin_dir()?; + fs::create_dir_all(&bin_dir).await.context("failed to create bin directory")?; + let hooks = InstallHooks { - bin_folder: bin_dir().await?, + bin_folder: bin_dir, }; #[allow(unused_variables)] diff --git a/src/cli/mod.rs b/src/cli/mod.rs index bcc4a3d..c3fe53d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -3,7 +3,6 @@ use crate::cli::{ style::{ERROR_STYLE, INFO_STYLE, WARN_STYLE}, }; use anyhow::Context as _; -use fs_err::tokio as fs; use futures::StreamExt as _; use pesde::{ errors::ManifestReadError, @@ -39,20 +38,31 @@ pub mod style; #[cfg(feature = "version-management")] pub mod version; -pub const HOME_DIR: &str = concat!(".", env!("CARGO_PKG_NAME")); +pub const PESDE_DIR: &str = concat!(".", env!("CARGO_PKG_NAME")); -pub fn home_dir() -> anyhow::Result { - Ok(dirs::home_dir() - .context("failed to get home directory")? - .join(HOME_DIR)) +fn base_dir() -> anyhow::Result { + Ok(match std::env::var("PESDE_HOME") { + Ok(base) => PathBuf::from(base), + _ => dirs::home_dir() + .context("failed to get home directory")? + .join(PESDE_DIR), + }) } -pub async fn bin_dir() -> anyhow::Result { - let bin_dir = home_dir()?.join("bin"); - fs::create_dir_all(&bin_dir) - .await - .context("failed to create bin folder")?; - Ok(bin_dir) +pub fn bin_dir() -> anyhow::Result { + Ok(base_dir()?.join("bin")) +} + +pub fn engines_dir() -> anyhow::Result { + Ok(base_dir()?.join("engines")) +} + +pub fn config_path() -> anyhow::Result { + Ok(base_dir()?.join("config.toml")) +} + +pub fn data_dir() -> anyhow::Result { + Ok(base_dir()?.join("data")) } pub fn resolve_overrides( diff --git a/src/cli/version.rs b/src/cli/version.rs index 38431e9..4cb89a6 100644 --- a/src/cli/version.rs +++ b/src/cli/version.rs @@ -3,7 +3,6 @@ use crate::{ bin_dir, config::{read_config, write_config, CliConfig}, files::make_executable, - home_dir, style::{ADDED_STYLE, CLI_STYLE, REMOVED_STYLE, URL_STYLE}, }, util::no_build_metadata, @@ -32,6 +31,8 @@ use std::{ }; use tracing::instrument; +use super::engines_dir; + pub fn current_version() -> Version { Version::parse(env!("CARGO_PKG_VERSION")).unwrap() } @@ -115,12 +116,10 @@ pub async fn check_for_updates(reqwest: &reqwest::Client) -> anyhow::Result<()> Ok(()) } -const ENGINES_DIR: &str = "engines"; - #[instrument(level = "trace")] async fn get_installed_versions(engine: EngineKind) -> anyhow::Result> { let source = engine.source(); - let path = home_dir()?.join(ENGINES_DIR).join(source.directory()); + let path = engines_dir()?.join(source.directory()); let mut installed_versions = BTreeSet::new(); let mut read_dir = match fs::read_dir(&path).await { @@ -152,7 +151,7 @@ pub async fn get_or_download_engine( reporter: Arc, ) -> anyhow::Result<(PathBuf, Version)> { let source = engine.source(); - let path = home_dir()?.join(ENGINES_DIR).join(source.directory()); + let path = engines_dir()?.join(source.directory()); let installed_versions = get_installed_versions(engine).await?; @@ -229,8 +228,7 @@ pub async fn get_or_download_engine( #[instrument(level = "trace")] pub async fn replace_pesde_bin_exe(with: &Path) -> anyhow::Result<()> { - let bin_exe_path = bin_dir() - .await? + let bin_exe_path = bin_dir()? .join(EngineKind::Pesde.to_string()) .with_extension(std::env::consts::EXE_EXTENSION); @@ -255,6 +253,12 @@ pub async fn replace_pesde_bin_exe(with: &Path) -> anyhow::Result<()> { } } + if let Some(parent) = bin_exe_path.parent() { + fs::create_dir_all(parent) + .await + .context("failed to create bin directory")?; + } + fs::copy(with, &bin_exe_path) .await .context("failed to copy executable to bin folder")?; @@ -264,14 +268,19 @@ pub async fn replace_pesde_bin_exe(with: &Path) -> anyhow::Result<()> { #[instrument(level = "trace")] pub async fn make_linker_if_needed(engine: EngineKind) -> anyhow::Result<()> { - let bin_dir = bin_dir().await?; - let linker = bin_dir + let linker = bin_dir()? .join(engine.to_string()) .with_extension(std::env::consts::EXE_EXTENSION); if fs::metadata(&linker).await.is_err() { let exe = current_exe().context("failed to get current exe path")?; + if let Some(parent) = linker.parent() { + fs::create_dir_all(parent) + .await + .context("failed to create linker directory")?; + } + #[cfg(windows)] let result = fs::symlink_file(exe, linker); #[cfg(not(windows))] diff --git a/src/main.rs b/src/main.rs index 7872031..8110c6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ #[cfg(feature = "version-management")] use crate::cli::version::{check_for_updates, current_version, get_or_download_engine}; -use crate::cli::{auth::get_tokens, display_err, home_dir, style::ERROR_STYLE, HOME_DIR}; +use crate::cli::{auth::get_tokens, display_err, style::ERROR_STYLE, PESDE_DIR}; use anyhow::Context as _; use clap::{builder::styling::AnsiColor, Parser}; +use cli::data_dir; use fs_err::tokio as fs; use indicatif::MultiProgress; use pesde::{ @@ -207,15 +208,6 @@ async fn run() -> anyhow::Result<()> { if bin_folder.file_name().is_some_and(|parent| parent != "bin") { break 'scripts; } - - // we're not in {path}/.pesde/bin/{exe} - if bin_folder - .parent() - .and_then(|home_folder| home_folder.file_name()) - .is_some_and(|home_folder| home_folder != HOME_DIR) - { - break 'scripts; - } } let linker_file_name = format!("{exe_name}.bin.luau"); @@ -287,27 +279,17 @@ async fn run() -> anyhow::Result<()> { std::process::exit(status.code().unwrap_or(1i32)); }; - let home_dir = home_dir()?; - let data_dir = home_dir.join("data"); - fs::create_dir_all(&data_dir) + let cas_dir = get_linkable_dir(&project_root_dir) .await - .expect("failed to create data directory"); - - let cas_dir = get_linkable_dir(&project_root_dir).await.join(HOME_DIR); - - let cas_dir = if cas_dir == home_dir { - &data_dir - } else { - &cas_dir - } - .join("cas"); + .join(PESDE_DIR) + .join("cas"); tracing::debug!("using cas dir in {}", cas_dir.display()); let project = Project::new( project_root_dir, project_workspace_dir, - data_dir, + data_dir()?, cas_dir, AuthConfig::new().with_tokens(get_tokens().await?.0), );