Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ secrecy = { version = "0.10.3", features = ["serde"] }
bimap = "0.6.3"
self_update = { version = "0.42", default-features = false, features = ["rustls"] }
semver = "1.0"
shellexpand = "3.1"
inquire = "0.9.2"
tracing-indicatif = "0.3.14"

Expand Down
70 changes: 60 additions & 10 deletions src/app_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,52 @@ use serde::{Deserialize, Serialize};

use crate::onboarding::{self, OnboardingError};

/// A `PathBuf` that automatically expands `~` to the user's home directory
/// during deserialization. This ensures that any path loaded from configuration
/// is already resolved.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(transparent)]
pub struct ExpandedPathBuf(PathBuf);

impl<'de> Deserialize<'de> for ExpandedPathBuf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw = String::deserialize(deserializer)?;
let expanded = shellexpand::tilde(&raw);
Ok(Self(PathBuf::from(expanded.into_owned())))
}
}

impl ExpandedPathBuf {
/// Creates a new `ExpandedPathBuf` from any path, without expansion.
/// Use this for programmatically-constructed paths that are already absolute.
pub fn new(path: PathBuf) -> Self {
Self(path)
}
}

impl std::ops::Deref for ExpandedPathBuf {
type Target = Path;

fn deref(&self) -> &Path {
&self.0
}
}

impl AsRef<Path> for ExpandedPathBuf {
fn as_ref(&self) -> &Path {
&self.0
}
}

impl std::fmt::Display for ExpandedPathBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.display().fmt(f)
}
}

fn mesa_runtime_dir() -> Option<PathBuf> {
let runtime_dir = dirs::runtime_dir();
if let Some(path) = runtime_dir {
Expand All @@ -30,15 +76,17 @@ fn mesa_runtime_dir() -> Option<PathBuf> {
None
}

fn default_pid_file() -> PathBuf {
mesa_runtime_dir().map_or_else(
fn default_pid_file() -> ExpandedPathBuf {
ExpandedPathBuf::new(mesa_runtime_dir().map_or_else(
|| PathBuf::from("/var/run/git-fs.pid"),
|rd| rd.join("git-fs.pid"),
)
))
}

fn default_mount_point() -> PathBuf {
mesa_runtime_dir().map_or_else(|| PathBuf::from("/tmp/git-fs/mnt"), |rd| rd.join("mnt"))
fn default_mount_point() -> ExpandedPathBuf {
ExpandedPathBuf::new(
mesa_runtime_dir().map_or_else(|| PathBuf::from("/tmp/git-fs/mnt"), |rd| rd.join("mnt")),
)
}

fn current_uid() -> u32 {
Expand All @@ -57,15 +105,17 @@ pub struct CacheConfig {
pub max_size: Option<ByteSize>,

/// The path to the cache directory.
pub path: PathBuf,
pub path: ExpandedPathBuf,
}

impl Default for CacheConfig {
fn default() -> Self {
Self {
max_size: None,
path: mesa_runtime_dir()
.map_or_else(|| PathBuf::from("/tmp/git-fs/cache"), |rd| rd.join("cache")),
path: ExpandedPathBuf::new(
mesa_runtime_dir()
.map_or_else(|| PathBuf::from("/tmp/git-fs/cache"), |rd| rd.join("cache")),
),
}
}
}
Expand Down Expand Up @@ -184,7 +234,7 @@ impl<'a> From<&'a OrganizationConfig> for DangerousOrganizationConfig<'a> {
pub struct DaemonConfig {
/// The path to the PID file for the daemon. Uses /var/run/git-fs.pid if not specified.
#[serde(default = "default_pid_file")]
pub pid_file: PathBuf,
pub pid_file: ExpandedPathBuf,
}

impl Default for DaemonConfig {
Expand Down Expand Up @@ -214,7 +264,7 @@ pub struct Config {

/// The mount point for the filesystem.
#[serde(default = "default_mount_point")]
pub mount_point: PathBuf,
pub mount_point: ExpandedPathBuf,

/// The user to mount the filesystem as. If not specified, runs as the current user.
#[serde(default = "current_uid")]
Expand Down
2 changes: 1 addition & 1 deletion src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ mod managed_fuse {
impl ManagedFuse {
pub fn new(config: &app_config::Config) -> Self {
Self {
mount_point: config.mount_point.clone(),
mount_point: config.mount_point.to_path_buf(),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fn main() {
}

let daemonize = daemonize::Daemonize::new()
.pid_file(config.daemon.pid_file.clone())
.pid_file(&config.daemon.pid_file)
.chown_pid_file(true)
.user(config.uid)
.group(config.gid);
Expand Down
9 changes: 6 additions & 3 deletions src/onboarding.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! Interactive onboarding wizard for first-time configuration.

use std::{io::IsTerminal as _, path::PathBuf};
use std::io::IsTerminal as _;
use std::path::PathBuf;

use inquire::{Confirm, Password, Text, validator::Validation};
use secrecy::SecretString;

use crate::app_config::{Config, OrganizationConfig};
use crate::app_config::{Config, ExpandedPathBuf, OrganizationConfig};

const WELCOME_MESSAGE: &str = "
\x1b[32m@@@@\x1b[0m Welcome to \x1b[1mgit-fs\x1b[0m! Let's get you started!
Expand Down Expand Up @@ -49,7 +50,9 @@ pub fn run_wizard() -> Result<Config, OnboardingError> {
let mount_point_str = Text::new("Where should git-fs mount the filesystem?")
.with_default(&defaults.mount_point.display().to_string())
.prompt()?;
let mount_point = PathBuf::from(mount_point_str);
let mount_point = ExpandedPathBuf::new(PathBuf::from(
shellexpand::tilde(&mount_point_str).into_owned(),
));

let mut org_keys: Vec<(String, SecretString)> = Vec::new();
loop {
Expand Down