aboutsummaryrefslogtreecommitdiff
path: root/build.sh
blob: 3354094c252325f6fcfed507a5bbb243d76b9789 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#!/bin/bash
# build.sh - Build the custom Arch ZFS installation ISO
# Must be run as root
#
# Uses linux-lts kernel with zfs-dkms from archzfs.com repository.
# DKMS builds ZFS from source, ensuring it always matches the kernel version.

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROFILE_DIR="$SCRIPT_DIR/profile"
WORK_DIR="$SCRIPT_DIR/work"
OUT_DIR="$SCRIPT_DIR/out"
CUSTOM_DIR="$SCRIPT_DIR/custom"

# Live ISO root password (for SSH access during testing/emergencies)
LIVE_ROOT_PASSWORD="archzfs"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

info() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }

# Check root
[[ $EUID -ne 0 ]] && error "This script must be run as root"

# Check dependencies
command -v mkarchiso >/dev/null 2>&1 || {
    info "Installing archiso..."
    pacman -Sy --noconfirm archiso
}

# Clean previous builds
if [[ -d "$WORK_DIR" ]]; then
    warn "Removing previous work directory..."
    rm -rf "$WORK_DIR"
fi

# Always start fresh from releng profile
info "Copying base releng profile..."
rm -rf "$PROFILE_DIR"
cp -r /usr/share/archiso/configs/releng "$PROFILE_DIR"

# Switch from linux to linux-lts
info "Switching to linux-lts kernel..."
sed -i 's/^linux$/linux-lts/' "$PROFILE_DIR/packages.x86_64"
sed -i 's/^linux-headers$/linux-lts-headers/' "$PROFILE_DIR/packages.x86_64"
# broadcom-wl depends on linux, use DKMS version instead
sed -i 's/^broadcom-wl$/broadcom-wl-dkms/' "$PROFILE_DIR/packages.x86_64"

# Update bootloader configs to use linux-lts kernel
info "Updating bootloader configurations for linux-lts..."

# UEFI systemd-boot entries
for entry in "$PROFILE_DIR"/efiboot/loader/entries/*.conf; do
    if [[ -f "$entry" ]]; then
        sed -i 's/vmlinuz-linux/vmlinuz-linux-lts/g' "$entry"
        sed -i 's/initramfs-linux\.img/initramfs-linux-lts.img/g' "$entry"
    fi
done

# BIOS syslinux entries
for cfg in "$PROFILE_DIR"/syslinux/*.cfg; do
    if [[ -f "$cfg" ]]; then
        sed -i 's/vmlinuz-linux/vmlinuz-linux-lts/g' "$cfg"
        sed -i 's/initramfs-linux\.img/initramfs-linux-lts.img/g' "$cfg"
    fi
done

# GRUB config
if [[ -f "$PROFILE_DIR/grub/grub.cfg" ]]; then
    sed -i 's/vmlinuz-linux/vmlinuz-linux-lts/g' "$PROFILE_DIR/grub/grub.cfg"
    sed -i 's/initramfs-linux\.img/initramfs-linux-lts.img/g' "$PROFILE_DIR/grub/grub.cfg"
fi

# Update mkinitcpio preset for linux-lts (archiso uses custom preset)
if [[ -f "$PROFILE_DIR/airootfs/etc/mkinitcpio.d/linux.preset" ]]; then
    # Rename to linux-lts.preset and update paths
    mv "$PROFILE_DIR/airootfs/etc/mkinitcpio.d/linux.preset" \
       "$PROFILE_DIR/airootfs/etc/mkinitcpio.d/linux-lts.preset"
    sed -i 's/vmlinuz-linux/vmlinuz-linux-lts/g' \
        "$PROFILE_DIR/airootfs/etc/mkinitcpio.d/linux-lts.preset"
    sed -i 's/initramfs-linux/initramfs-linux-lts/g' \
        "$PROFILE_DIR/airootfs/etc/mkinitcpio.d/linux-lts.preset"
    sed -i "s/'linux' package/'linux-lts' package/g" \
        "$PROFILE_DIR/airootfs/etc/mkinitcpio.d/linux-lts.preset"
fi

# Add archzfs repository to pacman.conf
info "Adding archzfs repository..."
cat >> "$PROFILE_DIR/pacman.conf" << 'EOF'

[archzfs]
Server = https://archzfs.com/$repo/$arch
SigLevel = Optional TrustAll
EOF

# Add ZFS and our custom packages
info "Adding ZFS and custom packages..."
cat >> "$PROFILE_DIR/packages.x86_64" << 'EOF'

# ZFS support (DKMS builds from source - always matches kernel)
zfs-dkms
zfs-utils
linux-lts-headers

# Additional networking
wget
networkmanager

# Development tools for Claude Code
nodejs
npm
jq

# Additional utilities
zsh
htop
ripgrep
eza
fd
fzf
emacs

# For installation scripts
dialog

# Rescue/Recovery tools
tealdeer
pv
rsync
mbuffer
lsof

# Data recovery
ddrescue
testdisk
foremost
sleuthkit
safecopy
smartmontools

# Boot repair
os-prober
syslinux
ms-sys

# Windows recovery
chntpw
ntfs-3g
dislocker
hivex

# Hardware diagnostics
memtester
stress-ng
lm_sensors
lshw
dmidecode
nvme-cli
hdparm
iotop

# Disk operations
partclone
fsarchiver
partimage
nwipe
xfsprogs
btrfs-progs
f2fs-tools
exfatprogs
ncdu
tree

# Network diagnostics
mtr
iperf3
iftop
nethogs
ethtool
tcpdump
bind
nmap
wireshark-cli
speedtest-cli
mosh
aria2
tmate
sshuttle

# Security
pass

EOF

# Get kernel version for ISO naming
info "Querying kernel version..."
KERNEL_VER=$(pacman -Si linux-lts 2>/dev/null | grep "^Version" | awk '{print $3}' | cut -d- -f1)
if [[ -z "$KERNEL_VER" ]]; then
    KERNEL_VER="unknown"
    warn "Could not determine kernel version, using 'unknown'"
fi
info "LTS Kernel version: $KERNEL_VER"

# Update profiledef.sh with our ISO name
info "Updating ISO metadata..."
# Format: archzfs-vmlinuz-6.12.65-lts-2026-01-18-x86_64.iso
ISO_DATE=$(date +%Y-%m-%d)
sed -i "s/^iso_name=.*/iso_name=\"archzfs-vmlinuz-${KERNEL_VER}-lts\"/" "$PROFILE_DIR/profiledef.sh"
sed -i "s/^iso_version=.*/iso_version=\"${ISO_DATE}\"/" "$PROFILE_DIR/profiledef.sh"

