Make shell profile locations chosen by planner (#375)

* Make shell profile locations chosen by planner

* Fix mac

* Fix fixture
This commit is contained in:
Ana Hobden 2023-03-27 14:36:54 -07:00 committed by GitHub
parent f2c94e3578
commit 4856cff7b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 189 additions and 49 deletions

View file

@ -6,6 +6,7 @@ use crate::{
common::{ConfigureShellProfile, PlaceNixConfiguration}, common::{ConfigureShellProfile, PlaceNixConfiguration},
Action, ActionDescription, ActionError, ActionTag, StatefulAction, Action, ActionDescription, ActionError, ActionTag, StatefulAction,
}, },
planner::ShellProfileLocations,
settings::{CommonSettings, SCRATCH_DIR}, settings::{CommonSettings, SCRATCH_DIR},
}; };
@ -23,14 +24,20 @@ pub struct ConfigureNix {
impl ConfigureNix { impl ConfigureNix {
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> { pub async fn plan(
shell_profile_locations: ShellProfileLocations,
settings: &CommonSettings,
) -> Result<StatefulAction<Self>, ActionError> {
let setup_default_profile = SetupDefaultProfile::plan(PathBuf::from(SCRATCH_DIR)) let setup_default_profile = SetupDefaultProfile::plan(PathBuf::from(SCRATCH_DIR))
.await .await
.map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?; .map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?;
let configure_shell_profile = if settings.modify_profile { let configure_shell_profile = if settings.modify_profile {
Some( Some(
ConfigureShellProfile::plan(settings.ssl_cert_file.clone()) ConfigureShellProfile::plan(
shell_profile_locations,
settings.ssl_cert_file.clone(),
)
.await .await
.map_err(|e| { .map_err(|e| {
ActionError::Child(ConfigureShellProfile::action_tag(), Box::new(e)) ActionError::Child(ConfigureShellProfile::action_tag(), Box::new(e))

View file

@ -1,43 +1,12 @@
use crate::action::base::{create_or_insert_into_file, CreateDirectory, CreateOrInsertIntoFile}; use crate::action::base::{create_or_insert_into_file, CreateDirectory, CreateOrInsertIntoFile};
use crate::action::{Action, ActionDescription, ActionError, ActionTag, StatefulAction}; use crate::action::{Action, ActionDescription, ActionError, ActionTag, StatefulAction};
use crate::planner::ShellProfileLocations;
use nix::unistd::User; use nix::unistd::User;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tokio::task::JoinSet; use tokio::task::JoinSet;
use tracing::{span, Instrument, Span}; 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_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"; 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)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureShellProfile { pub struct ConfigureShellProfile {
locations: ShellProfileLocations,
create_directories: Vec<StatefulAction<CreateDirectory>>, create_directories: Vec<StatefulAction<CreateDirectory>>,
create_or_insert_into_files: Vec<StatefulAction<CreateOrInsertIntoFile>>, create_or_insert_into_files: Vec<StatefulAction<CreateOrInsertIntoFile>>,
} }
impl ConfigureShellProfile { impl ConfigureShellProfile {
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(ssl_cert_file: Option<PathBuf>) -> Result<StatefulAction<Self>, ActionError> { pub async fn plan(
locations: ShellProfileLocations,
ssl_cert_file: Option<PathBuf>,
) -> Result<StatefulAction<Self>, ActionError> {
let mut create_or_insert_files = Vec::default(); let mut create_or_insert_files = Vec::default();
let mut create_directories = Vec::default(); let mut create_directories = Vec::default();
@ -78,12 +51,13 @@ impl ConfigureShellProfile {
inde = " ", // indent 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); let profile_target_path = Path::new(profile_target);
if let Some(parent) = profile_target_path.parent() { if let Some(parent) = profile_target_path.parent() {
if !parent.exists() { if !parent.exists() {
tracing::trace!( 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; continue;
} }
@ -113,7 +87,7 @@ impl ConfigureShellProfile {
inde = " ", // indent 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); let fish_prefix_path = PathBuf::from(fish_prefix);
if !fish_prefix_path.exists() { if !fish_prefix_path.exists() {
@ -122,7 +96,7 @@ impl ConfigureShellProfile {
} }
let mut profile_target = fish_prefix_path; 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() { if let Some(conf_d) = profile_target.parent() {
create_directories.push( create_directories.push(
@ -142,7 +116,7 @@ impl ConfigureShellProfile {
.await?, .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); let fish_prefix_path = PathBuf::from(fish_prefix);
if !fish_prefix_path.exists() { if !fish_prefix_path.exists() {
@ -151,7 +125,7 @@ impl ConfigureShellProfile {
} }
let mut profile_target = fish_prefix_path; 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() { if let Some(conf_d) = profile_target.parent() {
create_directories.push( create_directories.push(
@ -197,6 +171,7 @@ impl ConfigureShellProfile {
} }
Ok(Self { Ok(Self {
locations,
create_directories, create_directories,
create_or_insert_into_files: create_or_insert_files, create_or_insert_into_files: create_or_insert_files,
} }

View file

@ -12,6 +12,8 @@ use crate::{
use std::{collections::HashMap, path::Path}; use std::{collections::HashMap, path::Path};
use tokio::process::Command; use tokio::process::Command;
use super::ShellProfileLocations;
/// A planner for Linux installs /// A planner for Linux installs
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "cli", derive(clap::Parser))] #[cfg_attr(feature = "cli", derive(clap::Parser))]
@ -80,7 +82,7 @@ impl Planner for Linux {
.await .await
.map_err(PlannerError::Action)? .map_err(PlannerError::Action)?
.boxed(), .boxed(),
ConfigureNix::plan(&self.settings) ConfigureNix::plan(ShellProfileLocations::default(), &self.settings)
.await .await
.map_err(PlannerError::Action)? .map_err(PlannerError::Action)?
.boxed(), .boxed(),

View file

@ -4,6 +4,8 @@ use std::{collections::HashMap, io::Cursor};
use clap::ArgAction; use clap::ArgAction;
use tokio::process::Command; use tokio::process::Command;
use super::ShellProfileLocations;
use crate::{ use crate::{
action::{ action::{
base::RemoveDirectory, base::RemoveDirectory,
@ -144,7 +146,7 @@ impl Planner for Macos {
.await .await
.map_err(PlannerError::Action)? .map_err(PlannerError::Action)?
.boxed(), .boxed(),
ConfigureNix::plan(&self.settings) ConfigureNix::plan(ShellProfileLocations::default(), &self.settings)
.await .await
.map_err(PlannerError::Action)? .map_err(PlannerError::Action)?
.boxed(), .boxed(),

View file

@ -109,7 +109,9 @@ pub mod macos;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod steam_deck; pub mod steam_deck;
use std::{collections::HashMap, string::FromUtf8Error}; use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error};
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
action::{ActionError, StatefulAction}, 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<PathBuf>,
pub zsh: Vec<PathBuf>,
}
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<PathBuf>,
/// 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: <https://fishshell.com/docs/3.3/index.html#configuration-files>
*/
pub vendor_confd_prefixes: Vec<PathBuf>,
}
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`] /// An error originating from a [`Planner`]
#[non_exhaustive] #[non_exhaustive]
#[derive(thiserror::Error, Debug, strum::IntoStaticStr)] #[derive(thiserror::Error, Debug, strum::IntoStaticStr)]

View file

@ -73,6 +73,8 @@ use crate::{
BuiltinPlanner, BuiltinPlanner,
}; };
use super::ShellProfileLocations;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "cli", derive(clap::Parser))] #[cfg_attr(feature = "cli", derive(clap::Parser))]
pub struct SteamDeck { pub struct SteamDeck {
@ -205,6 +207,20 @@ impl Planner for SteamDeck {
.await .await
.map_err(PlannerError::Action)?; .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![ Ok(vec![
CreateDirectory::plan(&persistence, None, None, 0o0755, true) CreateDirectory::plan(&persistence, None, None, 0o0755, true)
.await .await
@ -221,7 +237,7 @@ impl Planner for SteamDeck {
.await .await
.map_err(PlannerError::Action)? .map_err(PlannerError::Action)?
.boxed(), .boxed(),
ConfigureNix::plan(&self.settings) ConfigureNix::plan(shell_profile_locations, &self.settings)
.await .await
.map_err(PlannerError::Action)? .map_err(PlannerError::Action)?
.boxed(), .boxed(),

View file

@ -808,6 +808,31 @@
}, },
"configure_shell_profile": { "configure_shell_profile": {
"action": { "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_directories": [],
"create_or_insert_into_files": [ "create_or_insert_into_files": [
{ {

View file

@ -852,6 +852,30 @@
}, },
"configure_shell_profile": { "configure_shell_profile": {
"action": { "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_directories": [],
"create_or_insert_into_files": [ "create_or_insert_into_files": [
{ {

View file

@ -888,6 +888,31 @@
}, },
"configure_shell_profile": { "configure_shell_profile": {
"action": { "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_directories": [],
"create_or_insert_into_files": [ "create_or_insert_into_files": [
{ {