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.
This commit is contained in:
daimond113 2025-04-26 20:45:27 +02:00
parent 398763a171
commit 6aeedd8602
No known key found for this signature in database
GPG key ID: 640DC95EC1190354
7 changed files with 62 additions and 54 deletions

View file

@ -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

View file

@ -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")
);
}

View file

@ -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<CliConfig> {
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<CliConfig> {
#[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")?;

View file

@ -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)]

View file

@ -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<PathBuf> {
Ok(dirs::home_dir()
fn base_dir() -> anyhow::Result<PathBuf> {
Ok(match std::env::var("PESDE_HOME") {
Ok(base) => PathBuf::from(base),
_ => dirs::home_dir()
.context("failed to get home directory")?
.join(HOME_DIR))
.join(PESDE_DIR),
})
}
pub async fn bin_dir() -> anyhow::Result<PathBuf> {
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<PathBuf> {
Ok(base_dir()?.join("bin"))
}
pub fn engines_dir() -> anyhow::Result<PathBuf> {
Ok(base_dir()?.join("engines"))
}
pub fn config_path() -> anyhow::Result<PathBuf> {
Ok(base_dir()?.join("config.toml"))
}
pub fn data_dir() -> anyhow::Result<PathBuf> {
Ok(base_dir()?.join("data"))
}
pub fn resolve_overrides(

View file

@ -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<BTreeSet<Version>> {
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<impl DownloadsReporter>,
) -> 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))]

View file

@ -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,19 +279,9 @@ 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(PESDE_DIR)
.join("cas");
tracing::debug!("using cas dir in {}", cas_dir.display());
@ -307,7 +289,7 @@ async fn run() -> anyhow::Result<()> {
let project = Project::new(
project_root_dir,
project_workspace_dir,
data_dir,
data_dir()?,
cas_dir,
AuthConfig::new().with_tokens(get_tokens().await?.0),
);