Browse Source

Support XDG convention in MacOS too (#1143)

* feat(macos): Support XDG conventions on macOS too

* store base strategy in FnmConfig

* add changeset

---------

Co-authored-by: Utkarsh Gupta <utkarshgupta137@gmail.com>
remotes/origin/fix-insecure-world-writable-dir
Gal Schlezinger 9 months ago committed by GitHub
parent
commit
f76a0011f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/cold-drinks-camp.md
  2. 70
      Cargo.lock
  3. 2
      Cargo.toml
  4. 3
      src/commands/env.rs
  5. 24
      src/config.rs
  6. 80
      src/directories.rs

5
.changeset/cold-drinks-camp.md

@ -0,0 +1,5 @@
---
"fnm": minor
---
use XDG conventions in MacOS directories by default

70
Cargo.lock generated

@ -512,27 +512,6 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.4" version = "0.2.4"
@ -639,6 +618,17 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "etcetera"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
dependencies = [
"cfg-if",
"home",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.1.0" version = "2.1.0"
@ -677,11 +667,11 @@ dependencies = [
"clap_complete", "clap_complete",
"colored", "colored",
"csv", "csv",
"dirs",
"duct", "duct",
"embed-resource", "embed-resource",
"encoding_rs_io", "encoding_rs_io",
"env_logger", "env_logger",
"etcetera",
"http", "http",
"indicatif", "indicatif",
"indoc", "indoc",
@ -821,6 +811,15 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.1.0" version = "1.1.0"
@ -1039,16 +1038,6 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.5.0",
"libc",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.14" version = "0.4.14"
@ -1252,12 +1241,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.1.5" version = "1.1.5"
@ -1433,17 +1416,6 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "redox_users"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.4" version = "1.10.4"

2
Cargo.toml

@ -16,7 +16,7 @@ chrono = { version = "0.4.38", features = ["serde", "now"], default-features = f
tar = "0.4.40" tar = "0.4.40"
xz2 = "0.1.7" xz2 = "0.1.7"
node-semver = "2.1.0" node-semver = "2.1.0"
dirs = "5.0.1" etcetera = "0.8.0"
colored = "2.1.0" colored = "2.1.0"
zip = "2.1.0" zip = "2.1.0"
tempfile = "3.10.1" tempfile = "3.10.1"

3
src/commands/env.rs

@ -1,6 +1,5 @@
use super::command::Command; use super::command::Command;
use crate::config::FnmConfig; use crate::config::FnmConfig;
use crate::directories;
use crate::fs::symlink_dir; use crate::fs::symlink_dir;
use crate::outln; use crate::outln;
use crate::path_ext::PathExt; use crate::path_ext::PathExt;
@ -36,7 +35,7 @@ fn generate_symlink_path() -> String {
} }
fn make_symlink(config: &FnmConfig) -> Result<std::path::PathBuf, Error> { fn make_symlink(config: &FnmConfig) -> Result<std::path::PathBuf, Error> {
let base_dir = directories::multishell_storage().ensure_exists_silently(); let base_dir = config.multishell_storage().ensure_exists_silently();
let mut temp_dir = base_dir.join(generate_symlink_path()); let mut temp_dir = base_dir.join(generate_symlink_path());
while temp_dir.exists() { while temp_dir.exists() {

24
src/config.rs

@ -1,8 +1,8 @@
use crate::arch::Arch; use crate::arch::Arch;
use crate::directories::Directories;
use crate::log_level::LogLevel; use crate::log_level::LogLevel;
use crate::path_ext::PathExt; use crate::path_ext::PathExt;
use crate::version_file_strategy::VersionFileStrategy; use crate::version_file_strategy::VersionFileStrategy;
use dirs::{data_dir, home_dir};
use url::Url; use url::Url;
#[derive(clap::Parser, Debug)] #[derive(clap::Parser, Debug)]
@ -87,6 +87,9 @@ pub struct FnmConfig {
verbatim_doc_comment verbatim_doc_comment
)] )]
resolve_engines: bool, resolve_engines: bool,
#[clap(skip)]
directories: Directories,
} }
impl Default for FnmConfig { impl Default for FnmConfig {
@ -100,6 +103,7 @@ impl Default for FnmConfig {
version_file_strategy: VersionFileStrategy::default(), version_file_strategy: VersionFileStrategy::default(),
corepack_enabled: false, corepack_enabled: false,
resolve_engines: false, resolve_engines: false,
directories: Directories::default(),
} }
} }
} }
@ -134,19 +138,7 @@ impl FnmConfig {
return dir; return dir;
} }
let legacy = home_dir() self.directories.default_base_dir()
.map(|dir| dir.join(".fnm"))
.filter(|dir| dir.exists());
let modern = data_dir().map(|dir| dir.join("fnm"));
if let Some(dir) = legacy {
return dir;
}
modern
.expect("Can't get data directory")
.ensure_exists_silently()
} }
pub fn installations_dir(&self) -> std::path::PathBuf { pub fn installations_dir(&self) -> std::path::PathBuf {
@ -165,6 +157,10 @@ impl FnmConfig {
.ensure_exists_silently() .ensure_exists_silently()
} }
pub fn multishell_storage(&self) -> std::path::PathBuf {
self.directories.multishell_storage()
}
#[cfg(test)] #[cfg(test)]
pub fn with_base_dir(mut self, base_dir: Option<std::path::PathBuf>) -> Self { pub fn with_base_dir(mut self, base_dir: Option<std::path::PathBuf>) -> Self {
self.base_dir = base_dir; self.base_dir = base_dir;

80
src/directories.rs

@ -1,26 +1,78 @@
use etcetera::BaseStrategy;
use std::path::PathBuf; use std::path::PathBuf;
use crate::path_ext::PathExt;
fn xdg_dir(env: &str) -> Option<PathBuf> { fn xdg_dir(env: &str) -> Option<PathBuf> {
let env_var = std::env::var(env).ok()?; if cfg!(windows) {
Some(PathBuf::from(env_var)) let env_var = std::env::var(env).ok()?;
Some(PathBuf::from(env_var))
} else {
// On non-Windows platforms, `etcetera` already handles XDG variables
None
}
} }
fn state_dir() -> Option<PathBuf> { fn runtime_dir(basedirs: &impl BaseStrategy) -> Option<PathBuf> {
xdg_dir("XDG_STATE_HOME").or_else(dirs::state_dir) xdg_dir("XDG_RUNTIME_DIR").or_else(|| basedirs.runtime_dir())
} }
fn cache_dir() -> Option<PathBuf> { fn state_dir(basedirs: &impl BaseStrategy) -> Option<PathBuf> {
xdg_dir("XDG_CACHE_HOME").or_else(dirs::cache_dir) xdg_dir("XDG_STATE_HOME").or_else(|| basedirs.state_dir())
} }
fn runtime_dir() -> Option<PathBuf> { fn cache_dir(basedirs: &impl BaseStrategy) -> PathBuf {
xdg_dir("XDG_RUNTIME_DIR").or_else(dirs::runtime_dir) xdg_dir("XDG_CACHE_HOME").unwrap_or_else(|| basedirs.cache_dir())
} }
pub fn multishell_storage() -> PathBuf { /// A helper struct for directories in fnm that uses XDG Base Directory Specification
runtime_dir() /// if applicable for the platform.
.or_else(state_dir) #[derive(Debug)]
.or_else(cache_dir) pub struct Directories(
.unwrap_or_else(std::env::temp_dir) #[cfg(windows)] etcetera::base_strategy::Windows,
.join("fnm_multishells") #[cfg(not(windows))] etcetera::base_strategy::Xdg,
);
impl Default for Directories {
fn default() -> Self {
Self(etcetera::choose_base_strategy().expect("choosing base strategy"))
}
}
impl Directories {
pub fn strategy(&self) -> &impl BaseStrategy {
&self.0
}
pub fn default_base_dir(&self) -> PathBuf {
let strategy = self.strategy();
let modern = strategy.data_dir().join("fnm");
if modern.exists() {
return modern;
}
let legacy = strategy.home_dir().join(".fnm");
if legacy.exists() {
return legacy;
}
#[cfg(target_os = "macos")]
{
let basedirs = etcetera::base_strategy::Apple::new().expect("Can't get home directory");
let legacy = basedirs.data_dir().join("fnm");
if legacy.exists() {
return legacy;
}
}
modern.ensure_exists_silently()
}
pub fn multishell_storage(&self) -> PathBuf {
let basedirs = self.strategy();
let dir = runtime_dir(basedirs)
.or_else(|| state_dir(basedirs))
.unwrap_or_else(|| cache_dir(basedirs));
dir.join("fnm_multishells")
}
} }

Loading…
Cancel
Save