# Create airootfs directories
mkdir -p "$PROFILE_DIR/airootfs/usr/local/bin"
mkdir -p "$PROFILE_DIR/airootfs/code"
mkdir -p "$PROFILE_DIR/airootfs/etc/systemd/system/multi-user.target.wants"

# Enable SSH on live ISO
info "Enabling SSH on live ISO..."
ln -sf /usr/lib/systemd/system/sshd.service \
    "$PROFILE_DIR/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service"

# Set root password for live ISO
info "Setting root password for live ISO..."
mkdir -p "$PROFILE_DIR/airootfs/etc"
# Generate password hash
PASS_HASH=$(openssl passwd -6 "$LIVE_ROOT_PASSWORD")
# Create shadow file entry (will be merged with existing)
cat > "$PROFILE_DIR/airootfs/etc/shadow" << EOF
root:${PASS_HASH}:19000:0:99999:7:::
EOF
chmod 400 "$PROFILE_DIR/airootfs/etc/shadow"

# Allow root SSH login with password (for testing)
mkdir -p "$PROFILE_DIR/airootfs/etc/ssh/sshd_config.d"
cat > "$PROFILE_DIR/airootfs/etc/ssh/sshd_config.d/allow-root.conf" << 'EOF'
PermitRootLogin yes
PasswordAuthentication yes
EOF

# Copy our custom scripts
info "Copying custom scripts..."
cp "$CUSTOM_DIR/install-archzfs" "$PROFILE_DIR/airootfs/usr/local/bin/"
cp "$CUSTOM_DIR/install-claude" "$PROFILE_DIR/airootfs/usr/local/bin/"
cp "$CUSTOM_DIR/archsetup-zfs" "$PROFILE_DIR/airootfs/usr/local/bin/"

# Copy grub-zfs-snap for ZFS snapshot boot entries
info "Copying grub-zfs-snap..."
cp "$CUSTOM_DIR/grub-zfs-snap" "$PROFILE_DIR/airootfs/usr/local/bin/"
mkdir -p "$PROFILE_DIR/airootfs/usr/local/share/grub-zfs-snap"
cp "$CUSTOM_DIR/40_zfs_snapshots" "$PROFILE_DIR/airootfs/usr/local/share/grub-zfs-snap/"
cp "$CUSTOM_DIR/zz-grub-zfs-snap.hook" "$PROFILE_DIR/airootfs/usr/local/share/grub-zfs-snap/"

