Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

om health: Shell dotfiles are Nix-managed #306

Merged
merged 46 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
11406d4
nix_health: Add Check for Shell Configurations.
rsrohitsingh682 Oct 8, 2024
b68077e
Merge branch 'main' into shell-configurations
rsrohitsingh682 Oct 8, 2024
1b92c1c
nix_health: add comments and improve msgs.
rsrohitsingh682 Oct 8, 2024
d101e14
nix_health: refactor and use struct for dotfiles.
rsrohitsingh682 Oct 10, 2024
5cc216c
nix_health: use enum for Shell.
rsrohitsingh682 Oct 10, 2024
06f7c65
Merge branch 'main' into shell-configurations
rsrohitsingh682 Oct 10, 2024
f985955
nix_health: refactored to reduce complexity.
rsrohitsingh682 Oct 23, 2024
8fdb606
Merge branch 'main' into shell-configurations
rsrohitsingh682 Oct 23, 2024
ab472b8
nix_health: Don't panic if shell is not supported.
rsrohitsingh682 Oct 23, 2024
76fd955
nix_health: remove unused import
rsrohitsingh682 Oct 23, 2024
446490c
nix_health: use read_link instead of canonicalize for reading symlink
rsrohitsingh682 Oct 24, 2024
4cf05e3
nix_health: add print statements for debugging CI failure on aarch64-…
rsrohitsingh682 Oct 24, 2024
960fd92
nix_health: Don't panic if dotfiles are not found.
rsrohitsingh682 Oct 24, 2024
aa86f83
Merge branch 'main' into shell-configurations
rsrohitsingh682 Oct 24, 2024
b901872
nix_health: refactored and removed extra Shell types; separate functi…
rsrohitsingh682 Oct 25, 2024
5f0bff4
Merge branch 'main' into shell-configurations
srid Oct 29, 2024
4939822
Improve docs
srid Oct 29, 2024
3b18c66
Use &self
srid Oct 29, 2024
8d12420
*do not use* String by habit. Use the approriate type. This also inci…
srid Oct 29, 2024
d68d1d8
Use lossy conversation to obviate error handling
srid Oct 29, 2024
6efdd4f
This error is panic worthy, no point in handling it
srid Oct 29, 2024
0afe5b4
don't allocate strings unnecessarily
srid Oct 29, 2024
5aaa25e
panic out here as well, asking the user to file bug report. we *expec…
srid Oct 29, 2024
65ab62b
decouple intertwined check from shell type
srid Oct 29, 2024
b87379a
Remove redundant Display instance
srid Oct 29, 2024
7331861
Result no more useful
srid Oct 29, 2024
84e1c24
edit suggestion
srid Oct 29, 2024
e73010f
No need to handle error either (will address logic later)
srid Oct 29, 2024
c35b5f0
rewrite faulty logic
srid Oct 29, 2024
583cec3
reomve redundant error types
srid Oct 29, 2024
04dd908
add .zshenv
srid Oct 29, 2024
9a3fd67
fmt
srid Oct 29, 2024
185746e
improve panic msg
srid Oct 29, 2024
74a43c6
simplify logic
srid Oct 29, 2024
cb5b5bb
panic only when required=true
srid Oct 29, 2024
ad5d310
warn: add prefix
srid Oct 30, 2024
1fef170
docs
srid Nov 1, 2024
14548f6
add .zprofile
srid Nov 1, 2024
6f7dfe9
mv type below
srid Nov 1, 2024
3755015
decouple
srid Nov 1, 2024
7d70226
move
srid Nov 1, 2024
ddc2d85
separate functoin confusing here
srid Nov 1, 2024
3e24995
rewrite this to track managed/unmanaged files
srid Nov 1, 2024
0295623
health: display url
srid Nov 1, 2024
b719d67
display managed & unmanaged
srid Nov 1, 2024
a74f31c
add shell too
srid Nov 1, 2024
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
2 changes: 1 addition & 1 deletion crates/omnix-health/src/check/direnv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fn install_check(
}

/// Check that the path is in the Nix store (usually /nix/store)
fn is_path_in_nix_store(path: &std::path::Path) -> bool {
pub fn is_path_in_nix_store(path: &std::path::Path) -> bool {
path.starts_with("/nix/store")
}

Expand Down
1 change: 1 addition & 0 deletions crates/omnix-health/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ pub mod flake_enabled;
pub mod max_jobs;
pub mod min_nix_version;
pub mod rosetta;
pub mod shell;
pub mod trusted_users;
158 changes: 158 additions & 0 deletions crates/omnix-health/src/check/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};

use crate::traits::{Check, CheckResult, Checkable};

/// Shell types
///
#[derive(Debug, Serialize, Deserialize, Clone, Hash, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Shell {
Zsh,
Bash,
Undeterminable,
srid marked this conversation as resolved.
Show resolved Hide resolved
#[serde(other)]
Other,
srid marked this conversation as resolved.
Show resolved Hide resolved
}

impl std::fmt::Display for Shell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let shell_str = match self {
Shell::Zsh => "zsh",
Shell::Bash => "bash",
Shell::Other => "<unsupported shell>",
Shell::Undeterminable => "<undeterminable shell>",
};
write!(f, "{}", shell_str)
}
}

