Skip to content

Commit 505eb55

Browse files
authored
fix: nvme partition detection, rescue mount, swap fallback, verbose mode
1 parent 93950f9 commit 505eb55

1 file changed

Lines changed: 83 additions & 79 deletions

File tree

nixos-setup.sh

Lines changed: 83 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,173 +1,177 @@
11
#!/usr/bin/env bash
22

3-
# shellcheck disable=SC2034,SC2155,SC2015
4-
5-
set -euo pipefail
3+
set -euxo pipefail
64
shopt -s inherit_errexit 2>/dev/null || true
75

86
readonly SCRIPT_NAME="$(basename "$0")"
97
readonly REQUIRED_CMDS=(parted mkfs.vfat mkfs.btrfs btrfs nixos-generate-config nixos-install nixos-enter)
10-
readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m'
118

12-
# State
139
DISK=""
1410
MODE=""
1511
BOOT_PART=""
1612
ROOT_PART=""
1713

18-
log() { echo -e "${GREEN}[INFO]${NC} $*" >&2; }
19-
warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; }
20-
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
21-
step() { echo -e "${BLUE}[STEP]${NC} $*" >&2; }
22-
2314
cleanup() {
2415
local code=$?
25-
[[ $code -ne 0 ]] && warn "Script exited with code $code"
26-
log "Cleaning up mounts & swap..."
2716
swapoff /mnt/var/swapfile 2>/dev/null || true
2817
umount -R /mnt 2>/dev/null || true
29-
30-
if [[ $code -eq 0 ]]; then
31-
log "[*] Cleanup complete."
32-
else
33-
warn "[!] Cleanup finished (verify mounts manually if needed)."
34-
fi
18+
[[ $code -ne 0 ]] && echo "Script exited with code $code. Verify mounts manually if needed." >&2
3519
}
3620
trap cleanup EXIT
3721

3822
usage() {
39-
echo -e "${RED}Usage:${NC} $SCRIPT_NAME <BLOCK_DEVICE> <install|rescue>"
40-
echo -e " BLOCK_DEVICE Target disk (e.g., /dev/nvme0n1, /dev/sda)"
41-
echo -e " MODE 'install' for fresh setup, 'rescue' to chroot"
42-
echo -e "${YELLOW}No defaults. Both arguments are strictly required.${NC}"
23+
echo "Usage: $SCRIPT_NAME <BLOCK_DEVICE> <install|rescue>"
24+
echo " BLOCK_DEVICE Target disk (e.g., /dev/nvme0n1, /dev/sda)"
25+
echo " MODE 'install' for fresh setup, 'rescue' to chroot"
4326
exit 1
4427
}
4528

4629
check_dependencies() {
4730
for cmd in "${REQUIRED_CMDS[@]}"; do
48-
command -v "$cmd" &>/dev/null || err "Missing required command: $cmd"
31+
command -v "$cmd" &>/dev/null || { echo "Missing required command: $cmd" >&2; exit 1; }
4932
done
5033
}
5134

5235
check_root() {
53-
[[ $EUID -ne 0 ]] && err "This script must be run as root."
36+
[[ $EUID -ne 0 ]] && { echo "This script must be run as root." >&2; exit 1; }
37+
}
38+
39+
set_partition_names() {
40+
local disk="$1"
41+
if [[ "$disk" =~ (nvme|mmcblk)[0-9] ]]; then
42+
BOOT_PART="${disk}p1"
43+
ROOT_PART="${disk}p2"
44+
else
45+
BOOT_PART="${disk}1"
46+
ROOT_PART="${disk}2"
47+
fi
5448
}
5549