# Copy zfs-snap-prune for snapshot retention
info "Copying zfs-snap-prune..."
cp "$CUSTOM_DIR/zfs-snap-prune" "$PROFILE_DIR/airootfs/usr/local/bin/"

# Copy example config for unattended installs
mkdir -p "$PROFILE_DIR/airootfs/root"
cp "$CUSTOM_DIR/install-archzfs.conf.example" "$PROFILE_DIR/airootfs/root/"

# Copy rescue guide
info "Copying rescue guide..."
cp "$CUSTOM_DIR/RESCUE-GUIDE.txt" "$PROFILE_DIR/airootfs/root/"

# Set permissions in profiledef.sh
info "Setting file permissions..."
if grep -q "file_permissions=" "$PROFILE_DIR/profiledef.sh"; then
    sed -i '/^file_permissions=(/,/)/ {
        /)/ i\  ["/usr/local/bin/install-archzfs"]="0:0:755"
    }' "$PROFILE_DIR/profiledef.sh"
    sed -i '/^file_permissions=(/,/)/ {
        /)/ i\  ["/usr/local/bin/install-claude"]="0:0:755"
    }' "$PROFILE_DIR/profiledef.sh"
    sed -i '/^file_permissions=(/,/)/ {
        /)/ i\  ["/usr/local/bin/archsetup-zfs"]="0:0:755"
    }' "$PROFILE_DIR/profiledef.sh"
    sed -i '/^file_permissions=(/,/)/ {
        /)/ i\  ["/usr/local/bin/grub-zfs-snap"]="0:0:755"
    }' "$PROFILE_DIR/profiledef.sh"
    sed -i '/^file_permissions=(/,/)/ {
        /)/ i\  ["/usr/local/bin/zfs-snap-prune"]="0:0:755"
    }' "$PROFILE_DIR/profiledef.sh"
    sed -i '/^file_permissions=(/,/)/ {
        /)/ i\  ["/etc/shadow"]="0:0:400"
    }' "$PROFILE_DIR/profiledef.sh"
fi

# Copy archsetup into airootfs
if [[ -d /home/cjennings/code/archsetup ]]; then
    info "Copying archsetup into ISO..."
    cp -r /home/cjennings/code/archsetup "$PROFILE_DIR/airootfs/code/"
    rm -rf "$PROFILE_DIR/airootfs/code/archsetup/.git"
    rm -rf "$PROFILE_DIR/airootfs/code/archsetup/.claude"
fi

# Pre-populate tealdeer (tldr) cache for offline use
info "Pre-populating tealdeer cache..."
if command -v tldr &>/dev/null; then
    tldr --update 2>/dev/null || true
    if [[ -d "$HOME/.cache/tealdeer" ]]; then
        mkdir -p "$PROFILE_DIR/airootfs/root/.cache"
        cp -r "$HOME/.cache/tealdeer" "$PROFILE_DIR/airootfs/root/.cache/"
        info "Tealdeer cache copied (~27MB)"
    fi
else
    warn "tealdeer not installed on build host, skipping cache pre-population"
    warn "Install with: pacman -S tealdeer && tldr --update"
fi

# Ensure scripts are executable in the profile
chmod +x "$PROFILE_DIR/airootfs/usr/local/bin/"*

# Build the ISO
info "Building ISO (this will take a while)..."
mkarchiso -v -w "$WORK_DIR" -o "$OUT_DIR" "$PROFILE_DIR"

# Report results
ISO_FILE=$(ls -t "$OUT_DIR"/*.iso 2>/dev/null | head -1)
if [[ -f "$ISO_FILE" ]]; then
    echo ""
    info "Build complete!"
    info "ISO location: $ISO_FILE"
    info "ISO size: $(du -h "$ISO_FILE" | cut -f1)"
    echo ""
    info "To test: ./scripts/test-vm.sh"
    echo ""
    info "After booting:"
    echo "  - ZFS is pre-loaded (no setup needed)"
    echo "  - SSH is enabled (root password: $LIVE_ROOT_PASSWORD)"
    echo "  - Run 'install-archzfs' to start installation"
    echo ""
    info "SSH access (from host):"
    echo "  ssh -p 2222 root@localhost"
else
    error "Build failed - no ISO file found"
fi