impl Shell {
/// Creates a `Shell` from a path string.
srid marked this conversation as resolved.
Show resolved Hide resolved
fn from_path(path: &str) -> Self {
srid marked this conversation as resolved.
Show resolved Hide resolved
let shell_name = Path::new(path).file_name().and_then(|name| name.to_str());
srid marked this conversation as resolved.
Show resolved Hide resolved

match shell_name {
None => Shell::Undeterminable,
Some("zsh") => Shell::Zsh,
Some("bash") => Shell::Bash,
Some(_) => Shell::Other,
}
}

/// Returns the dotfiles for a given `shell`
fn get_dotfiles_of_shell(shell: &Shell) -> Result<Vec<String>, ShellError> {
match shell {
Shell::Zsh => Ok(vec![".zshrc".to_string()]),
Shell::Bash => Ok(vec![
".bashrc".to_string(),
".bash_profile".to_string(),
".profile".to_string(),
]),
Shell::Other => Err(ShellError::UnsupportedShell),
Shell::Undeterminable => Err(ShellError::UndeterminableShell),
}
}
}

impl Default for Shell {
fn default() -> Self {
let shell_path = std::env::var("SHELL").expect("Environment variable `SHELL` not set");
srid marked this conversation as resolved.
Show resolved Hide resolved
Shell::from_path(&shell_path)
}
}

impl Checkable for Shell {
fn check(
&self,
_nix_info: &nix_rs::info::NixInfo,
_flake: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
let check = Check {
title: "Shell Configurations".to_string(),
info: "Dotfiles managed by Nix".to_string(),
result: match are_dotfiles_nix_managed(self) {
Ok(true) => CheckResult::Green,
Ok(false) => {
let shell = Shell::default();
CheckResult::Red {
msg: format!(
"Default Shell: {} is not managed by Nix",
shell
),
suggestion: format!("Manage {} configurations through https://github.com/juspay/nixos-unified-template", shell)
}
}
Err(err@ShellError::UnsupportedShell) => CheckResult::Red {
msg: err.to_string(),
suggestion: "We support only Bash & Zsh Shells. Manage Zsh or Bash through https://github.com/juspay/nixos-unified-template".to_owned(),
},
Err(ShellError::DotfilesNotFound(err)) => CheckResult::Red {
msg: err.to_string(),
suggestion: "Manage Zsh or Bash shells through https://github.com/juspay/nixos-unified-template".to_owned(),
},
Err(error) => {
panic!(
"Error occurred while checking shell configuration: {}",
error
);
}
},
required: false,
};
vec![check]
}
}

/// Checks if all dotfiles for a given shell are managed by nix
///
/// # Returns
/// * `true` if all dotfiles are nix-managed
/// * `false` if any dotfile is not nix-managed
/// * `Err` if there was an error during the check
fn are_dotfiles_nix_managed(shell: &Shell) -> Result<bool, ShellError> {
let home_dir = PathBuf::from(
std::env::var("HOME").map_err(|_| ShellError::EnvVarNotSet("HOME".to_string()))?,
srid marked this conversation as resolved.
Show resolved Hide resolved
);

let dotfiles = Shell::get_dotfiles_of_shell(shell)?;

// Iterate over each dotfile and check if it is managed by nix
for dotfile in dotfiles {
if !check_dotfile_is_managed_by_nix(&home_dir, &dotfile)? {
return Ok(false);
}
}
Ok(true)
}

fn check_dotfile_is_managed_by_nix(home_dir: &Path, dotfile: &str) -> Result<bool, ShellError> {
let path = home_dir.join(dotfile);
let target =
std::fs::read_link(path).map_err(|_| ShellError::DotfilesNotFound(dotfile.to_owned()))?;
Ok(super::direnv::is_path_in_nix_store(&target))
}

#[derive(thiserror::Error, Debug)]
pub struct DotfilesNotFound(#[from] std::io::Error);

impl std::fmt::Display for DotfilesNotFound {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}

#[derive(thiserror::Error, Debug)]
pub enum ShellError {
#[error("Checking configurations for shell is not supported")]
UnsupportedShell,

#[error("Unable to determine user's default shell")]
UndeterminableShell,

#[error("Cannot read symlink target of : {0}")]
DotfilesNotFound(String),

#[error("Environent variable {0} not set")]
EnvVarNotSet(String),
}
4 changes: 3 additions & 1 deletion crates/omnix-health/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use traits::Check;

use self::check::{
caches::Caches, flake_enabled::FlakeEnabled, max_jobs::MaxJobs, min_nix_version::MinNixVersion,
rosetta::Rosetta, trusted_users::TrustedUsers,
rosetta::Rosetta, shell::Shell, trusted_users::TrustedUsers,
};

/// Nix Health check of user's install
Expand All @@ -36,6 +36,7 @@ pub struct NixHealth {
pub trusted_users: TrustedUsers,
pub caches: Caches,
pub direnv: Direnv,
pub shell: Shell,
}

/// Convert [NixHealth] into a generic [Vec] of checks
Expand All @@ -53,6 +54,7 @@ impl<'a> IntoIterator for &'a NixHealth {
&self.trusted_users,
&self.caches,
&self.direnv,
&self.shell,
];
items.into_iter()
}
Expand Down