nixcfg/packages/nixos-hosts/nixos-hosts.sh
2024-03-21 15:00:36 +01:00

325 lines
6.1 KiB
Bash

#!/usr/bin/env bash
#==============================#
# Global #
#==============================#
DEBUG=${DEBUG:-"false"}
#==============================#
# Injected #
#==============================#
hosts="@hosts@"
help_root="@help@"
#==============================#
# Logging #
#==============================#
text_reset="\e[m"
text_bold="\e[1m"
text_dim="\e[2m"
text_italic="\e[3m"
text_underline="\e[4m"
text_blink="\e[5m"
text_highlight="\e[7m"
text_hidden="\e[8m"
text_strike="\e[9m"
text_fg_red="\e[38;5;1m"
text_fg_green="\e[38;5;2m"
text_fg_yellow="\e[38;5;3m"
text_fg_blue="\e[38;5;4m"
text_fg_magenta="\e[38;5;5m"
text_fg_cyan="\e[38;5;6m"
text_fg_white="\e[38;5;7m"
text_fg_dim="\e[38;5;8m"
text_bg_red="\e[48;5;1m"
text_bg_green="\e[48;5;2m"
text_bg_yellow="\e[48;5;3m"
text_bg_blue="\e[48;5;4m"
text_bg_magenta="\e[48;5;5m"
text_bg_cyan="\e[48;5;6m"
text_bg_white="\e[48;5;7m"
text_bg_dim="\e[48;5;8m"
# Usage: log_info <message>
log_info() {
echo -e "${text_fg_blue}info${text_reset} $1"
}
# Usage: log_todo <message>
log_todo() {
echo -e "${text_bg_magenta}${text_fg_white}todo${text_reset} $1"
}
# Usage: log_debug <message>
log_debug() {
if [[ $DEBUG == true ]]; then
echo -e "${text_fg_dim}debug${text_reset} $1"
fi
}
# Usage: log_warn <message>
log_warn() {
echo -e "${text_fg_yellow}warn${text_reset} $1"
}
# Usage: log_error <message>
log_error() {
echo -e "${text_fg_red}error${text_reset} $1"
}
# Usage: log_fatal <message> [exit-code]
log_fatal() {
echo -e "${text_fg_white}${text_bg_red}fatal${text_reset} $1"
if [ -z ${2:-} ]; then
exit 1
else
exit $2
fi
}
# Usage: clear_previous_line [number]
clear_line() {
echo -e "\e[${1:-"1"}A\e[2K"
}
# Usage:
# rewrite_line <message>
# rewrite_line <number> <message>
rewrite_line() {
if [[ $# == 1 ]]; then
echo -e "\e[1A\e[2K${1}"
else
echo -e "\e[${1}A\e[2K${2}"
fi
}
#==============================#
# Options #
#==============================#
positional_args=()
opt_help=false
opt_pick=false
opt_list=false
# Usage: missing_value <option>
missing_opt_value() {
log_fatal "Option $1 requires a value"
}
# shellcheck disable=SC2154
while [[ $# > 0 ]]; do
case "$1" in
-h|--help)
opt_help=true
shift
;;
-p|--pick)
opt_pick=true
shift
;;
-l|--list)
opt_list=true
shift
;;
--show-trace)
opt_show_trace=true
shift
;;
--debug)
DEBUG=true
shift
;;
--)
shift
break
;;
-*|--*)
echo "Unknown option $1"
exit 1
;;
*)
positional_args+=("$1")
shift
;;
esac
done
passthrough_args=($@)
show_trace=""
#==============================#
# Helpers #
#==============================#
# Usage: split <string> <delimiter>
split() {
IFS=$'\n' read -d "" -ra arr <<< "${1//$2/$'\n'}"
printf '%s\n' "${arr[@]}"
}
# Usage: lstrip <string> <pattern>
lstrip() {
printf '%s\n' "${1##$2}"
}
# Usage: show_help <path>
show_help() {
log_debug "Showing help: ${text_bold}$1${text_reset}"
source "${help_root}/$1.sh"
}
# Usage: require_flake_nix
require_flake_nix() {
if ! [ -f ./flake.nix ]; then
log_fatal "This command requires a flake.nix file, but one was not found in the current directory."
fi
}
# Usage: get_flake_attributes <output> <flake-uri>
get_flake_attributes() {
local outputs=""
# NOTE: Some flakes may not contain the values we're looking for. In
# which case, we swallow errors here to keep the output clean.
set +e
outputs=$(nix eval --impure --raw --expr "\
let
flake = builtins.getFlake \"$2\";
outputs =
if builtins.elem \"$1\" [ \"packages\" \"devShells\" \"apps\" ] then
builtins.attrNames (flake.\"$1\".\${builtins.currentSystem} or {})
else
builtins.attrNames (flake.\"$1\" or {});
names = builtins.map (output: \"$2#\" + output) outputs;
in
builtins.concatStringsSep \" \" names
" 2>/dev/null)
set -e
echo "$outputs"
}
# Usage: replace_each <pattern> <text> <data>
replace_each() {
local results=()
for item in $3; do
if [[ "$item" == $1* ]]; then
results+=("$2$(lstrip $item $1)")
else
results+=("$item")
fi
done
echo "${results[*]}"
}
# Usage: prefix_each <prefix> <data>
prefix_each() {
local results=()
for item in $2; do
results+=("$1 $item")
done
echo "${results[*]}"
}
# Usage: join_by <delimiter> <data> <data> [...<data>]
join_by() {
local d=${1-} f=${2-}
if shift 2; then
printf %s "$f" "${@/#/$d}"
fi
}
# Usage: privileged <command>
privileged() {
cmd="$@"
if command -v sudo >/dev/null; then
log_debug "sudo $cmd"
sudo $cmd
elif command -v doas >/dev/null; then
log_debug "doas $cmd"
doas $cmd
else
log_warn "Could not find ${text_bold}sudo${text_reset} or ${text_bold}doas${text_reset}. Executing without privileges."
log_debug "$cmd"
eval "$cmd"
fi
}
#==============================#
# Commands #
#==============================#
nixos_hosts_all() {
if [[ $opt_help == true ]]; then
show_help nixos-hosts
exit 0
fi
if [[ -z "${hosts}" ]]; then
log_error "To use this program, override it with your nixosConfigurations:"
log_error ""
log_error " nixos-hosts.override {"
log_error " inherit (self) nixosConfigurations;"
log_error " }"
log_error ""
log_fatal "No hosts configured."
fi
if [ "${opt_list}" == "true" ]; then
local csv=$(cat "${hosts}")
local csv_data=$(echo -n "$csv" | tail +2)
while read -r line; do
local name=$(split "${line}" "," | head -n 1)
echo "${name}"
done <<< "${csv_data}"
exit 0
fi
host=$(gum table --selected.foreground="4" --widths=32,20 < "${hosts}")
if [ -z "${host}" ]; then
exit 0
else
local name=$(split "${host}" "," | head -n 1)
echo $name
fi
}
#==============================#
# Execute #
#==============================#
if [ -z "${positional_args:-}" ]; then
if [[ $opt_help == true ]]; then
show_help "nixos-hosts"
exit 0
else
log_debug "Running subcommand: ${text_bold}nixos_hosts_all${text_reset}"
nixos_hosts_all
exit 0
fi
fi
case ${positional_args[0]} in
*)
log_fatal "Unknown subcommand: ${text_bold}${positional_args[0]}${text_reset}"
;;
esac