5650
parse_args() {
5751
[[ $# -lt 2 ]] && usage
5852
DISK="$1"
5953
MODE="$2"
6054

61-
[[ ! -b "$DISK" ]] && err "'$DISK' is not a valid block device."
62-
[[ "$DISK" =~ [0-9]$ ]] && err "Specify the base disk (e.g., /dev/sda), not a partition."
63-
[[ "$MODE" != "install" && "$MODE" != "rescue" ]] && err "Mode must be 'install' or 'rescue'."
55+
[[ ! -b "$DISK" ]] && { echo "'$DISK' is not a valid block device." >&2; exit 1; }
56+
57+
if [[ "$DISK" =~ nvme[0-9]+n[0-9]+p[0-9]+$ ]] || \
58+
[[ "$DISK" =~ mmcblk[0-9]+p[0-9]+$ ]] || \
59+
[[ "$DISK" =~ [a-z][0-9]+$ && ! "$DISK" =~ nvme && ! "$DISK" =~ mmcblk ]]; then
60+
echo "Specify the base disk (e.g., /dev/nvme0n1, /dev/sda), not a partition." >&2
61+
exit 1
62+
fi
63+
64+
[[ "$MODE" != "install" && "$MODE" != "rescue" ]] && { echo "Mode must be 'install' or 'rescue'." >&2; exit 1; }
65+
66+
set_partition_names "$DISK"
6467
}
6568

6669
validate() {
67-
# Prevent targeting the booted disk
68-
local root_src
70+
local root_src root_disk
6971
root_src=$(findmnt -n -o SOURCE / 2>/dev/null || echo "")
7072
if [[ -n "$root_src" ]]; then
71-
local root_disk
7273
root_disk=$(lsblk -ndo PKNAME "$root_src" 2>/dev/null || echo "")
73-
if [[ "/dev/$root_disk" == "$DISK" ]]; then
74-
err "Refusing to operate on the currently booted disk: $DISK"
75-
fi
74+
[[ "/dev/$root_disk" == "$DISK" ]] && { echo "Refusing to operate on the currently booted disk: $DISK" >&2; exit 1; }
7675
fi
7776

78-
# Check for active mounts
7977
if lsblk -n -o MOUNTPOINTS "$DISK" | grep -qE '\S'; then
80-
err "Disk '$DISK' or its partitions are mounted. Unmount them first."
78+
echo "Disk '$DISK' or its partitions are mounted. Unmount them first." >&2
79+
exit 1
8180
fi
8281

83-
BOOT_PART="${DISK}p1"
84-
ROOT_PART="${DISK}p2"
85-
86-
# Rescue mode expects existing partitions
8782
if [[ "$MODE" == "rescue" ]]; then
88-
[[ ! -b "$BOOT_PART" || ! -b "$ROOT_PART" ]] && err "Rescue mode requires existing: $BOOT_PART and $ROOT_PART"
83+
[[ ! -b "$BOOT_PART" || ! -b "$ROOT_PART" ]] && { echo "Rescue mode requires existing partitions: $BOOT_PART and $ROOT_PART" >&2; exit 1; }
8984
else
90-
# 4. Install mode warns about existing data
91-
if lsblk -n "$DISK" | grep -q "part"; then
92-
warn "Disk '$DISK' contains partitions. ALL DATA WILL BE DESTROYED."
93-
fi
85+
lsblk -n "$DISK" | grep -q "part" && echo "WARNING: '$DISK' contains partitions. ALL DATA WILL BE DESTROYED." >&2
9486
fi
9587

96-
# Explicit confirmation (NO --force bypass)
9788
local prompt
9889
if [[ "$MODE" == "install" ]]; then
99-
prompt="[!] This will WIPE '$DISK' and install NixOS. Type 'yes' to continue: "
90+
prompt="This will WIPE '$DISK' and install NixOS. Type 'yes' to continue: "
10091
else
101-
prompt="[!] Proceed with '$MODE' on '$DISK'? Type 'yes' to continue: "
92+
prompt="Proceed with '$MODE' on '$DISK'? Type 'yes' to continue: "
10293
fi
10394

104-
read -r -p "$(echo -e "${RED}$prompt${NC}")" confirm
105-
[[ "$confirm" != "yes" ]] && { log "Aborted by user."; exit 0; }
95+
read -r -p "$prompt" confirm
96+
[[ "$confirm" != "yes" ]] && { echo "Aborted."; exit 0; }
10697
}
10798

10899
partition_and_format() {
109-
step "Partitioning $DISK..."
100+
echo "Partitioning $DISK..."
110101
parted "$DISK" --script -- \
111102
mklabel gpt \
112103
mkpart ESP fat32 1MiB 513MiB \
113104
set 1 esp on \
114-
mkpart primary 513MiB 100% || err "Partitioning failed"
105+
mkpart primary 513MiB 100% || { echo "Partitioning failed." >&2; exit 1; }
106+
107+
partprobe "$DISK" 2>/dev/null || udevadm settle
115108

116-
step "Formatting partitions..."
117-
mkfs.vfat -F32 -n "NIX-BOOT" "$BOOT_PART" || err "Boot format failed"
118-
mkfs.btrfs -f -L "nix-root" "$ROOT_PART" || err "Root format failed"
109+
echo "Formatting partitions..."
110+
mkfs.vfat -F32 -n "NIX-BOOT" "$BOOT_PART" || { echo "Boot format failed." >&2; exit 1; }
111+
mkfs.btrfs -f -L "nix-root" "$ROOT_PART" || { echo "Root format failed." >&2; exit 1; }
119112
}
120113

121-
mount_system() {
122-
step "Creating Btrfs subvolumes..."
114+
create_subvolumes() {
115+
echo "Creating Btrfs subvolumes..."
123116
mount "$ROOT_PART" /mnt
124117
btrfs subvolume create /mnt/@
125118
btrfs subvolume create /mnt/@home
126119
btrfs subvolume create /mnt/@nix
127120
btrfs subvolume create /mnt/@var
128121
umount /mnt
122+
}
129123

130-
step "Mounting filesystems..."
131-
mount -o subvol=@,noatime,compress=zstd:3 "$ROOT_PART" /mnt
124+
mount_subvolumes() {
125+
echo "Mounting filesystems..."
126+
mount -o subvol=@ "$ROOT_PART" /mnt
132127
mkdir -p /mnt/{home,nix,var,boot}
133-
mount -o subvol=@home,noatime,compress=zstd:3 "$ROOT_PART" /mnt/home
134-
mount -o subvol=@nix,noatime,compress=zstd:3 "$ROOT_PART" /mnt/nix
135-
mount -o subvol=@var,noatime,compress=zstd:3 "$ROOT_PART" /mnt/var
128+
mount -o subvol=@home "$ROOT_PART" /mnt/home
129+
mount -o subvol=@nix "$ROOT_PART" /mnt/nix
130+
mount -o subvol=@var "$ROOT_PART" /mnt/var
136131
mount "$BOOT_PART" /mnt/boot
137132
}
138133

139134
setup_swap() {
140-
step "Creating swapfile..."
141-
btrfs filesystem mkswapfile --size 7G /mnt/var/swapfile || err "Swapfile creation failed"
135+
echo "Creating swapfile..."
136+
if btrfs filesystem mkswapfile --size 7G /mnt/var/swapfile 2>/dev/null; then
137+
:
138+
else
139+
echo "btrfs mkswapfile unavailable, falling back to manual method." >&2
140+
truncate -s 0 /mnt/var/swapfile
141+
chattr +C /mnt/var/swapfile 2>/dev/null || true
142+
fallocate -l 7G /mnt/var/swapfile || dd if=/dev/zero of=/mnt/var/swapfile bs=1M count=7168 status=progress
143+
mkswap /mnt/var/swapfile || { echo "mkswap failed." >&2; exit 1; }
144+
fi
142145
chmod 600 /mnt/var/swapfile
143-
swapon /mnt/var/swapfile || warn "Swap activation failed (continuing without swap)"
146+
swapon /mnt/var/swapfile || echo "Swap activation failed, continuing without swap." >&2
144147
}
145148

146149
run_install() {
147150
partition_and_format
148-
mount_system
151+
create_subvolumes
152+
mount_subvolumes
149153
setup_swap
150154

151-
step "Generating NixOS configuration..."
152-
nixos-generate-config --root /mnt || err "Config generation failed"
155+
echo "Generating NixOS configuration..."
156+
nixos-generate-config --root /mnt || { echo "Config generation failed." >&2; exit 1; }
153157

154-
step "Installing NixOS..."
158+
echo "Installing NixOS..."
155159
if [[ -f flake.nix ]]; then
156-
nixos-install --flake ".#nixos" --no-root-passwd || err "Flake installation failed"
160+
local flake_host
161+
flake_host=$(grep -oP '(?<=hostName = ").*(?=")' /mnt/etc/nixos/configuration.nix 2>/dev/null | head -1 || echo "")
162+
[[ -z "$flake_host" ]] && { echo "Could not detect hostname, defaulting flake target to 'nixos'." >&2; flake_host="nixos"; }
163+
nixos-install --flake ".#${flake_host}" --no-root-passwd || { echo "Flake installation failed." >&2; exit 1; }
157164
else
158-
nixos-install --no-root-passwd || err "Installation failed"
165+
nixos-install --no-root-passwd || { echo "Installation failed." >&2; exit 1; }
159166
fi
160-
log "[*] Installation complete. Reboot when ready."
167+
echo "Installation complete. Reboot when ready."
161168
}
162169

163170
run_rescue() {
164-
mount_system
165-
if [[ -f /mnt/var/swapfile ]]; then
166-
swapon /mnt/var/swapfile 2>/dev/null || warn "Swapfile found but failed to activate"
167-
fi
168-
169-
log "Launching nixos-enter. Type 'exit' to return to host."
170-
nixos-enter || err "nixos-enter failed"
171+
mount_subvolumes
172+
[[ -f /mnt/var/swapfile ]] && { swapon /mnt/var/swapfile 2>/dev/null || echo "Swapfile found but failed to activate." >&2; }
173+
echo "Launching nixos-enter. Type 'exit' to return to host."
174+
nixos-enter || { echo "nixos-enter failed." >&2; exit 1; }
171175
}
172176

173177
main() {
@@ -178,7 +182,7 @@ main() {
178182

179183
case "$MODE" in
180184
install) run_install ;;
181-
rescue) run_rescue ;;
185+
rescue) run_rescue ;;
182186
esac
183187
}
184188

0 commit comments

Comments
 (0)