This commit is contained in:
2025-09-13 23:16:22 +02:00
commit 5057e1effa
179 changed files with 20524 additions and 0 deletions

View File

@@ -0,0 +1,700 @@
{ pkgs
, profile
, backupFiles ? [ ".config/mimeapps.list.backup" ]
, ...
}:
let
backupFilesString = pkgs.lib.strings.concatStringsSep " " backupFiles;
# Create the get-doom script as a dependency
get-doom-script = pkgs.writeShellScriptBin "get-doom" ''
#!/usr/bin/env bash
set -euo pipefail
# --- Icons ---
ICON_CHECK=""
ICON_INFO=""
ICON_ROCKET="🚀"
# --- Helper Functions ---
print_status() {
echo
echo "--- $ICON_INFO $1 ---"
}
print_success() {
echo "--- $ICON_CHECK $1 ---"
}
print_banner() {
echo "==============================="
echo " Doom Emacs Installer $ICON_ROCKET"
echo "==============================="
}
is_doom_installed() {
local dir="$1"
[[ -x "$dir/bin/doom" ]] && [[ -f "$dir/core/doom.el" ]]
}
emacsdir_is_empty() {
local dir="$1"
[[ -d "$dir" ]] && [[ -z "$(ls -A "$dir" 2>/dev/null)" ]]
}
# --- Main Script ---
print_banner
EMACSDIR="$HOME/.emacs.d"
if is_doom_installed "$EMACSDIR"; then
print_success "Doom Emacs is already installed."
exit 0
fi
if [[ -d "$EMACSDIR" ]]; then
if emacsdir_is_empty "$EMACSDIR"; then
print_status "Found empty $EMACSDIR; proceeding to install Doom Emacs into it..."
else
echo "Error: Found $EMACSDIR but it does not look like a Doom Emacs installation." >&2
echo "Refusing to overwrite a non-empty directory. Move it away and re-run, e.g.:" >&2
echo " mv \"$EMACSDIR\" \"$EMACSDIR.bak\"" >&2
exit 1
fi
fi
print_status "Cloning Doom Emacs..."
${pkgs.git}/bin/git clone --depth 1 https://github.com/doomemacs/doomemacs "$EMACSDIR"
print_success "Doom Emacs cloned."
print_status "Running Doom install..."
"$EMACSDIR/bin/doom" install
print_success "Doom install complete."
print_status "Running doom sync..."
"$EMACSDIR/bin/doom" sync
print_success "Doom sync complete."
echo
print_success "All done! Doom Emacs is ready to use."
'';
in
pkgs.writeShellScriptBin "zcli" ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
# --- Program info ---
#
# zcli - NixOS System Management CLI
# ==================================
#
# Purpose: NixOS system management utility for ZaneyOS distribution
# Author: Don Williams (ddubs) & Zaney
# Start Date: June 7th, 2025
# Version: 1.0.2
#
# Architecture:
# - Nix-generated shell script using writeShellScriptBin
# - Configuration via Nix parameters (profile, backupFiles)
# - Uses 'nh' tool for NixOS operations, 'inxi' for diagnostics
# - Git integration for host configuration versioning
#
# Helper Functions:
# verify_hostname() - Validates current hostname against flake.nix host variable
# Exits with error if mismatch or missing host directory
# detect_gpu_profile() - Parses lspci output to identify GPU hardware
# Returns: nvidia/nvidia-laptop/amd/intel/vm/empty
# handle_backups() - Removes files listed in BACKUP_FILES array from $HOME
# parse_nh_args() - Parses command-line arguments for nh operations
# print_help() - Outputs command usage and available operations
#
# Command Functions:
# cleanup - Interactive cleanup of old generations via 'nh clean'
# diag - Generate system report using 'inxi --full'
# list-gens - Display user/system generations via nix-env and nix profile
# rebuild - NixOS rebuild using 'nh os switch'
# rebuild-boot - NixOS rebuild for next boot using 'nh os boot'
# trim - SSD optimization via 'sudo fstrim -v /'
# update - Flake update + rebuild using 'nh os switch --update'
# update-host - Modify flake.nix host/profile variables via sed
# add-host - Add new host configuration
# del-host - Delete host configuration
# doom [sub] - Doom Emacs management (install/status/remove/update)
#
# Variables:
# PROJECT - Base directory name (ddubsos/zaneyos)
# PROFILE - Hardware profile from Nix parameter
# BACKUP_FILES - Array of backup file paths to clean
# FLAKE_NIX_PATH - Path to flake.nix for host/profile updates
#
# --- Configuration ---
PROJECT="zaneyos" #ddubos or zaneyos
PROFILE="${profile}"
BACKUP_FILES_STR="${backupFilesString}"
VERSION="1.0.2"
FLAKE_NIX_PATH="$HOME/$PROJECT/flake.nix"
read -r -a BACKUP_FILES <<< "$BACKUP_FILES_STR"
# --- Helper Functions ---
verify_hostname() {
local current_hostname
local flake_hostname
current_hostname="$(hostname)"
# Extract the host value from flake.nix
if [ -f "$FLAKE_NIX_PATH" ]; then
flake_hostname=$(${pkgs.gnugrep}/bin/grep -E '^[[:space:]]*host[[:space:]]*=' "$FLAKE_NIX_PATH" | ${pkgs.gnused}/bin/sed 's/.*=[[:space:]]*"\([^"]*\)".*/\1/')
if [ -z "$flake_hostname" ]; then
echo "Error: Could not find 'host' variable in $FLAKE_NIX_PATH" >&2
exit 1
fi
if [ "$current_hostname" != "$flake_hostname" ]; then
echo "Error: Hostname mismatch!" >&2
echo " Current hostname: '$current_hostname'" >&2
echo " Flake.nix host: '$flake_hostname'" >&2
echo "" >&2
echo "Hint: Run 'zcli update-host' to automatically update flake.nix" >&2
echo " or manually edit $FLAKE_NIX_PATH" >&2
exit 1
fi
else
echo "Error: Flake.nix not found at $FLAKE_NIX_PATH" >&2
exit 1
fi
# Also check if host folder exists
local folder="$HOME/$PROJECT/hosts/$current_hostname"
if [ ! -d "$folder" ]; then
echo "Error: Matching host not found in $PROJECT, Missing folder: $folder" >&2
exit 1
fi
}
print_help() {
echo "ZaneyOS CLI Utility -- version $VERSION"
echo ""
echo "Usage: zcli [command] [options]"
echo ""
echo "Commands:"
echo " cleanup - Clean up old system generations. Can specify a number to keep."
echo " diag - Create a system diagnostic report."
echo " (Filename: homedir/diag.txt)"
echo " list-gens - List user and system generations."
echo " rebuild - Rebuild the NixOS system configuration."
echo " rebuild-boot - Rebuild and set as boot default (activates on next restart)."
echo " trim - Trim filesystems to improve SSD performance."
echo " update - Update the flake and rebuild the system."
echo " update-host - Auto set host and profile in flake.nix."
echo " (Opt: zcli update-host [hostname] [profile])"
echo ""
echo "Options for rebuild, rebuild-boot, and update commands:"
echo " --dry, -n - Show what would be done without doing it"
echo " --ask, -a - Ask for confirmation before proceeding"
echo " --cores N - Limit build to N cores (useful for VMs)"
echo " --verbose, -v - Show verbose output"
echo " --no-nom - Don't use nix-output-monitor"
echo ""
echo "Doom Emacs:"
echo " doom install - Install Doom Emacs using get-doom script."
echo " doom status - Check if Doom Emacs is installed."
echo " doom remove - Remove Doom Emacs installation."
echo " doom update - Update Doom Emacs (runs doom sync)."
echo ""
echo " help - Show this help message."
}
handle_backups() {
if [ ''${#BACKUP_FILES[@]} -eq 0 ]; then
echo "No backup files configured to check."
return
fi
echo "Checking for backup files to remove..."
for file_path in "''${BACKUP_FILES[@]}"; do
full_path="$HOME/$file_path"
if [ -f "$full_path" ]; then
echo "Removing stale backup file: $full_path"
rm "$full_path"
fi
done
}
detect_gpu_profile() {
local detected_profile=""
local has_nvidia=false
local has_intel=false
local has_amd=false
local has_vm=false
if ${pkgs.pciutils}/bin/lspci &> /dev/null; then # Check if lspci is available
if ${pkgs.pciutils}/bin/lspci | ${pkgs.gnugrep}/bin/grep -qi 'vga\|3d'; then
while read -r line; do
if echo "$line" | ${pkgs.gnugrep}/bin/grep -qi 'nvidia'; then
has_nvidia=true
elif echo "$line" | ${pkgs.gnugrep}/bin/grep -qi 'amd'; then
has_amd=true
elif echo "$line" | ${pkgs.gnugrep}/bin/grep -qi 'intel'; then
has_intel=true
elif echo "$line" | ${pkgs.gnugrep}/bin/grep -qi 'virtio\|vmware'; then
has_vm=true
fi
done < <(${pkgs.pciutils}/bin/lspci | ${pkgs.gnugrep}/bin/grep -i 'vga\|3d')
if "$has_vm"; then
detected_profile="vm"
elif "$has_nvidia" && "$has_intel"; then
detected_profile="nvidia-laptop"
elif "$has_nvidia"; then
detected_profile="nvidia"
elif "$has_amd"; then
detected_profile="amd"
elif "$has_intel"; then
detected_profile="intel"
fi
fi
else
echo "Warning: lspci command not found. Cannot auto-detect GPU profile." >&2
fi
echo "$detected_profile" # Return the detected profile
}
# --- Helper function to parse additional arguments ---
parse_nh_args() {
local args_string=""
local options_selected=()
shift # Remove the main command (rebuild, rebuild-boot, update)
while [[ $# -gt 0 ]]; do
case $1 in
--dry|-n)
args_string="$args_string --dry"
options_selected+=("dry run mode (showing what would be done)")
shift
;;
--ask|-a)
args_string="$args_string --ask"
options_selected+=("confirmation prompts enabled")
shift
;;
--cores)
if [[ -n $2 && $2 =~ ^[0-9]+$ ]]; then
args_string="$args_string -- --cores $2"
options_selected+=("limited to $2 CPU cores")
shift 2
else
echo "Error: --cores requires a numeric argument" >&2
exit 1
fi
;;
--verbose|-v)
args_string="$args_string --verbose"
options_selected+=("verbose output enabled")
shift
;;
--no-nom)
args_string="$args_string --no-nom"
options_selected+=("nix-output-monitor disabled")
shift
;;
--)
shift
args_string="$args_string -- $*"
options_selected+=("additional arguments: $*")
break
;;
-*)
echo "Warning: Unknown flag '$1' - passing through to nh" >&2
args_string="$args_string $1"
options_selected+=("unknown flag '$1' passed through")
shift
;;
*)
echo "Error: Unexpected argument '$1'" >&2
exit 1
;;
esac
done
# Print friendly confirmation of selected options to stderr so it doesn't interfere with return value
if [[ ''${#options_selected[@]} -gt 0 ]]; then
echo "Options selected:" >&2
for option in "''${options_selected[@]}"; do
echo " $option" >&2
done
echo >&2
fi
# Return only the args string
echo "$args_string"
}
# --- Main Logic ---
if [ "$#" -eq 0 ]; then
echo "Error: No command provided." >&2
print_help
exit 1
fi
case "$1" in
cleanup)
echo "Warning! This will remove old generations of your system."
read -p "How many generations to keep (default: all)? " keep_count
if [ -z "$keep_count" ]; then
read -p "This will remove all but the current generation. Continue (y/N)? " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
${pkgs.nh}/bin/nh clean all -v
else
echo "Cleanup cancelled."
fi
else
read -p "This will keep the last $keep_count generations. Continue (y/N)? " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
${pkgs.nh}/bin/nh clean all -k "$keep_count" -v
else
echo "Cleanup cancelled."
fi
fi
LOG_DIR="$HOME/zcli-cleanup-logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/zcli-cleanup-$(${pkgs.coreutils}/bin/date +%Y-%m-%d_%H-%M-%S).log"
echo "Cleaning up old log files..." >> "$LOG_FILE"
${pkgs.findutils}/bin/find "$LOG_DIR" -type f -mtime +3 -name "*.log" -delete >> "$LOG_FILE" 2>&1
echo "Cleanup process logged to $LOG_FILE"
;;
diag)
echo "Generating system diagnostic report..."
${pkgs.inxi}/bin/inxi --full > "$HOME/diag.txt"
echo "Diagnostic report saved to $HOME/diag.txt"
;;
help)
print_help
;;
list-gens)
echo "--- User Generations ---"
${pkgs.nix}/bin/nix-env --list-generations | ${pkgs.coreutils}/bin/cat || echo "Could not list user generations."
echo ""
echo "--- System Generations ---"
${pkgs.nix}/bin/nix profile history --profile /nix/var/nix/profiles/system | ${pkgs.coreutils}/bin/cat || echo "Could not list system generations."
;;
rebuild)
verify_hostname
handle_backups
# Parse additional arguments
extra_args=$(parse_nh_args "$@")
echo "Starting NixOS rebuild for host: $(${pkgs.nettools}/bin/hostname)"
if eval "${pkgs.nh}/bin/nh os switch --hostname '$PROFILE' $extra_args"; then
echo "Rebuild finished successfully"
else
echo "Rebuild Failed" >&2
exit 1
fi
;;
rebuild-boot)
verify_hostname
handle_backups
# Parse additional arguments
extra_args=$(parse_nh_args "$@")
echo "Starting NixOS rebuild (boot) for host: $(${pkgs.nettools}/bin/hostname)"
echo "Note: Configuration will be activated on next reboot"
if eval "${pkgs.nh}/bin/nh os boot --hostname '$PROFILE' $extra_args"; then
echo "Rebuild-boot finished successfully"
echo "New configuration set as boot default - restart to activate"
else
echo "Rebuild-boot Failed" >&2
exit 1
fi
;;
trim)
echo "Running 'sudo fstrim -v /' may take a few minutes and impact system performance."
read -p "Enter (y/Y) to run now or enter to exit (y/N): " -n 1 -r
echo # move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Running fstrim..."
sudo ${pkgs.util-linux}/bin/fstrim -v /
echo "fstrim complete."
else
echo "Trim operation cancelled."
fi
;;
update)
verify_hostname
handle_backups
# Parse additional arguments
extra_args=$(parse_nh_args "$@")
echo "Updating flake and rebuilding system for host: $(${pkgs.nettools}/bin/hostname)"
if eval "${pkgs.nh}/bin/nh os switch --hostname '$PROFILE' --update $extra_args"; then
echo "Update and rebuild finished successfully"
else
echo "Update and rebuild Failed" >&2
exit 1
fi
;;
update-host)
target_hostname=""
target_profile=""
if [ "$#" -eq 3 ]; then # zcli update-host <hostname> <profile>
target_hostname="$2"
target_profile="$3"
elif [ "$#" -eq 1 ]; then # zcli update-host (auto-detect)
echo "Attempting to auto-detect hostname and GPU profile..."
target_hostname=$(${pkgs.nettools}/bin/hostname)
target_profile=$(detect_gpu_profile)
if [ -z "$target_profile" ]; then
echo "Error: Could not auto-detect a specific GPU profile. Please provide it manually." >&2
echo "Usage: zcli update-host [hostname] [profile]" >&2
exit 1
fi
echo "Auto-detected Hostname: $target_hostname"
echo "Auto-detected Profile: $target_profile"
else
echo "Error: Invalid number of arguments for 'update-host'." >&2
echo "Usage: zcli update-host [hostname] [profile]" >&2
exit 1
fi
echo "Updating $FLAKE_NIX_PATH..."
# Update host
if ${pkgs.gnused}/bin/sed -i "s/^[[:space:]]*host[[:space:]]*=[[:space:]]*\".*\"/ host = \"$target_hostname\"/" "$FLAKE_NIX_PATH"; then
echo "Successfully updated host to: $target_hostname"
else
echo "Error: Failed to update host in $FLAKE_NIX_PATH" >&2
exit 1
fi
# Update profile
if ${pkgs.gnused}/bin/sed -i "s/^[[:space:]]*profile[[:space:]]*=[[:space:]]*\".*\"/ profile = \"$target_profile\"/" "$FLAKE_NIX_PATH"; then
echo "Successfully updated profile to: $target_profile"
else
echo "Error: Failed to update profile in $FLAKE_NIX_PATH" >&2
exit 1
fi
echo "Flake.nix updated successfully!"
;;
add-host)
hostname=""
profile_arg=""
if [ "$#" -ge 2 ]; then
hostname="$2"
fi
if [ "$#" -eq 3 ]; then
profile_arg="$3"
fi
if [ -z "$hostname" ]; then
read -p "Enter the new hostname: " hostname
fi
if [ -d "$HOME/$PROJECT/hosts/$hostname" ]; then
echo "Error: Host '$hostname' already exists." >&2
exit 1
fi
echo "Copying default host configuration..."
${pkgs.coreutils}/bin/cp -r "$HOME/$PROJECT/hosts/default" "$HOME/$PROJECT/hosts/$hostname"
detected_profile=""
if [[ -n "$profile_arg" && "$profile_arg" =~ ^(intel|amd|nvidia|nvidia-hybrid|vm)$ ]]; then
detected_profile="$profile_arg"
else
echo "Detecting GPU profile..."
detected_profile=$(detect_gpu_profile)
echo "Detected GPU profile: $detected_profile"
read -p "Is this correct? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
read -p "Enter the correct profile (intel, amd, nvidia, nvidia-hybrid, vm): " new_profile
while [[ ! "$new_profile" =~ ^(intel|amd|nvidia|nvidia-hybrid|vm)$ ]]; do
echo "Invalid profile. Please enter one of the following: intel, amd, nvidia, nvidia-hybrid, vm"
read -p "Enter the correct profile: " new_profile
done
detected_profile=$new_profile
fi
fi
echo "Setting profile to '$detected_profile'..."
${pkgs.gnused}/bin/sed -i "s/profile = .*/profile = \"$detected_profile\";/" "$HOME/$PROJECT/hosts/$hostname/default.nix"
read -p "Generate new hardware.nix? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Generating hardware.nix..."
sudo nixos-generate-config --show-hardware-config > "$HOME/$PROJECT/hosts/$hostname/hardware.nix"
echo "hardware.nix generated."
fi
echo "Adding new host to git..."
${pkgs.git}/bin/git -C "$HOME/$PROJECT" add .
echo "hostname: $hostname added"
;;
del-host)
hostname=""
if [ "$#" -eq 2 ]; then
hostname="$2"
else
read -p "Enter the hostname to delete: " hostname
fi
if [ ! -d "$HOME/$PROJECT/hosts/$hostname" ]; then
echo "Error: Host '$hostname' does not exist." >&2
exit 1
fi
read -p "Are you sure you want to delete the host '$hostname'? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Deleting host '$hostname'..."
${pkgs.coreutils}/bin/rm -rf "$HOME/$PROJECT/hosts/$hostname"
${pkgs.git}/bin/git -C "$HOME/$PROJECT" add .
echo "hostname: $hostname removed"
else
echo "Deletion cancelled."
fi
;;
doom)
if [ "$#" -lt 2 ]; then
echo "Error: doom command requires a subcommand." >&2
echo "Usage: zcli doom [install|status|remove|update]" >&2
exit 1
fi
# Ensure we're acting on a valid host and we can locate variables.nix
verify_hostname
current_hostname="$(hostname)"
host_vars_file="$HOME/$PROJECT/hosts/$current_hostname/variables.nix"
if [ ! -f "$host_vars_file" ]; then
echo "Error: Host variables file not found: $host_vars_file" >&2
echo "Please ensure your host folder exists and contains variables.nix." >&2
exit 1
fi
is_doom_enabled() {
# Return 0 if doomEmacsEnable = true; appears (ignoring leading spaces)
${pkgs.gnugrep}/bin/grep -Eq '^[[:space:]]*doomEmacsEnable[[:space:]]*=[[:space:]]*true[[:space:]]*;' "$host_vars_file"
}
ensure_doom_enabled() {
# If the variable is present but false, flip it; if missing, append it
if ${pkgs.gnugrep}/bin/grep -Eq '^[[:space:]]*doomEmacsEnable[[:space:]]*=' "$host_vars_file"; then
${pkgs.gnused}/bin/sed -i 's/^[[:space:]]*doomEmacsEnable[[:space:]]*=.*/ doomEmacsEnable = true;/' "$host_vars_file"
else
echo "" >> "$host_vars_file"
echo " # Enabled by zcli doom on $(date)" >> "$host_vars_file"
echo " doomEmacsEnable = true;" >> "$host_vars_file"
fi
}
doom_subcommand="$2"
case "$doom_subcommand" in
install)
if ! is_doom_enabled; then
echo " Doom Emacs is disabled for host '$current_hostname' (doomEmacsEnable = false)." >&2
echo "To enable, set doomEmacsEnable = true; in:" >&2
echo " $host_vars_file" >&2
echo "and rebuild your system before installing Doom." >&2
echo
read -p "Enable Doom for this host now and rebuild? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Enabling Doom Emacs in $host_vars_file..."
ensure_doom_enabled
echo "Rebuilding system so user modules are applied..."
if ${pkgs.nh}/bin/nh os switch --hostname "$PROFILE"; then
echo "Rebuild complete. Proceeding with Doom installation."
else
echo "Error: Rebuild failed. Please fix the build and re-run 'zcli doom install'." >&2
exit 1
fi
else
echo "Aborting. Please enable doomEmacsEnable and rebuild before installing." >&2
exit 1
fi
fi
echo "Installing Doom Emacs..."
${get-doom-script}/bin/get-doom
;;
status)
if [ -x "$HOME/.emacs.d/bin/doom" ] && [ -f "$HOME/.emacs.d/core/doom.el" ]; then
echo " Doom Emacs appears installed at $HOME/.emacs.d"
if [ -f "$HOME/.doom.d/init.el" ]; then
echo " User config found: $HOME/.doom.d/init.el"
else
echo " Warning: User config (~/.doom.d) not found"
fi
echo "Version information:"
"$HOME/.emacs.d/bin/doom" version 2>/dev/null || echo "Could not get version information"
else
if [ -d "$HOME/.emacs.d" ]; then
if [ -z "$(ls -A "$HOME/.emacs.d" 2>/dev/null)" ]; then
echo " Found empty ~/.emacs.d (not a valid Doom installation)"
else
echo " ~/.emacs.d exists but Doom was not detected"
fi
else
echo " Doom Emacs is not installed"
fi
echo "Run 'zcli doom install' to install it"
fi
;;
remove)
if [ ! -d "$HOME/.emacs.d" ]; then
echo "Doom Emacs is not installed"
exit 0
fi
echo "Warning: This will completely remove Doom Emacs and all your configuration!"
read -p "Are you sure you want to continue? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Removing Doom Emacs..."
${pkgs.coreutils}/bin/rm -rf "$HOME/.emacs.d"
echo " Doom Emacs has been removed"
else
echo "Removal cancelled"
fi
;;
update)
if [ ! -x "$HOME/.emacs.d/bin/doom" ] || [ ! -f "$HOME/.emacs.d/core/doom.el" ]; then
echo "Error: Doom Emacs is not installed correctly. Run 'zcli doom install' first." >&2
exit 1
fi
echo "Updating Doom Emacs..."
"$HOME/.emacs.d/bin/doom" sync
echo " Doom Emacs update complete"
;;
*)
echo "Error: Invalid doom subcommand '$doom_subcommand'" >&2
echo "Usage: zcli doom [install|status|remove|update]" >&2
exit 1
;;
esac
;;
*)
echo "Error: Invalid command '$1'" >&2
print_help
exit 1
;;
esac
''