diff --git a/src/action/common/configure_nix.rs b/src/action/common/configure_nix.rs index fc35b4e..b8a571d 100644 --- a/src/action/common/configure_nix.rs +++ b/src/action/common/configure_nix.rs @@ -6,6 +6,7 @@ use crate::{ common::{ConfigureShellProfile, PlaceNixConfiguration}, Action, ActionDescription, ActionError, ActionTag, StatefulAction, }, + planner::ShellProfileLocations, settings::{CommonSettings, SCRATCH_DIR}, }; @@ -23,18 +24,24 @@ pub struct ConfigureNix { impl ConfigureNix { #[tracing::instrument(level = "debug", skip_all)] - pub async fn plan(settings: &CommonSettings) -> Result, ActionError> { + pub async fn plan( + shell_profile_locations: ShellProfileLocations, + settings: &CommonSettings, + ) -> Result, ActionError> { let setup_default_profile = SetupDefaultProfile::plan(PathBuf::from(SCRATCH_DIR)) .await .map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?; let configure_shell_profile = if settings.modify_profile { Some( - ConfigureShellProfile::plan(settings.ssl_cert_file.clone()) - .await - .map_err(|e| { - ActionError::Child(ConfigureShellProfile::action_tag(), Box::new(e)) - })?, + ConfigureShellProfile::plan( + shell_profile_locations, + settings.ssl_cert_file.clone(), + ) + .await + .map_err(|e| { + ActionError::Child(ConfigureShellProfile::action_tag(), Box::new(e)) + })?, ) } else { None diff --git a/src/action/common/configure_shell_profile.rs b/src/action/common/configure_shell_profile.rs index 1b93770..96688ac 100644 --- a/src/action/common/configure_shell_profile.rs +++ b/src/action/common/configure_shell_profile.rs @@ -1,43 +1,12 @@ use crate::action::base::{create_or_insert_into_file, CreateDirectory, CreateOrInsertIntoFile}; use crate::action::{Action, ActionDescription, ActionError, ActionTag, StatefulAction}; +use crate::planner::ShellProfileLocations; use nix::unistd::User; use std::path::{Path, PathBuf}; use tokio::task::JoinSet; use tracing::{span, Instrument, Span}; -// Fish has different syntax than zsh/bash, treat it separate -const PROFILE_FISH_VENDOR_CONFD_SUFFIX: &str = "vendor_conf.d/nix.fish"; -/** - Each of these are common values of $__fish_vendor_confdir, -under which Fish will look for a file named -[`PROFILE_FISH_CONFD_SUFFIX`]. - -More info: https://fishshell.com/docs/3.3/index.html#configuration-files -*/ -const PROFILE_FISH_VENDOR_CONFD_PREFIXES: &[&str] = &["/usr/share/fish/", "/usr/local/share/fish/"]; - -const PROFILE_FISH_CONFD_SUFFIX: &str = "conf.d/nix.fish"; -/** - Each of these are common values of $__fish_sysconf_dir, -under which Fish will look for a file named -[`PROFILE_FISH_CONFD_PREFIXES`]. -*/ -const PROFILE_FISH_CONFD_PREFIXES: &[&str] = &[ - "/etc/fish", // standard - "/usr/local/etc/fish", // their installer .pkg for macOS - "/opt/homebrew/etc/fish", // homebrew - "/opt/local/etc/fish", // macports -]; - -const PROFILE_TARGETS: &[&str] = &[ - "/etc/bashrc", - "/etc/profile.d/nix.sh", - "/etc/bash.bashrc", - // https://zsh.sourceforge.io/Intro/intro_3.html - "/etc/zshrc", - "/etc/zsh/zshrc", -]; const PROFILE_NIX_FILE_SHELL: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"; const PROFILE_NIX_FILE_FISH: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish"; @@ -46,13 +15,17 @@ Configure any detected shell profiles to include Nix support */ #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct ConfigureShellProfile { + locations: ShellProfileLocations, create_directories: Vec>, create_or_insert_into_files: Vec>, } impl ConfigureShellProfile { #[tracing::instrument(level = "debug", skip_all)] - pub async fn plan(ssl_cert_file: Option) -> Result, ActionError> { + pub async fn plan( + locations: ShellProfileLocations, + ssl_cert_file: Option, + ) -> Result, ActionError> { let mut create_or_insert_files = Vec::default(); let mut create_directories = Vec::default(); @@ -78,12 +51,13 @@ impl ConfigureShellProfile { inde = " ", // indent ); - for profile_target in PROFILE_TARGETS { + for profile_target in locations.bash.iter().chain(locations.zsh.iter()) { let profile_target_path = Path::new(profile_target); if let Some(parent) = profile_target_path.parent() { if !parent.exists() { tracing::trace!( - "Did not plan to edit `{profile_target}` as its parent folder does not exist." + "Did not plan to edit `{}` as its parent folder does not exist.", + profile_target.display(), ); continue; } @@ -113,7 +87,7 @@ impl ConfigureShellProfile { inde = " ", // indent ); - for fish_prefix in PROFILE_FISH_CONFD_PREFIXES { + for fish_prefix in &locations.fish.confd_prefixes { let fish_prefix_path = PathBuf::from(fish_prefix); if !fish_prefix_path.exists() { @@ -122,7 +96,7 @@ impl ConfigureShellProfile { } let mut profile_target = fish_prefix_path; - profile_target.push(PROFILE_FISH_CONFD_SUFFIX); + profile_target.push(locations.fish.confd_suffix.clone()); if let Some(conf_d) = profile_target.parent() { create_directories.push( @@ -142,7 +116,7 @@ impl ConfigureShellProfile { .await?, ); } - for fish_prefix in PROFILE_FISH_VENDOR_CONFD_PREFIXES { + for fish_prefix in &locations.fish.vendor_confd_prefixes { let fish_prefix_path = PathBuf::from(fish_prefix); if !fish_prefix_path.exists() { @@ -151,7 +125,7 @@ impl ConfigureShellProfile { } let mut profile_target = fish_prefix_path; - profile_target.push(PROFILE_FISH_VENDOR_CONFD_SUFFIX); + profile_target.push(locations.fish.vendor_confd_suffix.clone()); if let Some(conf_d) = profile_target.parent() { create_directories.push( @@ -197,6 +171,7 @@ impl ConfigureShellProfile { } Ok(Self { + locations, create_directories, create_or_insert_into_files: create_or_insert_files, } diff --git a/src/planner/linux.rs b/src/planner/linux.rs index 18f386a..0bf8f7e 100644 --- a/src/planner/linux.rs +++ b/src/planner/linux.rs @@ -12,6 +12,8 @@ use crate::{ use std::{collections::HashMap, path::Path}; use tokio::process::Command; +use super::ShellProfileLocations; + /// A planner for Linux installs #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "cli", derive(clap::Parser))] @@ -80,7 +82,7 @@ impl Planner for Linux { .await .map_err(PlannerError::Action)? .boxed(), - ConfigureNix::plan(&self.settings) + ConfigureNix::plan(ShellProfileLocations::default(), &self.settings) .await .map_err(PlannerError::Action)? .boxed(), diff --git a/src/planner/macos.rs b/src/planner/macos.rs index c351095..bd9ab5c 100644 --- a/src/planner/macos.rs +++ b/src/planner/macos.rs @@ -4,6 +4,8 @@ use std::{collections::HashMap, io::Cursor}; use clap::ArgAction; use tokio::process::Command; +use super::ShellProfileLocations; + use crate::{ action::{ base::RemoveDirectory, @@ -144,7 +146,7 @@ impl Planner for Macos { .await .map_err(PlannerError::Action)? .boxed(), - ConfigureNix::plan(&self.settings) + ConfigureNix::plan(ShellProfileLocations::default(), &self.settings) .await .map_err(PlannerError::Action)? .boxed(), diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 4f50f3a..9e996f6 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -109,7 +109,9 @@ pub mod macos; #[cfg(target_os = "linux")] pub mod steam_deck; -use std::{collections::HashMap, string::FromUtf8Error}; +use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error}; + +use serde::{Deserialize, Serialize}; use crate::{ action::{ActionError, StatefulAction}, @@ -278,6 +280,68 @@ impl BuiltinPlanner { } } +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] +pub struct ShellProfileLocations { + pub fish: FishShellProfileLocations, + pub bash: Vec, + pub zsh: Vec, +} + +impl Default for ShellProfileLocations { + fn default() -> Self { + Self { + fish: FishShellProfileLocations::default(), + bash: vec![ + "/etc/bashrc".into(), + "/etc/profile.d/nix.sh".into(), + "/etc/bash.bashrc".into(), + ], + zsh: vec![ + // https://zsh.sourceforge.io/Intro/intro_3.html + "/etc/zshrc".into(), + "/etc/zsh/zshrc".into(), + ], + } + } +} + +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] +pub struct FishShellProfileLocations { + pub confd_suffix: PathBuf, + /** + Each of these are common values of $__fish_sysconf_dir, + under which Fish will look for the file named by + `confd_suffix`. + */ + pub confd_prefixes: Vec, + /// Fish has different syntax than zsh/bash, treat it separate + pub vendor_confd_suffix: PathBuf, + /** + Each of these are common values of $__fish_vendor_confdir, + under which Fish will look for the file named by + `confd_suffix`. + + More info: + */ + pub vendor_confd_prefixes: Vec, +} + +impl Default for FishShellProfileLocations { + fn default() -> Self { + Self { + confd_prefixes: vec![ + "/etc/fish".into(), // standard + "/usr/local/etc/fish".into(), // their installer .pkg for macOS + "/opt/homebrew/etc/fish".into(), // homebrew + "/opt/local/etc/fish".into(), // macports + ], + confd_suffix: "conf.d/nix.fish".into(), + vendor_confd_prefixes: vec!["/usr/share/fish/".into(), "/usr/local/share/fish/".into()], + vendor_confd_suffix: "vendor_conf.d/nix.fish".into(), + } + } +} + /// An error originating from a [`Planner`] #[non_exhaustive] #[derive(thiserror::Error, Debug, strum::IntoStaticStr)] diff --git a/src/planner/steam_deck.rs b/src/planner/steam_deck.rs index 528cc0b..bcfcd88 100644 --- a/src/planner/steam_deck.rs +++ b/src/planner/steam_deck.rs @@ -73,6 +73,8 @@ use crate::{ BuiltinPlanner, }; +use super::ShellProfileLocations; + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "cli", derive(clap::Parser))] pub struct SteamDeck { @@ -205,6 +207,20 @@ impl Planner for SteamDeck { .await .map_err(PlannerError::Action)?; + // We need to remove this path since it's part of the read-only install. + let mut shell_profile_locations = ShellProfileLocations::default(); + if let Some(index) = shell_profile_locations + .fish + .vendor_confd_prefixes + .iter() + .position(|v| *v == PathBuf::from("/usr/share/fish/")) + { + shell_profile_locations + .fish + .vendor_confd_prefixes + .remove(index); + } + Ok(vec![ CreateDirectory::plan(&persistence, None, None, 0o0755, true) .await @@ -221,7 +237,7 @@ impl Planner for SteamDeck { .await .map_err(PlannerError::Action)? .boxed(), - ConfigureNix::plan(&self.settings) + ConfigureNix::plan(shell_profile_locations, &self.settings) .await .map_err(PlannerError::Action)? .boxed(), diff --git a/tests/fixtures/linux/linux.json b/tests/fixtures/linux/linux.json index 733c304..ff6c475 100644 --- a/tests/fixtures/linux/linux.json +++ b/tests/fixtures/linux/linux.json @@ -808,6 +808,31 @@ }, "configure_shell_profile": { "action": { + "locations": { + "fish": { + "confd_suffix": "conf.d/nix.fish", + "confd_prefixes": [ + "/etc/fish", + "/usr/local/etc/fish", + "/opt/homebrew/etc/fish", + "/opt/local/etc/fish" + ], + "vendor_confd_suffix": "vendor_conf.d/nix.fish", + "vendor_confd_prefixes": [ + "/usr/share/fish/", + "/usr/local/share/fish/" + ] + }, + "bash": [ + "/etc/bashrc", + "/etc/profile.d/nix.sh", + "/etc/bash.bashrc" + ], + "zsh": [ + "/etc/zshrc", + "/etc/zsh/zshrc" + ] + }, "create_directories": [], "create_or_insert_into_files": [ { diff --git a/tests/fixtures/linux/steam-deck.json b/tests/fixtures/linux/steam-deck.json index 0f9169c..aa8ea21 100644 --- a/tests/fixtures/linux/steam-deck.json +++ b/tests/fixtures/linux/steam-deck.json @@ -852,6 +852,30 @@ }, "configure_shell_profile": { "action": { + "locations": { + "fish": { + "confd_suffix": "conf.d/nix.fish", + "confd_prefixes": [ + "/etc/fish", + "/usr/local/etc/fish", + "/opt/homebrew/etc/fish", + "/opt/local/etc/fish" + ], + "vendor_confd_suffix": "vendor_conf.d/nix.fish", + "vendor_confd_prefixes": [ + "/usr/local/share/fish/" + ] + }, + "bash": [ + "/etc/bashrc", + "/etc/profile.d/nix.sh", + "/etc/bash.bashrc" + ], + "zsh": [ + "/etc/zshrc", + "/etc/zsh/zshrc" + ] + }, "create_directories": [], "create_or_insert_into_files": [ { diff --git a/tests/fixtures/macos/macos.json b/tests/fixtures/macos/macos.json index cfd9ed7..0ffcf33 100644 --- a/tests/fixtures/macos/macos.json +++ b/tests/fixtures/macos/macos.json @@ -888,6 +888,31 @@ }, "configure_shell_profile": { "action": { + "locations": { + "fish": { + "confd_suffix": "conf.d/nix.fish", + "confd_prefixes": [ + "/etc/fish", + "/usr/local/etc/fish", + "/opt/homebrew/etc/fish", + "/opt/local/etc/fish" + ], + "vendor_confd_suffix": "vendor_conf.d/nix.fish", + "vendor_confd_prefixes": [ + "/usr/share/fish/", + "/usr/local/share/fish/" + ] + }, + "bash": [ + "/etc/bashrc", + "/etc/profile.d/nix.sh", + "/etc/bash.bashrc" + ], + "zsh": [ + "/etc/zshrc", + "/etc/zsh/zshrc" + ] + }, "create_directories": [], "create_or_insert_into_files": [ {