Huge update to the disk selection method. Now support striped or mirrored boot disk(s), and up to 9 disks for data with automatic inclusion of up to 3 parity disks.

This commit is contained in:
Raphael Numbus
2025-11-23 19:36:37 +01:00
parent 6827785db7
commit 8601bdad7f
9 changed files with 320 additions and 587 deletions
+2 -1
View File
@@ -1,3 +1,4 @@
agents/
ai-production/
extra-files/
extra-files/
test.sh
-110
View File
@@ -1,110 +0,0 @@
{ lib, ... }:
{
disko.devices = {
disk = {
# Boot disk
system = {
type = "disk";
device = "${BOOT_DISK_1}";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
settings = {
allowDiscards = true;
};
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
# First data disk
data1 = {
type = "disk";
device = "${DATA_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-1";
keyFile = "/run/secrets/disks/data-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-1";
};
};
};
};
};
};
# Parity disk
parity1 = {
type = "disk";
device = "${PARITY_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-parity-1";
keyFile = "/run/secrets/disks/parity-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/parity-1";
};
};
};
};
};
};
};
# Boot disk LVM configuration
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
swap = {
size = "8G";
content.type = "swap";
};
};
};
};
};
}
-134
View File
@@ -1,134 +0,0 @@
{ lib, ... }:
{
disko.devices = {
disk = {
# Boot disk
system = {
type = "disk";
device = "${BOOT_DISK_1}";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
settings = {
allowDiscards = true;
};
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
# First data disk
data1 = {
type = "disk";
device = "${DATA_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-1";
keyFile = "/run/secrets/disks/data-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-1";
};
};
};
};
};
};
# Second data disk
data2 = {
type = "disk";
device = "${DATA_DISK_2}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-2";
keyFile = "/run/secrets/disks/data-disk-2";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-2";
};
};
};
};
};
};
# Parity disk
parity1 = {
type = "disk";
device = "${PARITY_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-parity-1";
keyFile = "/run/secrets/disks/parity-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/parity-1";
};
};
};
};
};
};
};
# Boot disk LVM configuration
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
swap = {
size = "8G";
content.type = "swap";
};
};
};
};
};
}
-158
View File
@@ -1,158 +0,0 @@
{ lib, ... }:
{
disko.devices = {
disk = {
# Boot disk
system = {
type = "disk";
device = "${BOOT_DISK_1}";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
settings = {
allowDiscards = true;
};
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
# First data disk
data1 = {
type = "disk";
device = "${DATA_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-1";
keyFile = "/run/secrets/disks/data-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-1";
};
};
};
};
};
};
# Second data disk
data2 = {
type = "disk";
device = "${DATA_DISK_2}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-2";
keyFile = "/run/secrets/disks/data-disk-2";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-2";
};
};
};
};
};
};
# Third data disk
data3 = {
type = "disk";
device = "${DATA_DISK_3}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-3";
keyFile = "/run/secrets/disks/data-disk-3";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-3";
};
};
};
};
};
};
# Parity disk
parity1 = {
type = "disk";
device = "${PARITY_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-parity-1";
keyFile = "/run/secrets/disks/parity-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/parity-1";
};
};
};
};
};
};
};
# Boot disk LVM configuration
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
swap = {
size = "8G";
content.type = "swap";
};
};
};
};
};
}
@@ -1,9 +1,27 @@
{ lib, ... }:
{
disko.devices = {
# Boot disk LVM configuration
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content.type = "filesystem";
content.format = "btrfs";
content.mountpoint = "/";
};
swap = {
size = "16G";
content.type = "swap";
};
};
};
};
disk = {
# Boot disk
system = {
system-1 = {
type = "disk";
device = "${BOOT_DISK_1}";
content = {
@@ -35,28 +53,4 @@
};
};
};
};
};
# Boot disk LVM configuration
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
swap = {
size = "8G";
content.type = "swap";
};
};
};
};
};
}
};
@@ -2,8 +2,7 @@
{
disko.devices = {
disk = {
# Boot disk
system = {
system-1 = {
type = "disk";
device = "${BOOT_DISK_1}";
content = {
@@ -19,68 +18,56 @@
mountOptions = [ "umask=0077" ];
};
};
luks = {
crypt_p1 = {
size = "100%";
content = {
type = "luks";
name = "crypted";
name = "nixos-p1";
settings = {
allowDiscards = true;
};
};
};
};
};
};
system-2 = {
type = "disk";
device = "${BOOT_DISK_2}";
content = {
type = "gpt";
partitions = {
crypt_p2 = {
size = "100%";
content = {
type = "luks";
name = "nixos-p2";
settings = {
allowDiscards = true;
};
content = {
type = "lvm_pv";
vg = "pool";
type = "btrfs";
extraArgs = [
"-d raid1"
"/dev/mapper/nixos-p1"
];
subvolumes = {
"/root" = {
mountpoint = "/";
mountOptions = [
"rw"
"relatime"
"ssd"
];
};
"/swap" = {
mountpoint = "none";
swap.size = "16G";
};
};
};
};
};
};
};
};
# First data disk
data1 = {
type = "disk";
device = "${DATA_DISK_1}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-1";
keyFile = "/run/secrets/disks/data-disk-1";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-1";
};
};
};
};
};
};
};
# Boot disk LVM configuration
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
swap = {
size = "8G";
content.type = "swap";
};
};
};
};
};
}
+22
View File
@@ -0,0 +1,22 @@
data-${DISK_NUMBER} = {
type = "disk";
device = "${DISK_PATH}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-data-${DISK_NUMBER}";
keyFile = "/run/secrets/disks/data-disk-${DISK_NUMBER}";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/data-${DISK_NUMBER}";
};
};
};
};
};
};
+22
View File
@@ -0,0 +1,22 @@
parity-${DISK_NUMBER} = {
type = "disk";
device = "${DISK_PATH}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted-parity-${DISK_NUMBER}";
keyFile = "/run/secrets/disks/parity-disk-${DISK_NUMBER}";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/mnt/parity-${DISK_NUMBER}";
};
};
};
};
};
};
+213 -104
View File
@@ -1,11 +1,13 @@
#!/bin/bash
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p gum openssl sops ssh-to-age age sshpass envsubst
prerun_action() {
echo -e "$1"
SETUP_ANSWER="$(gum input --placeholder "Type 'done' when you have finished.")"
SETUP_ANSWER="$(gum input --placeholder 'Type "done" when you have finished.')"
if [[ "$SETUP_ANSWER" == "done" ]]; then
:
else
echo " Aborting - you did not type 'done'."
echo ' ❌ Aborting - you did not type "done".'
exit 1
fi
}
@@ -65,14 +67,27 @@ necessary_credentials_with_config() {
fi
}
setup_ssh() {
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.pub" "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.pub" "nixos@$TARGET_HOST" "$1"
}
hardware_detection() {
echo -e "\n\n ➡️ Please provide the password of the target host :"
ssh-copy-id -i extra-files/home/numbus-admin/.ssh/id_ed25519.pub nixos@$TARGET_HOST
ssh_to_host() {
ssh -i extra-files/home/numbus-admin/.ssh/id_ed25519 nixos@$TARGET_HOST "$1"
}
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"; then
@@ -127,11 +142,11 @@ files_generation() {
mkdir -p extra-files/mnt/data-storage/immich
echo -e "\n\n ✅ Generating new SSH for numbus-admin..."
ssh-keygen -t ed25519 -C numbus-admin@numbus-server -f extra-files/home/numbus-admin/.ssh/id_ed25519 -N "" -q
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-server" -f "extra-files/home/numbus-admin/.ssh/id_ed25519" -N "" -q
echo -e "\n\n ✅ Generating sops-nix keys..."
nix run nixpkgs#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=$(nix shell nixpkgs#age -c age-keygen -y extra-files/var/lib/sops-nix/key.txt)
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\n ✅ Generating sops-nix configuration files..."
envsubst < config-files/sops-nix/.sops.yaml > extra-files/etc/nixos/.sops.yaml
@@ -230,103 +245,205 @@ files_generation() {
}
disk_config_generation() {
ssh_to_host() {
ssh -i extra-files/home/numbus-admin/.ssh/id_ed25519 nixos@$TARGET_HOST "$1"
}
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 ⚠️ WARNING: you will choose the disks you want to install NixOS on."
echo -e " !! PLEASE MAKE SURE YOU BACKED UP ANY IMPORTANT DATA !!"
echo -e " !! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !!"
echo -e " Press CTRL+C to abort."
echo -e "\n\n 🔎 Fetching and analyzing disks from target host... (This may take a moment)"
SETUP_ANSWER="$(gum input --placeholder "Type 'understood' once you have read the warning.")"
if [[ "$SETUP_ANSWER" == "understood" ]]; then
:
else
echo " Aborting - you did not type 'understood'."
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
echo -e "\n\n 🔎 Fetching disks from target host..."
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[@]}")
DISK_JSON=$(ssh_to_host "lsblk -d --json -o NAME,ROTA,SIZE,PATH")
BY_ID_RAW=$(ssh_to_host "ls -l /dev/disk/by-id/")
if [ -z "$DISK_JSON" ]; then
echo " ❌ Could not find any disks on the target host. Aborting."
exit 1
fi
if [ ${#SELECTED_BOOT_LABELS[@]} -eq 0 ]; then echo " ❌ No boot disk selected. Aborting."; exit 1; fi
declare -A BY_ID_MAP
while read -r line; do
if [[ "$line" =~ "-> ../../"(.*)$ ]]; then
dev_name="${BASH_REMATCH[1]}"
by_id_path="/dev/disk/by-id/$(echo "$line" | awk '{print $9}')"
if [[ ! -v "BY_ID_MAP[$dev_name]" && ! "$by_id_path" =~ -part ]]; then
BY_ID_MAP["$dev_name"]="$by_id_path"
fi
fi
done <<< "$BY_ID_RAW"
declare -A DISK_MAP
declare -a DISK_OPTIONS
while read -r name type size; do
by_id=${BY_ID_MAP[$name]}
if [ -z "$by_id" ]; then continue; fi
option=$(printf "%-8s %-5s %-8s (%s)" "$name" "$type" "$size" "$by_id")
DISK_OPTIONS+=("$option")
DISK_MAP["$option"]="$by_id"
done < <(echo "$DISK_JSON" | jq -r '.blockdevices[] | "\(.name) \(if .name | test("^nvme") then "NVMe" else (if .rota == "0" then "SSD" else "HDD" end) end) \(.size)"')
echo -e "\n\n ➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation:"
mapfile -t SELECTED_BOOT_OPTIONS < <(gum choose --limit 2 "${DISK_OPTIONS[@]}")
if [ ${#SELECTED_BOOT_OPTIONS[@]} -eq 0 ]; then
echo " ❌ No boot disk selected. Aborting."
exit 1
fi
NUMBER_OF_BOOT_DISKS=${#SELECTED_BOOT_OPTIONS[@]}
BOOT_DISK_1=${DISK_MAP["${SELECTED_BOOT_OPTIONS[0]}"]}
if [ "$NUMBER_OF_BOOT_DISKS" -eq 2 ]; then
BOOT_DISK_2=${DISK_MAP["${SELECTED_BOOT_OPTIONS[1]}"]}
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 option in "${DISK_OPTIONS[@]}"; do
for label in "${DISK_OPTIONS[@]}"; do
is_boot_disk=false
for selected in "${SELECTED_BOOT_OPTIONS[@]}"; do
if [[ "$option" == "$selected" ]]; then
is_boot_disk=true
break
fi
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+=("$option")
fi
if ! $is_boot_disk; then REMAINING_DISKS+=("$label"); fi
done
if [ ${#REMAINING_DISKS[@]} -gt 0 ]; then
echo -e "\n\n ➡️ Please choose your data disks (up to 4):"
mapfile -t SELECTED_DATA_OPTIONS < <(gum choose --limit 4 "${REMAINING_DISKS[@]}")
NUMBER_OF_DATA_DISKS=${#SELECTED_DATA_OPTIONS[@]}
for i in $(seq 0 $(($NUMBER_OF_DATA_DISKS - 1))); do
declare "DATA_DISK_$(($i + 1))"="${DISK_MAP["${SELECTED_DATA_OPTIONS[$i]}"]}"
done
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 available for data storage."
NUMBER_OF_DATA_DISKS=0
echo -e "\n️ No remaining disks to select for data."
fi
DISK_CONFIG_TEMPLATE="config-files/disks/boot-${NUMBER_OF_BOOT_DISKS}-data-${NUMBER_OF_DATA_DISKS}.nix"
# --- 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))
if [[ -f "$DISK_CONFIG_TEMPLATE" ]]; then
echo -e "\n\n ✅ Generating disk configuration from template: $DISK_CONFIG_TEMPLATE"
envsubst < "$DISK_CONFIG_TEMPLATE" > disk-config.nix
else
echo -e "\n\n ❌ Error: No disk configuration template found for $NUMBER_OF_BOOT_DISKS boot disk(s) and $NUMBER_OF_DATA_DISKS data disk(s)."
echo " Looked for: $DISK_CONFIG_TEMPLATE"
exit 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 " ✅ 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 " ✅ 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 " ✅ Generated $NUMBER_OF_PARITY_DISKS parity disk configuration(s)."
# Close the imports block
cat <<'EOF' >> disk-config.nix
};
};
}
EOF
echo -e " ✅ Final disko configuration created at 'disk-config.nix'."
}
deploy() {
@@ -338,7 +455,7 @@ deploy() {
--chown "/home/numbus-admin/" 1000:1000 \
--target-host nixos@$TARGET_HOST
echo -e "\n\n ✅ Installation successfull !!"
echo -e "\n\n ✅ Installation successfull !"
sleep 1
}
@@ -375,18 +492,6 @@ EOF
sleep 1
# Pre-run checks
if ! command -v gum &> /dev/null; then
echo " ❌ 'gum' is not installed. Please install it to use the interactive TUI."
echo " Add 'gum' to your configuration.nix in the system packages section."
exit 1
fi
if ! command -v openssl &> /dev/null; then
echo " ❌ 'openssl' is not installed."
echo " Add 'openssl' to your configuration.nix in the system packages section."
exit 1
fi
# 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
@@ -395,16 +500,20 @@ 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
files_generation
disk_config_generation
generate_disko_config
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
files_generation
disk_config_generation
generate_disko_config
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."