524 lines
24 KiB
Bash
Executable File
524 lines
24 KiB
Bash
Executable File
#!/usr/bin/env nix-shell
|
|
#!nix-shell -i bash -p gum openssl sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto
|
|
|
|
prerun_action() {
|
|
echo -e "$1"
|
|
SETUP_ANSWER="$(gum input --placeholder 'Type "done" when you have finished.')"
|
|
if [[ "$SETUP_ANSWER" == "done" ]]; then
|
|
:
|
|
else
|
|
echo ' ❌ Aborting - you did not type "done".'
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
necessary_credentials() {
|
|
#TARGET SETTINGS
|
|
echo -e "\n\n ➡️ Please provide the IP address of the target host :"
|
|
TARGET_HOST="$(gum input --placeholder "192.168.1.100")"
|
|
echo -e "\n\n ➡️ Please provide the public SSH key of an authorized device :"
|
|
SSH_PUBLIC_KEY="$(gum input --placeholder "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhcYDmjMo5YApLkk/3P3HZCnOSzm0uYewNAbxL8Fci8 user@your-pc")"
|
|
|
|
# TRAEFIK SETTINGS
|
|
echo -e "\n\n ➡️ Please provide the domain name (FQDN) your home server will use :"
|
|
DOMAIN_NAME="$(gum input --placeholder "yourdomain.com")"
|
|
echo -e "\n\n ➡️ Please provide a valid email address (will be used for ACME, and your services) :"
|
|
EMAIL_ADDRESS="$(gum input --placeholder "myemail@gmail.com")"
|
|
echo -e "\n\n ➡️ Please provide a cloudflare API token with DNS zone permission :"
|
|
CF_DNS_API_TOKEN="$(gum input --placeholder "bA7hdvCOuXGytlNKohi3ZGtlVpf5CHpLuCMiJrE")"
|
|
|
|
# SMTP SETTINGS
|
|
echo -e "\n\n ➡️ Some services will be able to send you emails. For that you need an email that supports sending emails.\n Please provide a valid sender email address :"
|
|
SENDER_EMAIL_ADDRESS="$(gum input --placeholder "myemail@gmail.com")"
|
|
echo -e "\n\n ➡️ Please provide the password of this email address :"
|
|
SENDER_EMAIL_ADDRESS_PASSWORD="$(gum input --placeholder "abcd efgh ijkl mnop")"
|
|
echo -e "\n\n ➡️ Please provide the SMTP server endpoint :"
|
|
SENDER_EMAIL_DOMAIN="$(gum input --placeholder "smtp.gmail.com")"
|
|
echo -e "\n\n ➡️ Please provide the smtp TLS port (for gmail : 587) :"
|
|
SENDER_EMAIL_PORT="$(gum input --placeholder "587")"
|
|
|
|
# NETWORK SETTINGS
|
|
echo -e "\n\n ➡️ Please provide your home network subnet :"
|
|
HOME_ROUTER_SUBNET="$(gum input --placeholder "192.168.1.1/24")"
|
|
echo -e "\n\n ➡️ Please provide the ip address of your router :"
|
|
HOME_ROUTER_IP="$(gum input --placeholder "192.168.1.1")"
|
|
echo -e "\n\n ➡️ Please choose the ip address that your server will use (i.e. any address in the 192.168.1.1/24 range that is not in use.) :"
|
|
HOME_SERVER_IP="$(gum input --placeholder "192.168.1.5")"
|
|
}
|
|
|
|
necessary_credentials_with_config() {
|
|
echo -e "\n\n ➡️ Please choose your configuration file :"
|
|
CONFIG_PATH="$(gum file)"
|
|
|
|
source "$CONFIG_PATH"
|
|
REQUIRED_VARS=(TARGET_HOST SSH_PUBLIC_KEY DOMAIN_NAME EMAIL_ADDRESS CF_DNS_API_TOKEN SENDER_EMAIL_ADDRESS SENDER_EMAIL_ADDRESS_PASSWORD SENDER_EMAIL_DOMAIN SENDER_EMAIL_PORT HOME_ROUTER_SUBNET HOME_ROUTER_IP HOME_SERVER_IP)
|
|
MISSING=0
|
|
for VAR in "${REQUIRED_VARS[@]}"; do
|
|
if [[ -v $VAR && -n ${!VAR} ]]; then
|
|
echo -e "\n ✅ $VAR imported successfully from the config file"
|
|
else
|
|
echo "\n ❌ $VAR is missing or empty"
|
|
MISSING=1
|
|
fi
|
|
done
|
|
|
|
if [[ "$MISSING" == "1" ]]; then
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
setup_ssh() {
|
|
echo -e "\n\n ✅ Generating new SSH for numbus-admin..."
|
|
mkdir -p extra-files/home/numbus-admin/.ssh/
|
|
chmod 700 extra-files/home/numbus-admin/.ssh/
|
|
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-server" -f "extra-files/home/numbus-admin/.ssh/id_ed25519" -N "" -q
|
|
|
|
REMOTE_PASS=$(gum input --password --placeholder "Enter password for 'nixos' on '$TARGET_HOST'")
|
|
if [ -z "$REMOTE_PASS" ]; then
|
|
echo " ❌ Password is required to proceed. Aborting."
|
|
exit 1
|
|
fi
|
|
echo -e "\n\n ➡️ Copying SSH key to target host 'nixos@$TARGET_HOST'..."
|
|
if sshpass -p "$REMOTE_PASS" ssh-copy-id -o StrictHostKeyChecking=no -i "extra-files/home/numbus-admin/.ssh/id_ed25519" "nixos@$TARGET_HOST"; then
|
|
echo " ✅ SSH key copied successfully."
|
|
else
|
|
echo " ❌ Failed to copy SSH key. Please check the host IP and password."
|
|
exit 1
|
|
fi
|
|
export REMOTE_PASS
|
|
}
|
|
|
|
ssh_to_host() {
|
|
ssh -i "extra-files/home/numbus-admin/.ssh/id_ed25519" "nixos@$TARGET_HOST" "$1"
|
|
}
|
|
|
|
hardware_detection() {
|
|
echo -e "\n\n 🔎 Detecting graphics card on target host..."
|
|
VGA_INFO=$(ssh_to_host "lspci -nn | grep -i 'vga'")
|
|
if echo "$VGA_INFO" | grep -iq "intel" 2>/dev/null; then
|
|
echo -e " ✅ Intel graphics card detected."
|
|
TARGET_GRAPHICS="true"
|
|
elif echo "$VGA_INFO" | grep -iq "amd" 2>/dev/null; then
|
|
echo -e " ✅ AMD graphics card detected."
|
|
TARGET_GRAPHICS="true"
|
|
elif echo "$VGA_INFO" | grep -iq "nvidia" 2>/dev/null; then
|
|
echo -e " ✅ NVIDIA graphics card detected."
|
|
TARGET_GRAPHICS="true"
|
|
else
|
|
echo -e " ⚠️ No dedicated graphics card detected."
|
|
TARGET_GRAPHICS="false"
|
|
fi
|
|
echo -e "\n\n 🔎 Detecting transconding acceleration on target host..."
|
|
if ssh_to_host "ls /dev/dri/renderD300" 2>/dev/null; then
|
|
echo -e " ✅ Transcoding capable card detected."
|
|
TARGET_GRAPHICS_RENDERER="true"
|
|
else
|
|
echo -e " ⚠️ No transcoding capable card detected."
|
|
TARGET_GRAPHICS_RENDERER="false"
|
|
fi
|
|
echo -e "\n\n 🔎 Detecting USB Google Coral TPU on target host..."
|
|
if ssh_to_host "lsusb | grep -iq 'google'" 2>/dev/null; then
|
|
echo -e " ✅ USB Google Coral TPU detected."
|
|
TARGET_USB_CORAL="true"
|
|
else
|
|
echo -e " ⚠️ No USB Google Coral TPU detected."
|
|
TARGET_USB_CORAL="false"
|
|
fi
|
|
echo -e "\n\n 🔎 Detecting Zigbee coordinator on target host..."
|
|
if ssh_to_host "ls /dev/serial/by-id/ | grep -i 'zigbee'" 2>/dev/null; then
|
|
echo -e " ✅ Zigbee device found in /dev/serial/by-id/."
|
|
TARGET_ZIGBEE_DEVICE=$(ssh_to_host "ls /dev/serial/by-id/ | grep -i 'zigbee'")
|
|
else
|
|
echo -e " ⚠️ No Zigbee device found."
|
|
TARGET_ZIGBEE_DEVICE=""
|
|
fi
|
|
}
|
|
|
|
services_selection() {
|
|
echo -e "\n\n ➡️ You will now select the services you want installed on your server:"
|
|
|
|
declare -A SERVICE_MAP
|
|
SERVICE_MAP["Pi-Hole: Block ads on all your devices"]="pihole"
|
|
SERVICE_MAP["Home Assistant: Manage your smart home or security cameras"]="hass"
|
|
SERVICE_MAP["Passbolt: Secure password manager with collaboration features"]="passbolt"
|
|
SERVICE_MAP["Frigate [Home Assistant required]: Secure your house with security cameras"]="frigate"
|
|
SERVICE_MAP["Nextcloud: No fuss Office 365 replacement"]="nextcloud"
|
|
SERVICE_MAP["Immich: Pictures and videos backup with local machine-learning"]="immich"
|
|
|
|
mapfile -t SERVICE_DESCRIPTIONS < <(for key in "${!SERVICE_MAP[@]}"; do echo "$key"; done | sort)
|
|
|
|
SELECTED_DESCRIPTIONS_STRING=$(gum choose --no-limit --header "Homelab services:" "${SERVICE_DESCRIPTIONS[@]}")
|
|
|
|
SERVICES=()
|
|
if [[ -n "$SELECTED_DESCRIPTIONS_STRING" ]]; then
|
|
while IFS= read -r line; do
|
|
SERVICES+=("${SERVICE_MAP[$line]}")
|
|
done <<< "$SELECTED_DESCRIPTIONS_STRING"
|
|
fi
|
|
}
|
|
|
|
files_generation() {
|
|
echo -e "\n\n ✅ Generating necessary folder tree..."
|
|
mkdir -p extra-files/var/lib/sops-nix/
|
|
mkdir -p extra-files/etc/nixos/secrets/
|
|
mkdir -p extra-files/mnt/config-storage/traefik/config/conf/
|
|
mkdir -p extra-files/mnt/config-storage/hass/mqtt/config/
|
|
mkdir -p extra-files/mnt/config-storage/hass/mqtt/data/
|
|
mkdir -p extra-files/mnt/data-storage/nextcloud/
|
|
mkdir -p extra-files/mnt/data-storage/immich/
|
|
|
|
echo -e "\n ✅ Generating sops-nix keys..."
|
|
ssh-to-age -private-key -i extra-files/home/numbus-admin/.ssh/id_ed25519 > extra-files/var/lib/sops-nix/key.txt
|
|
SOPS_PUBLIC_KEY=$(age-keygen -y extra-files/var/lib/sops-nix/key.txt)
|
|
|
|
echo -e "\n ✅ Generating sops-nix configuration files..."
|
|
envsubst < config-files/sops-nix/.sops.yaml > extra-files/etc/nixos/.sops.yaml
|
|
|
|
echo -e "\n ✅ Generating secure random database passwords..."
|
|
HOME_ASSISTANT_MQTT_USER=$(openssl rand -base64 29 | tr -d "\123456789=+/" | cut -c1-10)
|
|
HOME_ASSISTANT_MQTT_PASSWORD=$(openssl rand -base64 29 | tr -d "\=+/" | cut -c1-64)
|
|
PASSBOLT_MYSQL_DATABASE=$(openssl rand -base64 29 | tr -d "\123456789=+/" | cut -c1-10)
|
|
PASSBOLT_MYSQL_USER=$(openssl rand -base64 29 | tr -d "\123456789=+/" | cut -c1-10)
|
|
PASSBOLT_MYSQL_PASSWORD=$(openssl rand -base64 29 | tr -d "\=+/" | cut -c1-64)
|
|
FTLCONF_WEBSERVER_PASSWORD=$(openssl rand -base64 29 | tr -d "\=+/" | cut -c1-64)
|
|
DATA_DISK_1=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
DATA_DISK_2=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
DATA_DISK_3=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
DATA_DISK_4=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
DATA_DISK_5=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
DATA_DISK_6=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
PARITY_DISK_1=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
PARITY_DISK_2=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
PARITY_DISK_3=$(openssl rand -base64 300 | tr -d "\=+/" | cut -c1-300)
|
|
|
|
echo -e "\n ✅ Encrypting secrets in the correct file..."
|
|
envsubst < "config-files/sops-nix/secrets.yaml" | sops encrypt --filename-override secrets.yaml \
|
|
--input-type yaml --output-type yaml \
|
|
--age $SOPS_PUBLIC_KEY \
|
|
--output extra-files/etc/nixos/secrets/secrets.yaml
|
|
|
|
echo -e "\n ✅ Writing correct ips to configuration.nix..."
|
|
sed -i s+HOME_SERVER_IP+$HOME_SERVER_IP+g configuration.nix
|
|
sed -i s+HOME_ROUTER_IP+$HOME_ROUTER_IP+g configuration.nix
|
|
|
|
echo -e "\n ✅ Adapting the docker configuration to your hardware..."
|
|
DEVICES_BLOCK=""
|
|
if [[ "$TARGET_GRAPHICS_RENDERER" == "true" ]]; then
|
|
DEVICES_BLOCK+=" - /dev/dri/renderD300:/dev/dri/renderD300\n"
|
|
fi
|
|
if [[ "$TARGET_USB_CORAL" == "true" ]]; then
|
|
DEVICES_BLOCK+=" - /dev/bus/usb:/dev/bus/usb\n"
|
|
fi
|
|
|
|
if [[ -n "$DEVICES_BLOCK" ]]; then
|
|
REPLACEMENT="devices:\n${DEVICES_BLOCK%\\n}"
|
|
sed -i.bak "s|# --- frigate devices --- #|$REPLACEMENT|" docker/frigate.original
|
|
else
|
|
sed -i.bak "/# --- frigate devices --- #/d" docker/frigate.original
|
|
fi
|
|
|
|
if [[ -n "$TARGET_ZIGBEE_DEVICE" ]]; then
|
|
REPLACEMENT="devices:\n - /dev/serial/by-id/${TARGET_ZIGBEE_DEVICE}:/dev/ttyUSB0"
|
|
sed -i.bak "s|# --- hass devices --- #|$REPLACEMENT|" docker/hass.original
|
|
else
|
|
sed -i.bak "/# --- hass devices --- #/d" docker/hass.original
|
|
fi
|
|
|
|
echo -e "\n ✅ Copying configuration files for the selected homelab services..."
|
|
cp docker/traefik.original docker/traefik.nix
|
|
for service in "${SERVICES[@]}"; do
|
|
cp docker/${service}.original docker/${service}.nix
|
|
done
|
|
|
|
echo -e "\n ✅ Writing docker configuration files..."
|
|
envsubst < config-files/docker/traefik/headers.yaml > extra-files/mnt/config-storage/traefik/config/conf/headers.yaml
|
|
envsubst < config-files/docker/traefik/nextcloud.yaml > extra-files/mnt/config-storage/traefik/config/conf/nextcloud.yaml
|
|
envsubst < config-files/docker/traefik/tls.yaml > extra-files/mnt/config-storage/traefik/config/conf/tls.yaml
|
|
envsubst < config-files/docker/traefik/traefik.yaml > extra-files/mnt/config-storage/traefik/config/traefik.yaml
|
|
envsubst < config-files/docker/hass/mosquitto.conf > extra-files/mnt/config-storage/hass/mqtt/config/mosquitto.conf
|
|
touch extra-files/mnt/config-storage/hass/mqtt/config/password.txt
|
|
chmod 0700 extra-files/mnt/config-storage/hass/mqtt/config/password.txt
|
|
mosquitto_passwd -b extra-files/mnt/config-storage/hass/mqtt/config/password.txt $HOME_ASSISTANT_MQTT_USER $HOME_ASSISTANT_MQTT_PASSWORD
|
|
}
|
|
|
|
disk_config_generation() {
|
|
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
|
|
⚠️ $(gum style --foreground 212 'WARNING:') You will choose the disks to install NixOS on.
|
|
!! PLEASE MAKE SURE YOU BACKED UP ANY IMPORTANT DATA !!
|
|
!! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !!
|
|
Please press CTRL+C to abort.
|
|
"
|
|
gum confirm "Do you understand and wish to proceed?" || { echo " ❌ Aborting."; exit 1; }
|
|
|
|
echo -e "\n\n 🔎 Fetching and analyzing disks from target host... (This may take a moment)"
|
|
|
|
declare -A DISK_INFO_MAP
|
|
declare -A DISK_SIZE_MAP
|
|
declare -A DISK_BY_ID_MAP
|
|
declare -A DISK_LABEL_MAP
|
|
DISK_OPTIONS=()
|
|
|
|
DISK_NAMES=$(ssh_to_host "lsblk -d -n -o NAME,TYPE | awk '\$2==\"disk\" {print \$1}'")
|
|
|
|
for name in $DISK_NAMES; do
|
|
details=$(echo "$REMOTE_PASS" | ssh_to_host "
|
|
set -e
|
|
devpath=/dev/$name
|
|
rota=1 # Default to rotational (HDD)
|
|
[ -f /sys/block/$name/queue/rotational ] && rota=\$(cat /sys/block/$name/queue/rotational)
|
|
tran=\$(lsblk -d -n -o TRAN \"\$devpath\" || echo 'unknown')
|
|
health=\$(sudo -S smartctl -H \"\$devpath\" 2>/dev/null | grep 'self-assessment' | awk '{print \$6}')
|
|
by_id=\$(ls -l /dev/disk/by-id | grep -m 1 \"../../$name\$\" | awk '{print \"/dev/disk/by-id/\"\$9}')
|
|
|
|
# Determine type
|
|
if [[ \"\$name\" == nvme* ]]; then type=\"NVMe\"; # Check for NVMe first
|
|
elif [[ \"\$rota\" == \"0\" ]]; then type=\"SSD\";
|
|
elif [[ \"\$tran\" == \"usb\" ]]; then type=\"USB\";
|
|
else type=\"HDD\"; fi
|
|
|
|
# Fallback for health and by-id
|
|
[[ -z \"\$health\" || \"\$health\" == \"\" ]] && health=\"N/A\"
|
|
[ -z \"\$by_id\" ] && by_id=\"\$devpath\"
|
|
|
|
# Get size last, after other commands that might fail
|
|
size=\$(lsblk -b -d -n -o SIZE \"\$devpath\")
|
|
echo \"\$size:::\$type:::\$health:::\$by_id\"
|
|
")
|
|
|
|
mapfile -t parts < <(echo "$details" | tr ':' '\n')
|
|
size="${parts[0]}"
|
|
disk_type="${parts[3]}"
|
|
health="${parts[6]}"
|
|
by_id="${parts[9]}"
|
|
|
|
human_size=$(numfmt --to=iec-i --suffix=B "$size")
|
|
label=$(printf "%-12s %-12s %-12s %-12s %s" "$name" "$disk_type" "$human_size" "$health" "$by_id")
|
|
|
|
DISK_OPTIONS+=("$label")
|
|
DISK_INFO_MAP["$label"]="$name"
|
|
DISK_SIZE_MAP["$name"]="$size"
|
|
DISK_BY_ID_MAP["$name"]="$by_id"
|
|
DISK_LABEL_MAP["$name"]="$label"
|
|
done
|
|
|
|
if [ ${#DISK_OPTIONS[@]} -eq 0 ]; then
|
|
echo " ❌ No disks found on the target host. Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
HEADER=$(printf " %-12s %-12s %-12s %-12s %s" "Device" "Type" "Size" "SMART" "ID")
|
|
gum style --foreground 212 " ➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation:"
|
|
echo -e ""
|
|
mapfile -t SELECTED_BOOT_LABELS < <(gum choose --limit 2 --header "$HEADER" "${DISK_OPTIONS[@]}")
|
|
|
|
if [ ${#SELECTED_BOOT_LABELS[@]} -eq 0 ]; then echo " ❌ No boot disk selected. Aborting."; exit 1; fi
|
|
|
|
BOOT_DISK_1_NAME=${DISK_INFO_MAP["${SELECTED_BOOT_LABELS[0]}"]}
|
|
BOOT_DISK_1=${DISK_BY_ID_MAP[$BOOT_DISK_1_NAME]}
|
|
BOOT_DISK_2=""
|
|
if [ ${#SELECTED_BOOT_LABELS[@]} -eq 2 ]; then
|
|
BOOT_DISK_2_NAME=${DISK_INFO_MAP["${SELECTED_BOOT_LABELS[1]}"]}
|
|
BOOT_DISK_2=${DISK_BY_ID_MAP[$BOOT_DISK_2_NAME]}
|
|
fi
|
|
|
|
REMAINING_DISKS=()
|
|
for label in "${DISK_OPTIONS[@]}"; do
|
|
is_boot_disk=false
|
|
for selected in "${SELECTED_BOOT_LABELS[@]}"; do
|
|
if [[ "$label" == "$selected" ]]; then is_boot_disk=true; break; fi
|
|
done
|
|
if ! $is_boot_disk; then REMAINING_DISKS+=("$label"); fi
|
|
done
|
|
|
|
if [ ${#REMAINING_DISKS[@]} -gt 0 ]; then
|
|
echo -e ""
|
|
gum style --foreground 212 " ➡️ Please choose your data and parity disks (up to 9 total)."
|
|
mapfile -t SELECTED_DATA_LABELS < <(gum choose --limit 9 --header "$HEADER" "${REMAINING_DISKS[@]}")
|
|
|
|
if [ ${#SELECTED_DATA_LABELS[@]} -gt 0 ]; then
|
|
selected_data_names=()
|
|
for label in "${SELECTED_DATA_LABELS[@]}"; do
|
|
selected_data_names+=("${DISK_INFO_MAP[$label]}")
|
|
done
|
|
|
|
num_selected=${#selected_data_names[@]}
|
|
num_parity=0
|
|
if (( num_selected > 0 )); then
|
|
num_parity=$(( (num_selected - 1) / 3 + 1 ))
|
|
fi
|
|
|
|
# Sort selected disks by size (largest first)
|
|
sorted_disks=($(
|
|
for name in "${selected_data_names[@]}"; do
|
|
echo "${DISK_SIZE_MAP[$name]} $name"
|
|
done | sort -rn | awk '{print $2}'
|
|
))
|
|
|
|
# Assign parity disks (the largest ones)
|
|
parity_disks_final=()
|
|
for i in $(seq 0 $((num_parity - 1))); do
|
|
[[ -n "${sorted_disks[$i]}" ]] && parity_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}")
|
|
done
|
|
|
|
# Assign data disks (the remaining ones)
|
|
data_disks_final=()
|
|
for i in $(seq $num_parity $((num_selected - 1))); do
|
|
[[ -n "${sorted_disks[$i]}" ]] && data_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}")
|
|
done
|
|
|
|
# Set exported variables (up to 9 data disks and 2 parity disks)
|
|
for i in {0..8}; do export "DATA_DISK_$((i+1))"="${data_disks_final[$i]:-}"; done
|
|
for i in {0..2}; do export "PARITY_DISK_$((i+1))"="${parity_disks_final[$i]:-}"; done
|
|
fi
|
|
else
|
|
echo -e "\n\n ⚠️ No remaining disks to select for data."
|
|
fi
|
|
|
|
# --- Final Recap ---
|
|
NUMBER_OF_BOOT_DISKS=0
|
|
[[ -n "$BOOT_DISK_1" ]] && NUMBER_OF_BOOT_DISKS=$((NUMBER_OF_BOOT_DISKS + 1))
|
|
[[ -n "$BOOT_DISK_2" ]] && NUMBER_OF_BOOT_DISKS=$((NUMBER_OF_BOOT_DISKS + 1))
|
|
|
|
NUMBER_OF_DATA_DISKS=0
|
|
for i in {1..9}; do
|
|
disk_var="DATA_DISK_$i"
|
|
[[ -n "${!disk_var}" ]] && NUMBER_OF_DATA_DISKS=$((NUMBER_OF_DATA_DISKS + 1))
|
|
done
|
|
|
|
NUMBER_OF_PARITY_DISKS=0
|
|
for i in {1..3}; do
|
|
disk_var="PARITY_DISK_$i"
|
|
[[ -n "${!disk_var}" ]] && NUMBER_OF_PARITY_DISKS=$((NUMBER_OF_PARITY_DISKS + 1))
|
|
done
|
|
|
|
RECAP_CONTENT=$(cat <<EOF
|
|
### Disk Configuration Summary
|
|
|
|
Please review the selected disk layout before proceeding.
|
|
|
|
**Boot Disks ($NUMBER_OF_BOOT_DISKS):**
|
|
* **Boot 1:** \`$BOOT_DISK_1\`
|
|
$( [[ -n "$BOOT_DISK_2" ]] && echo "* **Boot 2:** \`$BOOT_DISK_2\`" || echo "* **Boot 2:** *Not configured*")
|
|
|
|
**Data Disks ($NUMBER_OF_DATA_DISKS):**
|
|
$(for i in {1..9}; do disk_var="DATA_DISK_$i"; [[ -n "${!disk_var}" ]] && echo "* **Data $i:** \`${!disk_var}\`"; done)
|
|
$( [[ $NUMBER_OF_DATA_DISKS -eq 0 ]] && echo "* *Not configured*")
|
|
|
|
**Parity Disks ($NUMBER_OF_PARITY_DISKS):**
|
|
$(for i in {1..3}; do disk_var="PARITY_DISK_$i"; [[ -n "${!disk_var}" ]] && echo "* **Parity $i:** \`${!disk_var}\`"; done)
|
|
$( [[ $NUMBER_OF_PARITY_DISKS -eq 0 ]] && echo "* *Not configured*")
|
|
EOF
|
|
)
|
|
|
|
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "$RECAP_CONTENT")"
|
|
gum confirm "Proceed with this disk configuration?" || { echo " ❌ Aborting as requested."; exit 1; }
|
|
|
|
echo -e "\n\n ⚙️ Generating disko configuration from templates..."
|
|
template_file="config-files/disks/boot-${NUMBER_OF_BOOT_DISKS}.nix"
|
|
(envsubst < "$template_file") > disk-config.nix
|
|
echo -e "\n ✅ Generated boot disk configuration."
|
|
|
|
for i in $(seq 1 $NUMBER_OF_DATA_DISKS); do
|
|
disk_var="DATA_DISK_$i"
|
|
export DISK_NUMBER=$i
|
|
export DISK_PATH=${!disk_var}
|
|
(envsubst < "config-files/disks/data.nix") >> disk-config.nix
|
|
done
|
|
[[ "$NUMBER_OF_DATA_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_DATA_DISKS data disk configuration(s)."
|
|
|
|
for i in $(seq 1 $NUMBER_OF_PARITY_DISKS); do
|
|
disk_var="PARITY_DISK_$i"
|
|
export DISK_NUMBER=$i
|
|
export DISK_PATH=${!disk_var}
|
|
(envsubst < "config-files/disks/parity.nix") >> disk-config.nix
|
|
done
|
|
[[ "$NUMBER_OF_PARITY_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_PARITY_DISKS parity disk configuration(s)."
|
|
|
|
# Close the imports block
|
|
cat <<'EOF' >> disk-config.nix
|
|
};
|
|
};
|
|
}
|
|
EOF
|
|
echo -e "\n ✅ Final disko configuration created at 'disk-config.nix'."
|
|
}
|
|
|
|
deploy() {
|
|
echo -e "\n\n 🔄 Deploying to the remote server..."
|
|
nix run github:nix-community/nixos-anywhere -- \
|
|
--generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
|
|
--flake .#numbus-server \
|
|
--extra-files "extra-files/" \
|
|
--chown "/home/numbus-admin/" 1000:1000 \
|
|
--target-host nixos@$TARGET_HOST
|
|
|
|
echo -e "\n\n ✅ Installation successfull !"
|
|
sleep 1
|
|
}
|
|
|
|
nixos_update() {
|
|
echo -e "\n\n 🔄 Updating NixOS on the remote server..."
|
|
echo "coming soon !"
|
|
}
|
|
|
|
set -euo pipefail
|
|
|
|
cat <<EOF
|
|
██████ █████ ███ ███████ █████████
|
|
░░██████ ░░███ ░░░ ███░░░░░███ ███░░░░░███
|
|
░███░███ ░███ ████ █████ █████ ███ ░░███░███ ░░░
|
|
░███░░███░███ ░░███ ░░███ ░░███ ░███ ░███░░█████████
|
|
░███ ░░██████ ░███ ░░░█████░ ░███ ░███ ░░░░░░░░███
|
|
░███ ░░█████ ░███ ███░░░███ ░░███ ███ ███ ░███
|
|
█████ ░░█████ █████ █████ █████ ░░░███████░ ░░█████████
|
|
░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░
|
|
|
|
|
|
|
|
█████████ █████
|
|
███░░░░░███ ░░███
|
|
░███ ░███ ████████ █████ ████ █████ ███ █████ ░███████ ██████ ████████ ██████
|
|
░███████████ ░░███░░███ ░░███ ░███ ░░███ ░███░░███ ░███░░███ ███░░███░░███░░███ ███░░███
|
|
░███░░░░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░███ ░░░ ░███████
|
|
░███ ░███ ░███ ░███ ░███ ░███ ░░███████████ ░███ ░███ ░███░░░ ░███ ░███░░░
|
|
█████ █████ ████ █████ ░░███████ ░░████░████ ████ █████░░██████ █████ ░░██████
|
|
░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░███ ░░░░ ░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░
|
|
███ ░███
|
|
░░██████
|
|
░░░░░░
|
|
EOF
|
|
|
|
sleep 1
|
|
|
|
# Choose the action
|
|
ACTION_ANSWER=$(gum choose "[1] 🌐 Deploy NixOS on a remote machine" "[2] 💽 Deploy NixOS on a remote machine with a file configuration" "[3] 🛠️ Update a NixOS remote machine")
|
|
echo $ACTION_ANSWER
|
|
|
|
if [[ "$ACTION_ANSWER" == "[1] 🌐 Deploy NixOS on a remote machine" ]]; then
|
|
echo -e "\n ➡️ Proceeding with deployment…"
|
|
prerun_action "\n\n ➡️ On the target host : start the computer and boot into the NixOS iso.\n Launch a console and set up a new user password."
|
|
necessary_credentials
|
|
setup_ssh
|
|
hardware_detection
|
|
services_selection
|
|
files_generation
|
|
disk_config_generation
|
|
deploy
|
|
elif [[ "$ACTION_ANSWER" == "[2] 💽 Deploy NixOS on a remote machine with a file configuration" ]]; then
|
|
echo -e "\n ➡️ Proceeding with deployment using a config file…"
|
|
prerun_action "\n\n ➡️ On the target host : start the computer and boot into the NixOS iso.\n Launch a console and set up a new user password."
|
|
necessary_credentials_with_config
|
|
setup_ssh
|
|
hardware_detection
|
|
services_selection
|
|
files_generation
|
|
disk_config_generation
|
|
deploy
|
|
elif [[ "$ACTION_ANSWER" == "[3] 🛠️ Update a NixOS remote machine" ]]; then
|
|
echo -e "\n ➡️ Proceeding with update…"
|
|
prerun_action "\n\n ➡️ On the target host : make sure the NixOS installation you want to update is up-and-running, accessible with SSH."
|
|
nixos_update
|
|
else
|
|
echo "Aborting - you did not type '1, 2 or 3'."
|
|
exit 1
|
|
fi |