Skip to content

Commit 1af7b4b

Browse files
committed
fix(core): use home crate for cross-platform home directory resolution
Replace all direct `std::env::var("HOME")` lookups with the `home` crate, which resolves the home directory correctly on both Unix (`HOME`) and Windows (`USERPROFILE` / `HOMEDRIVE`+`HOMEPATH`). Adds a `home_dir()` helper to `openshell-core::paths` and updates all call sites across openshell-core, openshell-cli, openshell-providers, openshell-server, openshell-driver-vm, and openshell-driver-podman. Closes #2028 Signed-off-by: Jeff MAURY <jmaury@redhat.com>
1 parent 7e0cce4 commit 1af7b4b

13 files changed

Lines changed: 57 additions & 33 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ ratatui = "0.26"
4949
crossterm = "0.28"
5050
terminal-colorsaurus = "1.0"
5151

52+
# Home directory resolution
53+
home = "0.5"
54+
5255
# Error handling
5356
miette = { version = "7", features = ["fancy"] }
5457
thiserror = "2"

crates/openshell-cli/src/run.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,11 @@ fn package_managed_tls_dirs() -> Vec<PathBuf> {
735735

736736
let state_dir = std::env::var_os("XDG_STATE_HOME")
737737
.map(PathBuf::from)
738-
.or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".local/state")));
738+
.or_else(|| {
739+
openshell_core::paths::home_dir()
740+
.ok()
741+
.map(|home| home.join(".local/state"))
742+
});
739743
if let Some(state_dir) = state_dir {
740744
dirs.push(state_dir.join("openshell/tls"));
741745
}
@@ -4351,10 +4355,10 @@ fn read_gcloud_adc() -> Result<(String, String, String)> {
43514355
{
43524356
PathBuf::from(config_dir).join("application_default_credentials.json")
43534357
} else {
4354-
let home = std::env::var("HOME")
4355-
.map_err(|_| miette::miette!("HOME is not set; cannot locate gcloud ADC file"))?;
4356-
PathBuf::from(home)
4357-
.join(".config")
4358+
let home = openshell_core::paths::home_dir().map_err(|_| {
4359+
miette::miette!("could not determine home directory; cannot locate gcloud ADC file")
4360+
})?;
4361+
home.join(".config")
43584362
.join("gcloud")
43594363
.join("application_default_credentials.json")
43604364
};

crates/openshell-cli/src/ssh.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,10 +1454,8 @@ fn openshell_ssh_config_path() -> Result<PathBuf> {
14541454
}
14551455

14561456
fn user_ssh_config_path() -> Result<PathBuf> {
1457-
let home = std::env::var("HOME")
1458-
.into_diagnostic()
1459-
.wrap_err("HOME is not set")?;
1460-
Ok(PathBuf::from(home).join(".ssh").join("config"))
1457+
let home = openshell_core::paths::home_dir()?;
1458+
Ok(home.join(".ssh").join("config"))
14611459
}
14621460

14631461
fn render_include_line(path: &Path) -> String {

crates/openshell-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ tonic = { workspace = true, features = ["channel", "tls-native-roots"] }
1717
tonic-prost = { workspace = true }
1818
tokio = { workspace = true }
1919
thiserror = { workspace = true }
20+
home = { workspace = true }
2021
miette = { workspace = true }
2122
serde = { workspace = true }
2223
serde_json = { workspace = true }

crates/openshell-core/src/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ fn podman_socket_candidates() -> Vec<PathBuf> {
161161
podman_socket_candidates_from_env(
162162
socket,
163163
std::env::var_os("XDG_RUNTIME_DIR").map(PathBuf::from),
164-
std::env::var_os("HOME").map(PathBuf::from),
164+
crate::paths::home_dir().ok(),
165165
)
166166
}
167167

@@ -216,8 +216,8 @@ fn docker_socket_candidates() -> Vec<PathBuf> {
216216

217217
candidates.push(PathBuf::from("/var/run/docker.sock"));
218218

219-
if let Some(home) = std::env::var_os("HOME") {
220-
candidates.push(PathBuf::from(home).join(".docker/run/docker.sock"));
219+
if let Ok(home) = crate::paths::home_dir() {
220+
candidates.push(home.join(".docker/run/docker.sock"));
221221
}
222222

223223
if let Some(runtime_dir) = std::env::var_os("XDG_RUNTIME_DIR") {

crates/openshell-core/src/paths.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,24 @@
1313
use miette::{IntoDiagnostic, Result, WrapErr};
1414
use std::path::{Path, PathBuf};
1515

16+
/// Resolve the user's home directory in a cross-platform way.
17+
///
18+
/// Uses the `home` crate, which reads `HOME` on Unix and `USERPROFILE`
19+
/// (falling back to `HOMEDRIVE`+`HOMEPATH`) on Windows.
20+
pub fn home_dir() -> Result<PathBuf> {
21+
home::home_dir().ok_or_else(|| miette::miette!("could not determine home directory"))
22+
}
23+
1624
/// Resolve the XDG config base directory.
1725
///
1826
/// Returns `$XDG_CONFIG_HOME` if set, otherwise `$HOME/.config`.
1927
pub fn xdg_config_dir() -> Result<PathBuf> {
2028
if let Ok(path) = std::env::var("XDG_CONFIG_HOME") {
2129
return Ok(PathBuf::from(path));
2230
}
23-
let home = std::env::var("HOME")
24-
.into_diagnostic()
25-
.wrap_err("HOME is not set")?;
26-
Ok(PathBuf::from(home).join(".config"))
31+
Ok(home_dir()
32+
.wrap_err("cannot resolve XDG config directory")?
33+
.join(".config"))
2734
}
2835

2936
/// The top-level `OpenShell` config directory: `$XDG_CONFIG_HOME/openshell/`.
@@ -38,10 +45,10 @@ pub fn xdg_state_dir() -> Result<PathBuf> {
3845
if let Ok(path) = std::env::var("XDG_STATE_HOME") {
3946
return Ok(PathBuf::from(path));
4047
}
41-
let home = std::env::var("HOME")
42-
.into_diagnostic()
43-
.wrap_err("HOME is not set")?;
44-
Ok(PathBuf::from(home).join(".local").join("state"))
48+
Ok(home_dir()
49+
.wrap_err("cannot resolve XDG state directory")?
50+
.join(".local")
51+
.join("state"))
4552
}
4653

4754
/// The top-level `OpenShell` state directory: `$XDG_STATE_HOME/openshell/`.
@@ -56,10 +63,10 @@ pub fn xdg_data_dir() -> Result<PathBuf> {
5663
if let Ok(path) = std::env::var("XDG_DATA_HOME") {
5764
return Ok(PathBuf::from(path));
5865
}
59-
let home = std::env::var("HOME")
60-
.into_diagnostic()
61-
.wrap_err("HOME is not set")?;
62-
Ok(PathBuf::from(home).join(".local").join("share"))
66+
Ok(home_dir()
67+
.wrap_err("cannot resolve XDG data directory")?
68+
.join(".local")
69+
.join("share"))
6370
}
6471

6572
/// Create a directory (and parents) with owner-only permissions (`0o700`) on
@@ -159,6 +166,12 @@ pub fn normalize_path(path: &str) -> String {
159166
mod tests {
160167
use super::*;
161168

169+
#[test]
170+
fn home_dir_returns_ok() {
171+
let dir = home_dir().unwrap();
172+
assert!(dir.is_absolute(), "expected absolute path, got: {dir:?}");
173+
}
174+
162175
#[test]
163176
fn xdg_config_dir_respects_env() {
164177
// This test checks the logic — actual env var mutation is unsafe so

crates/openshell-driver-podman/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ rustix = { workspace = true }
3333
tracing = { workspace = true }
3434
tracing-subscriber = { workspace = true }
3535
thiserror = { workspace = true }
36+
home = { workspace = true }
3637
miette = { workspace = true }
3738

3839
[dev-dependencies]

crates/openshell-driver-podman/src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ impl PodmanComputeConfig {
227227
pub fn default_socket_path() -> PathBuf {
228228
#[cfg(target_os = "macos")]
229229
{
230-
let home = std::env::var("HOME").expect("HOME must be set on macOS");
231-
PathBuf::from(home).join(".local/share/containers/podman/machine/podman.sock")
230+
let home = home::home_dir().expect("home directory must be resolvable on macOS");
231+
home.join(".local/share/containers/podman/machine/podman.sock")
232232
}
233233
#[cfg(target_os = "linux")]
234234
{

crates/openshell-driver-vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ nix = { workspace = true }
3333
clap = { workspace = true }
3434
tracing = { workspace = true }
3535
tracing-subscriber = { workspace = true }
36+
home = { workspace = true }
3637
miette = { workspace = true }
3738
url = { workspace = true }
3839
serde = { workspace = true }

0 commit comments

Comments
 (0)