aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-27 13:20:30 -0500
committerCraig Jennings <c@cjennings.net>2026-04-27 13:20:30 -0500
commit6dcdf180289823fceda376c67af9d4ea659463a8 (patch)
tree28ddf17a13f70c7a05abe9a196888f3f66512a2a
parent26f3f823ac17940a1b0153619f6140f45d856e33 (diff)
downloadarchangel-6dcdf180289823fceda376c67af9d4ea659463a8.tar.gz
archangel-6dcdf180289823fceda376c67af9d4ea659463a8.zip
refactor: extract MNTPOINT constant for the install chroot mount point
Last on the tech-debt drain. The installer hardcoded /mnt at 50+ sites: pacstrap, arch-chroot, mount/umount, fstab writes, and every host-side write into the chroot's /etc, /usr, /var, /boot, /tmp. Same magic-string smell as /mnt/efi but at much larger scale. Add MNTPOINT="/mnt" to lib/common.sh next to EFI_DIR. Replace literal /mnt/... with $MNTPOINT/... across installer/archangel, installer/lib/btrfs.sh, and installer/lib/common.sh. Replace bare /mnt (mount target, arch-chroot root, umount target, install_dropin parameter) with $MNTPOINT. EFI_DIR's own definition becomes EFI_DIR="$MNTPOINT/efi" for the natural composition. Folded in the related ticket: /mnt${chroot_efi_dir} in btrfs.sh:install_grub_all_efi becomes ${MNTPOINT}${chroot_efi_dir}. Was filed as a separate item but the ticket said it should ship with the MNTPOINT extraction, since the composition pattern is unusual and easy to miss in a global sed. Three /mnt references kept literal in comments where the comment describes the string concept rather than the mount point ("Remove /mnt prefix - config is used inside chroot where root is /", etc.). Substituting to $MNTPOINT in those comments would obscure the documentation. Bats: 146 → 147. One new test in test_common.bats pins MNTPOINT="/mnt". Lint clean (one shellcheck SC2295 warning fixed by quoting the parameter expansion: ${isp_firmware#"$MNTPOINT"}). VM verification deferred to a single full make test-install run after all three tech-debt commits land.
-rwxr-xr-xinstaller/archangel130
-rw-r--r--installer/lib/btrfs.sh128
-rw-r--r--installer/lib/common.sh10
-rw-r--r--testing-strategy.org2
-rw-r--r--tests/unit/test_common.bats4
5 files changed, 143 insertions, 131 deletions
diff --git a/installer/archangel b/installer/archangel
index 479b53a..107f03d 100755
--- a/installer/archangel
+++ b/installer/archangel
@@ -627,7 +627,7 @@ create_zfs_pool() {
-O relatime=on \
-O xattr=sa \
-O mountpoint=none \
- -R /mnt \
+ -R $MNTPOINT \
"$POOL_NAME" $pool_config
else
echo "$ZFS_PASSPHRASE" | zpool create -f \
@@ -645,7 +645,7 @@ create_zfs_pool() {
-O keyformat=passphrase \
-O keylocation=prompt \
-O mountpoint=none \
- -R /mnt \
+ -R $MNTPOINT \
"$POOL_NAME" $pool_config
fi
@@ -691,12 +691,12 @@ create_datasets() {
# /var/tmp - ZFS-backed, persists across reboots, excluded from snapshots
zfs create -o mountpoint=/var/tmp -o com.sun:auto-snapshot=false "$POOL_NAME/var/tmp"
- chmod 1777 /mnt/var/tmp
+ chmod 1777 $MNTPOINT/var/tmp
# /tmp - tmpfs via fstab (not ZFS: ZFS statx() returns ENOLINK on
# systemd PrivateTmp paths, breaking systemd-tmpfiles-clean every boot)
- mkdir -p /mnt/tmp
- chmod 1777 /mnt/tmp
+ mkdir -p $MNTPOINT/tmp
+ chmod 1777 $MNTPOINT/tmp
info "Datasets created:"
zfs list -r "$POOL_NAME" -o name,mountpoint,compression
@@ -741,7 +741,7 @@ EOF
|| error "Unknown filesystem: $FILESYSTEM"
# Use yes to auto-select defaults for provider prompts
- yes "" | pacstrap -K /mnt "${packages[@]}"
+ yes "" | pacstrap -K $MNTPOINT "${packages[@]}"
info "Base system installed."
}
@@ -751,30 +751,30 @@ configure_system() {
# fstab (EFI + tmpfs /tmp; /boot is on ZFS root)
info "Generating fstab..."
- echo "# /efi - EFI System Partition (ZFSBootMenu binary)" > /mnt/etc/fstab
- echo "UUID=$(blkid -s UUID -o value "${EFI_PARTS[0]}") /efi vfat defaults,noatime 0 2" >> /mnt/etc/fstab
- echo "" >> /mnt/etc/fstab
- echo "# /tmp - tmpfs (avoids ZFS statx ENOLINK on systemd PrivateTmp paths)" >> /mnt/etc/fstab
- echo "tmpfs /tmp tmpfs defaults,nosuid,nodev,mode=1777 0 0" >> /mnt/etc/fstab
+ echo "# /efi - EFI System Partition (ZFSBootMenu binary)" > $MNTPOINT/etc/fstab
+ echo "UUID=$(blkid -s UUID -o value "${EFI_PARTS[0]}") /efi vfat defaults,noatime 0 2" >> $MNTPOINT/etc/fstab
+ echo "" >> $MNTPOINT/etc/fstab
+ echo "# /tmp - tmpfs (avoids ZFS statx ENOLINK on systemd PrivateTmp paths)" >> $MNTPOINT/etc/fstab
+ echo "tmpfs /tmp tmpfs defaults,nosuid,nodev,mode=1777 0 0" >> $MNTPOINT/etc/fstab
# Timezone
info "Setting timezone to $TIMEZONE..."
- arch-chroot /mnt ln -sf "/usr/share/zoneinfo/$TIMEZONE" /etc/localtime
- arch-chroot /mnt hwclock --systohc
+ arch-chroot $MNTPOINT ln -sf "/usr/share/zoneinfo/$TIMEZONE" /etc/localtime
+ arch-chroot $MNTPOINT hwclock --systohc
# Locale
info "Configuring locale..."
- echo "$LOCALE UTF-8" >> /mnt/etc/locale.gen
- arch-chroot /mnt locale-gen
- echo "LANG=$LOCALE" > /mnt/etc/locale.conf
+ echo "$LOCALE UTF-8" >> $MNTPOINT/etc/locale.gen
+ arch-chroot $MNTPOINT locale-gen
+ echo "LANG=$LOCALE" > $MNTPOINT/etc/locale.conf
# Keymap
- echo "KEYMAP=$KEYMAP" > /mnt/etc/vconsole.conf
+ echo "KEYMAP=$KEYMAP" > $MNTPOINT/etc/vconsole.conf
# Hostname
info "Setting hostname to $HOSTNAME..."
- echo "$HOSTNAME" > /mnt/etc/hostname
- cat > /mnt/etc/hosts << EOF
+ echo "$HOSTNAME" > $MNTPOINT/etc/hostname
+ cat > $MNTPOINT/etc/hosts << EOF
127.0.0.1 localhost
::1 localhost
127.0.1.1 $HOSTNAME.localdomain $HOSTNAME
@@ -782,7 +782,7 @@ EOF
# Add archzfs repo (SigLevel=Never — same rationale as install_base)
info "Adding archzfs repository..."
- cat >> /mnt/etc/pacman.conf << 'EOF'
+ cat >> $MNTPOINT/etc/pacman.conf << 'EOF'
[archzfs]
Server = https://github.com/archzfs/archzfs/releases/download/experimental
@@ -794,21 +794,21 @@ EOF
# get created in tmpfs then hidden when ZFS mounts over it.
# Solution: Make journal-flush wait for zfs-mount, and enable persistent storage.
info "Configuring journald for ZFS..."
- mkdir -p /mnt/etc/systemd/journald.conf.d
- cat > /mnt/etc/systemd/journald.conf.d/persistent.conf << 'EOF'
+ mkdir -p $MNTPOINT/etc/systemd/journald.conf.d
+ cat > $MNTPOINT/etc/systemd/journald.conf.d/persistent.conf << 'EOF'
[Journal]
Storage=persistent
EOF
- mkdir -p /mnt/etc/systemd/system/systemd-journal-flush.service.d
- cat > /mnt/etc/systemd/system/systemd-journal-flush.service.d/zfs.conf << 'EOF'
+ mkdir -p $MNTPOINT/etc/systemd/system/systemd-journal-flush.service.d
+ cat > $MNTPOINT/etc/systemd/system/systemd-journal-flush.service.d/zfs.conf << 'EOF'
[Unit]
After=zfs-mount.service
EOF
# Set root password
info "Setting root password..."
- echo "root:$ROOT_PASSWORD" | arch-chroot /mnt chpasswd
+ echo "root:$ROOT_PASSWORD" | arch-chroot $MNTPOINT chpasswd
}
configure_wifi() {
@@ -817,9 +817,9 @@ configure_wifi() {
# Copy NetworkManager connection from live environment
if [[ -d /etc/NetworkManager/system-connections ]]; then
- mkdir -p /mnt/etc/NetworkManager/system-connections
- cp /etc/NetworkManager/system-connections/* /mnt/etc/NetworkManager/system-connections/ 2>/dev/null || true
- chmod 600 /mnt/etc/NetworkManager/system-connections/* 2>/dev/null || true
+ mkdir -p $MNTPOINT/etc/NetworkManager/system-connections
+ cp /etc/NetworkManager/system-connections/* $MNTPOINT/etc/NetworkManager/system-connections/ 2>/dev/null || true
+ chmod 600 $MNTPOINT/etc/NetworkManager/system-connections/* 2>/dev/null || true
fi
info "WiFi configuration copied to installed system."
@@ -830,10 +830,10 @@ configure_ssh() {
if [[ "$ENABLE_SSH" == "yes" ]]; then
step "Configuring SSH"
- enable_sshd_root_login /mnt/etc/ssh/sshd_config
+ enable_sshd_root_login $MNTPOINT/etc/ssh/sshd_config
# Enable sshd service
- arch-chroot /mnt systemctl enable sshd
+ arch-chroot $MNTPOINT systemctl enable sshd
info "SSH enabled with root password login."
warn "Harden SSH after install (key auth, fail2ban)."
@@ -845,20 +845,20 @@ configure_ssh() {
configure_initramfs() {
step "Configuring Initramfs for ZFS"
- cp /mnt/etc/mkinitcpio.conf /mnt/etc/mkinitcpio.conf.bak
+ cp $MNTPOINT/etc/mkinitcpio.conf $MNTPOINT/etc/mkinitcpio.conf.bak
# CRITICAL: Remove archiso drop-in that overrides mkinitcpio.conf HOOKS
# The archiso.conf contains live ISO-specific hooks that are incompatible with ZFS
# If not removed, it overrides our HOOKS setting and breaks boot after kernel updates
- if [[ -f /mnt/etc/mkinitcpio.conf.d/archiso.conf ]]; then
+ if [[ -f $MNTPOINT/etc/mkinitcpio.conf.d/archiso.conf ]]; then
info "Removing archiso drop-in config..."
- rm -f /mnt/etc/mkinitcpio.conf.d/archiso.conf
+ rm -f $MNTPOINT/etc/mkinitcpio.conf.d/archiso.conf
fi
# CRITICAL: Fix linux-lts preset file
# The preset from archiso uses archiso-specific config that breaks mkinitcpio -P
info "Creating proper linux-lts preset..."
- cat > /mnt/etc/mkinitcpio.d/linux-lts.preset << 'PRESET_EOF'
+ cat > $MNTPOINT/etc/mkinitcpio.d/linux-lts.preset << 'PRESET_EOF'
# mkinitcpio preset file for linux-lts
PRESETS=(default fallback)
@@ -876,13 +876,13 @@ PRESET_EOF
# The firmware must be in initramfs since amdgpu loads before root is mounted
if lspci | grep -qi "amd.*display\|amd.*vga\|radeon"; then
local isp_firmware
- isp_firmware=$(ls /mnt/usr/lib/firmware/amdgpu/isp_*.bin.zst 2>/dev/null | head -1)
+ isp_firmware=$(ls $MNTPOINT/usr/lib/firmware/amdgpu/isp_*.bin.zst 2>/dev/null | head -1)
if [[ -n "$isp_firmware" ]]; then
# Remove /mnt prefix - config is used inside chroot where root is /
- local chroot_path="${isp_firmware#/mnt}"
+ local chroot_path="${isp_firmware#"$MNTPOINT"}"
info "AMD APU detected with ISP firmware - adding to initramfs"
- mkdir -p /mnt/etc/mkinitcpio.conf.d
- cat > /mnt/etc/mkinitcpio.conf.d/amd-isp.conf << EOF
+ mkdir -p $MNTPOINT/etc/mkinitcpio.conf.d
+ cat > $MNTPOINT/etc/mkinitcpio.conf.d/amd-isp.conf << EOF
# AMD ISP (Image Signal Processor) firmware for camera support
# Loaded early so amdgpu can initialize ISP before root is mounted
FILES+=($chroot_path)
@@ -899,11 +899,11 @@ EOF
# No sed verification needed: a missing HOOKS= line makes mkinitcpio -P
# fail loudly downstream, so silent-replace failure can't reach a booted
# system. (Audited 2026-04-27 against silent-sed pattern.)
- sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block zfs filesystems)/' /mnt/etc/mkinitcpio.conf
+ sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block zfs filesystems)/' $MNTPOINT/etc/mkinitcpio.conf
# Get the installed kernel version (not the running kernel)
local kernel_ver
- kernel_ver=$(ls /mnt/usr/lib/modules | grep lts | head -1)
+ kernel_ver=$(ls $MNTPOINT/usr/lib/modules | grep lts | head -1)
if [[ -z "$kernel_ver" ]]; then
error "Could not find LTS kernel modules"
fi
@@ -912,16 +912,16 @@ EOF
# Ensure kernel module dependencies are up to date after DKMS build
# Must specify kernel version since running kernel differs from installed kernel
info "Updating module dependencies..."
- arch-chroot /mnt depmod "$kernel_ver"
+ arch-chroot $MNTPOINT depmod "$kernel_ver"
# Verify ZFS module exists
- if ! [[ -f "/mnt/usr/lib/modules/$kernel_ver/updates/dkms/zfs.ko.zst" ]]; then
+ if ! [[ -f "$MNTPOINT/usr/lib/modules/$kernel_ver/updates/dkms/zfs.ko.zst" ]]; then
error "ZFS module not found! DKMS build may have failed."
fi
info "ZFS module verified for kernel $kernel_ver"
info "Regenerating initramfs..."
- arch-chroot /mnt mkinitcpio -P
+ arch-chroot $MNTPOINT mkinitcpio -P
}
configure_zfsbootmenu() {
@@ -939,7 +939,7 @@ configure_zfsbootmenu() {
host_id=$(hostid)
# Copy hostid to installed system (ZFS uses this for pool ownership)
- cp /etc/hostid /mnt/etc/hostid
+ cp /etc/hostid $MNTPOINT/etc/hostid
# Create ZFSBootMenu directory on EFI
mkdir -p $EFI_DIR/EFI/ZBM
@@ -1021,17 +1021,17 @@ configure_zfsbootmenu() {
configure_zfs_services() {
step "Configuring ZFS Services"
- arch-chroot /mnt systemctl enable zfs.target
+ arch-chroot $MNTPOINT systemctl enable zfs.target
# Use zfs-import-scan instead of zfs-import-cache
# This is the recommended method - it uses blkid to scan for pools
# and doesn't require a cachefile
# Note: ZFS package preset enables zfs-import-cache by default, so we must
# explicitly disable it before enabling zfs-import-scan
- arch-chroot /mnt systemctl disable zfs-import-cache.service
- arch-chroot /mnt systemctl enable zfs-import-scan.service
- arch-chroot /mnt systemctl enable zfs-mount.service
- arch-chroot /mnt systemctl enable zfs-import.target
+ arch-chroot $MNTPOINT systemctl disable zfs-import-cache.service
+ arch-chroot $MNTPOINT systemctl enable zfs-import-scan.service
+ arch-chroot $MNTPOINT systemctl enable zfs-mount.service
+ arch-chroot $MNTPOINT systemctl enable zfs-import.target
# Note: hostid and bootfs are already set by configure_zfsbootmenu()
@@ -1039,12 +1039,12 @@ configure_zfs_services() {
# Also remove any existing cachefile since zfs-import-scan has a condition
# that prevents it from running if /etc/zfs/zpool.cache exists
zpool set cachefile=none "$POOL_NAME"
- rm -f /mnt/etc/zfs/zpool.cache
+ rm -f $MNTPOINT/etc/zfs/zpool.cache
# Enable other services
- arch-chroot /mnt systemctl enable NetworkManager
- arch-chroot /mnt systemctl enable avahi-daemon
- arch-chroot /mnt systemctl enable sshd
+ arch-chroot $MNTPOINT systemctl enable NetworkManager
+ arch-chroot $MNTPOINT systemctl enable avahi-daemon
+ arch-chroot $MNTPOINT systemctl enable sshd
info "ZFS services configured."
}
@@ -1052,9 +1052,9 @@ configure_zfs_services() {
configure_pacman_hook() {
step "Configuring Pacman Snapshot Hook"
- mkdir -p /mnt/etc/pacman.d/hooks
+ mkdir -p $MNTPOINT/etc/pacman.d/hooks
- cat > /mnt/etc/pacman.d/hooks/zfs-snapshot.hook << EOF
+ cat > $MNTPOINT/etc/pacman.d/hooks/zfs-snapshot.hook << EOF
[Trigger]
Operation = Upgrade
Operation = Install
@@ -1068,7 +1068,7 @@ When = PreTransaction
Exec = /usr/local/bin/zfs-pre-snapshot
EOF
- cat > /mnt/usr/local/bin/zfs-pre-snapshot << 'EOF'
+ cat > $MNTPOINT/usr/local/bin/zfs-pre-snapshot << 'EOF'
#!/bin/bash
POOL="zroot"
DATASET="$POOL/ROOT/default"
@@ -1096,7 +1096,7 @@ else
fi
EOF
- chmod +x /mnt/usr/local/bin/zfs-pre-snapshot
+ chmod +x $MNTPOINT/usr/local/bin/zfs-pre-snapshot
info "Pacman hook configured."
}
@@ -1105,10 +1105,10 @@ configure_zfs_tools() {
step "Installing ZFS Management Tools"
# Copy ZFS management scripts
- cp /usr/local/bin/zfssnapshot /mnt/usr/local/bin/zfssnapshot
- cp /usr/local/bin/zfsrollback /mnt/usr/local/bin/zfsrollback
- chmod +x /mnt/usr/local/bin/zfssnapshot
- chmod +x /mnt/usr/local/bin/zfsrollback
+ cp /usr/local/bin/zfssnapshot $MNTPOINT/usr/local/bin/zfssnapshot
+ cp /usr/local/bin/zfsrollback $MNTPOINT/usr/local/bin/zfsrollback
+ chmod +x $MNTPOINT/usr/local/bin/zfssnapshot
+ chmod +x $MNTPOINT/usr/local/bin/zfsrollback
info "ZFS management scripts installed: zfssnapshot, zfsrollback"
info "Tip: Install sanoid for automated snapshot retention."
@@ -1122,7 +1122,7 @@ configure_tmpfiles_private_tmp() {
local svc
for svc in systemd-tmpfiles-setup systemd-tmpfiles-clean; do
- install_dropin "$svc" zfs-private-tmp /mnt << 'EOF'
+ install_dropin "$svc" zfs-private-tmp $MNTPOINT << 'EOF'
# ZFS: statx of sibling services' /var/tmp/systemd-private-*/tmp mounts
# returns errno 132. Running in own namespace avoids traversing them.
[Service]
@@ -1174,7 +1174,7 @@ create_genesis_snapshot() {
# Create rollback script in /root
info "Installing rollback-to-genesis script..."
- cat > /mnt/root/rollback-to-genesis << 'ROLLBACK_EOF'
+ cat > $MNTPOINT/root/rollback-to-genesis << 'ROLLBACK_EOF'
#!/bin/bash
# rollback-to-genesis - Roll back all datasets to the genesis snapshot
#
@@ -1223,7 +1223,7 @@ echo "Rollback complete!"
echo "Reboot to complete the process: reboot"
ROLLBACK_EOF
- chmod +x /mnt/root/rollback-to-genesis
+ chmod +x $MNTPOINT/root/rollback-to-genesis
info "Genesis snapshot created. Rollback script: /root/rollback-to-genesis"
}
@@ -1264,7 +1264,7 @@ install_failure_cleanup() {
case "$FILESYSTEM" in
zfs)
umount $EFI_DIR 2>/dev/null || true
- umount -R /mnt 2>/dev/null || true
+ umount -R $MNTPOINT 2>/dev/null || true
if zpool list "$POOL_NAME" >/dev/null 2>&1; then
zpool export "$POOL_NAME" 2>/dev/null \
|| zpool export -f "$POOL_NAME" 2>/dev/null \
diff --git a/installer/lib/btrfs.sh b/installer/lib/btrfs.sh
index f704fd7..3704c65 100644
--- a/installer/lib/btrfs.sh
+++ b/installer/lib/btrfs.sh
@@ -87,14 +87,14 @@ setup_luks_testing_keyfile() {
warn "This reduces security - for testing only!"
# Generate random keyfile
- dd if=/dev/urandom of="/mnt${LUKS_KEYFILE}" bs=512 count=4 status=none \
+ dd if=/dev/urandom of="$MNTPOINT${LUKS_KEYFILE}" bs=512 count=4 status=none \
|| error "Failed to generate keyfile"
- chmod 000 "/mnt${LUKS_KEYFILE}"
+ chmod 000 "$MNTPOINT${LUKS_KEYFILE}"
# Add keyfile to each LUKS partition (slot 1, passphrase stays in slot 0)
for partition in "${partitions[@]}"; do
info "Adding keyfile to $partition..."
- echo -n "$passphrase" | cryptsetup luksAddKey "$partition" "/mnt${LUKS_KEYFILE}" -d - \
+ echo -n "$passphrase" | cryptsetup luksAddKey "$partition" "$MNTPOINT${LUKS_KEYFILE}" -d - \
|| error "Failed to add keyfile to $partition"
done
@@ -172,7 +172,7 @@ configure_crypttab() {
step "Configuring crypttab"
- echo "# LUKS encrypted root partitions" > /mnt/etc/crypttab
+ echo "# LUKS encrypted root partitions" > $MNTPOINT/etc/crypttab
# Use keyfile if in testing mode, otherwise prompt for passphrase
local key_source="none"
@@ -188,7 +188,7 @@ configure_crypttab() {
local name="${LUKS_MAPPER_NAME}${i}"
[[ $i -eq 0 ]] && name="$LUKS_MAPPER_NAME"
- echo "$name UUID=$uuid $key_source luks,discard" >> /mnt/etc/crypttab
+ echo "$name UUID=$uuid $key_source luks,discard" >> $MNTPOINT/etc/crypttab
info "crypttab: $name -> UUID=$uuid"
((++i))
done
@@ -200,29 +200,29 @@ configure_luks_initramfs() {
step "Configuring Initramfs for LUKS"
# Backup original
- cp /mnt/etc/mkinitcpio.conf /mnt/etc/mkinitcpio.conf.bak
+ cp $MNTPOINT/etc/mkinitcpio.conf $MNTPOINT/etc/mkinitcpio.conf.bak
# Add encrypt hook before filesystems (configure_btrfs_initramfs overwrites
# this with the final hook list, using sd-encrypt for multi-disk setups)
# No sed verification needed: a missing HOOKS= line makes mkinitcpio -P
# fail loudly downstream. (Audited 2026-04-27 against silent-sed pattern.)
sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block encrypt filesystems fsck)/' \
- /mnt/etc/mkinitcpio.conf
+ $MNTPOINT/etc/mkinitcpio.conf
# Include keyfile in initramfs for testing mode (unattended boot)
if [[ "${TESTING:-}" == "yes" ]]; then
info "Testing mode: embedding keyfile in initramfs"
- sed -i "s|^FILES=.*|FILES=($LUKS_KEYFILE)|" /mnt/etc/mkinitcpio.conf
+ sed -i "s|^FILES=.*|FILES=($LUKS_KEYFILE)|" $MNTPOINT/etc/mkinitcpio.conf
# If FILES line doesn't exist, add it
- if ! grep -q "^FILES=" /mnt/etc/mkinitcpio.conf; then
- echo "FILES=($LUKS_KEYFILE)" >> /mnt/etc/mkinitcpio.conf
+ if ! grep -q "^FILES=" $MNTPOINT/etc/mkinitcpio.conf; then
+ echo "FILES=($LUKS_KEYFILE)" >> $MNTPOINT/etc/mkinitcpio.conf
fi
fi
# Create crypttab.initramfs for sd-encrypt (used by multi-disk LUKS)
# sd-encrypt reads this file to open all LUKS devices during initramfs
- if [[ -f /mnt/etc/crypttab ]]; then
- cp /mnt/etc/crypttab /mnt/etc/crypttab.initramfs
+ if [[ -f $MNTPOINT/etc/crypttab ]]; then
+ cp $MNTPOINT/etc/crypttab $MNTPOINT/etc/crypttab.initramfs
info "Created crypttab.initramfs for sd-encrypt."
fi
@@ -238,7 +238,7 @@ configure_luks_grub() {
uuid=$(blkid -s UUID -o value "$partition")
# Enable GRUB cryptodisk support (required for encrypted /boot)
- echo "GRUB_ENABLE_CRYPTODISK=y" >> /mnt/etc/default/grub
+ echo "GRUB_ENABLE_CRYPTODISK=y" >> $MNTPOINT/etc/default/grub
# Add cryptdevice to GRUB cmdline
# For testing mode, also add cryptkey parameter for automated unlock
@@ -251,7 +251,7 @@ configure_luks_grub() {
prepend_grub_cmdline_linux \
"cryptdevice=UUID=$uuid:$LUKS_MAPPER_NAME:allow-discards ${cryptkey_param}" \
- /mnt/etc/default/grub
+ $MNTPOINT/etc/default/grub
info "GRUB configured with cryptdevice parameter and cryptodisk enabled."
}
@@ -352,17 +352,17 @@ create_btrfs_subvolumes() {
step "Creating Btrfs Subvolumes"
# Mount the raw btrfs volume temporarily
- mount "$partition" /mnt || error "Failed to mount btrfs volume"
+ mount "$partition" $MNTPOINT || error "Failed to mount btrfs volume"
# Create each subvolume
for subvol_spec in "${BTRFS_SUBVOLS[@]}"; do
IFS=':' read -r name mountpoint extra <<< "$subvol_spec"
info "Creating subvolume: $name -> $mountpoint"
- btrfs subvolume create "/mnt/$name" || error "Failed to create subvolume $name"
+ btrfs subvolume create "$MNTPOINT/$name" || error "Failed to create subvolume $name"
done
# Unmount raw volume
- umount /mnt
+ umount $MNTPOINT
info "Created ${#BTRFS_SUBVOLS[@]} subvolumes."
}
@@ -377,8 +377,8 @@ mount_btrfs_subvolumes() {
step "Mounting Btrfs Subvolumes"
# Mount root subvolume first
- info "Mounting @ -> /mnt"
- mount -o "subvol=@,$BTRFS_OPTS" "$partition" /mnt || error "Failed to mount root subvolume"
+ info "Mounting @ -> $MNTPOINT"
+ mount -o "subvol=@,$BTRFS_OPTS" "$partition" $MNTPOINT || error "Failed to mount root subvolume"
# Create mount points and mount remaining subvolumes
for subvol_spec in "${BTRFS_SUBVOLS[@]}"; do
@@ -407,13 +407,13 @@ mount_btrfs_subvolumes() {
fi
fi
- info "Mounting $name -> /mnt$mountpoint"
- mkdir -p "/mnt$mountpoint"
- mount -o "$opts" "$partition" "/mnt$mountpoint" || error "Failed to mount $name"
+ info "Mounting $name -> $MNTPOINT$mountpoint"
+ mkdir -p "$MNTPOINT$mountpoint"
+ mount -o "$opts" "$partition" "$MNTPOINT$mountpoint" || error "Failed to mount $name"
done
# Set permissions on tmp directories
- chmod 1777 /mnt/tmp /mnt/var/tmp
+ chmod 1777 $MNTPOINT/tmp $MNTPOINT/var/tmp
info "All subvolumes mounted."
}
@@ -432,7 +432,7 @@ generate_btrfs_fstab() {
uuid=$(blkid -s UUID -o value "$partition")
# Start with header
- cat > /mnt/etc/fstab << EOF
+ cat > $MNTPOINT/etc/fstab << EOF
# /etc/fstab - Btrfs subvolume mounts
# IMPORTANT: Using subvol= NOT subvolid= for snapshot compatibility
# Generated by archangel installer
@@ -460,15 +460,15 @@ EOF
fi
fi
- echo "UUID=$uuid $mountpoint btrfs $opts 0 0" >> /mnt/etc/fstab
+ echo "UUID=$uuid $mountpoint btrfs $opts 0 0" >> $MNTPOINT/etc/fstab
done
# Add EFI partition
local efi_uuid
efi_uuid=$(blkid -s UUID -o value "$efi_partition")
- echo "" >> /mnt/etc/fstab
- echo "# EFI System Partition" >> /mnt/etc/fstab
- echo "UUID=$efi_uuid /efi vfat defaults,noatime 0 2" >> /mnt/etc/fstab
+ echo "" >> $MNTPOINT/etc/fstab
+ echo "# EFI System Partition" >> $MNTPOINT/etc/fstab
+ echo "UUID=$efi_uuid /efi vfat defaults,noatime 0 2" >> $MNTPOINT/etc/fstab
info "fstab generated with ${#BTRFS_SUBVOLS[@]} btrfs mounts + EFI"
}
@@ -528,8 +528,8 @@ configure_snapper() {
echo 'grub-mkconfig -o /efi/grub/grub.cfg'
echo ''
echo 'echo "Snapper configuration complete!"'
- } > /mnt/usr/local/bin/snapper-firstboot
- chmod +x /mnt/usr/local/bin/snapper-firstboot
+ } > $MNTPOINT/usr/local/bin/snapper-firstboot
+ chmod +x $MNTPOINT/usr/local/bin/snapper-firstboot
# Create systemd service for firstboot
{
@@ -547,14 +547,14 @@ configure_snapper() {
echo ''
echo '[Install]'
echo 'WantedBy=multi-user.target'
- } > /mnt/etc/systemd/system/snapper-firstboot.service
+ } > $MNTPOINT/etc/systemd/system/snapper-firstboot.service
# Enable the firstboot service
- arch-chroot /mnt systemctl enable snapper-firstboot.service
+ arch-chroot $MNTPOINT systemctl enable snapper-firstboot.service
# Enable snapper timers
- arch-chroot /mnt systemctl enable snapper-timeline.timer
- arch-chroot /mnt systemctl enable snapper-cleanup.timer
+ arch-chroot $MNTPOINT systemctl enable snapper-timeline.timer
+ arch-chroot $MNTPOINT systemctl enable snapper-cleanup.timer
info "Snapper firstboot service configured."
info "Snapper will be fully configured on first boot."
@@ -575,7 +575,7 @@ configure_grub() {
# Configure GRUB defaults for btrfs
info "Setting GRUB configuration..."
- cat > /mnt/etc/default/grub << 'EOF'
+ cat > $MNTPOINT/etc/default/grub << 'EOF'
# GRUB configuration for btrfs root with snapshots
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
@@ -596,9 +596,9 @@ EOF
# Add LUKS encryption settings if enabled
if [[ "$NO_ENCRYPT" != "yes" && -n "$LUKS_PASSPHRASE" ]]; then
- echo "" >> /mnt/etc/default/grub
- echo "# LUKS encryption support" >> /mnt/etc/default/grub
- echo "GRUB_ENABLE_CRYPTODISK=y" >> /mnt/etc/default/grub
+ echo "" >> $MNTPOINT/etc/default/grub
+ echo "# LUKS encryption support" >> $MNTPOINT/etc/default/grub
+ echo "GRUB_ENABLE_CRYPTODISK=y" >> $MNTPOINT/etc/default/grub
# For multi-disk LUKS, sd-encrypt reads crypttab.initramfs — no cmdline params needed
# For single-disk LUKS, the encrypt hook needs cryptdevice= on the cmdline
@@ -618,7 +618,7 @@ EOF
fi
prepend_grub_cmdline_linux \
"cryptdevice=UUID=$uuid:$LUKS_MAPPER_NAME:allow-discards ${cryptkey_param}" \
- /mnt/etc/default/grub
+ $MNTPOINT/etc/default/grub
info "Added cryptdevice parameter for LUKS partition."
fi
else
@@ -632,17 +632,17 @@ EOF
# Install GRUB with boot-directory on EFI partition
info "Installing GRUB to EFI partition..."
- arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/efi \
+ arch-chroot $MNTPOINT grub-install --target=x86_64-efi --efi-directory=/efi \
--bootloader-id=GRUB --boot-directory=/efi \
|| error "GRUB installation failed"
# Create symlink BEFORE grub-mkconfig (grub-btrfs expects /boot/grub)
- rm -rf /mnt/boot/grub 2>/dev/null || true
- arch-chroot /mnt ln -sfn /efi/grub /boot/grub
+ rm -rf $MNTPOINT/boot/grub 2>/dev/null || true
+ arch-chroot $MNTPOINT ln -sfn /efi/grub /boot/grub
# Generate GRUB config (uses /boot/grub symlink -> /efi/grub)
info "Generating GRUB configuration..."
- arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg \
+ arch-chroot $MNTPOINT grub-mkconfig -o /boot/grub/grub.cfg \
|| error "Failed to generate GRUB config"
# Sync to ensure grub.cfg is written to FAT32 EFI partition
@@ -650,7 +650,7 @@ EOF
# Enable grub-btrfsd for automatic snapshot menu updates
info "Enabling grub-btrfs daemon..."
- arch-chroot /mnt systemctl enable grub-btrfsd
+ arch-chroot $MNTPOINT systemctl enable grub-btrfsd
info "GRUB configured with btrfs snapshot support."
}
@@ -682,13 +682,13 @@ install_grub_all_efi() {
mkdir -p "$mount_point"
mount "$efi_part" "$mount_point" || { warn "Failed to mount $efi_part"; ((++i)); continue; }
# Also create the directory in chroot for grub-install
- mkdir -p "/mnt${chroot_efi_dir}"
- mount --bind "$mount_point" "/mnt${chroot_efi_dir}"
+ mkdir -p "${MNTPOINT}${chroot_efi_dir}"
+ mount --bind "$mount_point" "${MNTPOINT}${chroot_efi_dir}"
fi
fi
info "Installing GRUB to $efi_part ($bootloader_id)..."
- arch-chroot /mnt grub-install --target=x86_64-efi \
+ arch-chroot $MNTPOINT grub-install --target=x86_64-efi \
--efi-directory="$chroot_efi_dir" \
--bootloader-id="$bootloader_id" \
--boot-directory=/efi \
@@ -750,12 +750,12 @@ sync_grub() {
sync_grub
'
- echo "$script_content" > /mnt/usr/local/bin/grub-sync-efi
- chmod +x /mnt/usr/local/bin/grub-sync-efi
+ echo "$script_content" > $MNTPOINT/usr/local/bin/grub-sync-efi
+ chmod +x $MNTPOINT/usr/local/bin/grub-sync-efi
# Create pacman hook
- mkdir -p /mnt/etc/pacman.d/hooks
- cat > /mnt/etc/pacman.d/hooks/99-grub-sync-efi.hook << 'HOOKEOF'
+ mkdir -p $MNTPOINT/etc/pacman.d/hooks
+ cat > $MNTPOINT/etc/pacman.d/hooks/99-grub-sync-efi.hook << 'HOOKEOF'
[Trigger]
Type = Package
Operation = Upgrade
@@ -805,8 +805,8 @@ configure_btrfs_services() {
step "Configuring System Services"
# Enable standard services
- arch-chroot /mnt systemctl enable NetworkManager
- arch-chroot /mnt systemctl enable avahi-daemon
+ arch-chroot $MNTPOINT systemctl enable NetworkManager
+ arch-chroot $MNTPOINT systemctl enable avahi-daemon
# Snapper timers (already enabled in configure_snapper)
@@ -823,17 +823,17 @@ configure_btrfs_initramfs() {
step "Configuring Initramfs for Btrfs"
# Backup original
- cp /mnt/etc/mkinitcpio.conf /mnt/etc/mkinitcpio.conf.bak
+ cp $MNTPOINT/etc/mkinitcpio.conf $MNTPOINT/etc/mkinitcpio.conf.bak
# Remove archiso drop-in if present
- if [[ -f /mnt/etc/mkinitcpio.conf.d/archiso.conf ]]; then
+ if [[ -f $MNTPOINT/etc/mkinitcpio.conf.d/archiso.conf ]]; then
info "Removing archiso drop-in config..."
- rm -f /mnt/etc/mkinitcpio.conf.d/archiso.conf
+ rm -f $MNTPOINT/etc/mkinitcpio.conf.d/archiso.conf
fi
# Create proper linux-lts preset
info "Creating linux-lts preset..."
- cat > /mnt/etc/mkinitcpio.d/linux-lts.preset << 'EOF'
+ cat > $MNTPOINT/etc/mkinitcpio.d/linux-lts.preset << 'EOF'
# mkinitcpio preset file for linux-lts
PRESETS=(default fallback)
@@ -860,22 +860,22 @@ EOF
# The traditional encrypt hook only supports a single cryptdevice
info "Multi-device LUKS: using sd-encrypt for multi-device LUKS unlock"
sed -i "s/^HOOKS=.*/HOOKS=(base systemd microcode modconf kms keyboard sd-vconsole block sd-encrypt btrfs filesystems fsck)/" \
- /mnt/etc/mkinitcpio.conf
+ $MNTPOINT/etc/mkinitcpio.conf
elif [[ $num_disks -gt 1 ]]; then
info "Multi-device btrfs: adding btrfs hook for device assembly"
sed -i "s/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block btrfs filesystems fsck)/" \
- /mnt/etc/mkinitcpio.conf
+ $MNTPOINT/etc/mkinitcpio.conf
elif [[ "$luks_enabled" == "yes" ]]; then
sed -i "s/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block encrypt filesystems fsck)/" \
- /mnt/etc/mkinitcpio.conf
+ $MNTPOINT/etc/mkinitcpio.conf
else
sed -i "s/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block filesystems fsck)/" \
- /mnt/etc/mkinitcpio.conf
+ $MNTPOINT/etc/mkinitcpio.conf
fi
# Regenerate initramfs
info "Regenerating initramfs..."
- arch-chroot /mnt mkinitcpio -P
+ arch-chroot $MNTPOINT mkinitcpio -P
info "Initramfs configured for btrfs."
}
@@ -900,11 +900,11 @@ btrfs_cleanup() {
for ((i=${#BTRFS_SUBVOLS[@]}-1; i>=0; i--)); do
IFS=':' read -r name mountpoint extra <<< "${BTRFS_SUBVOLS[$i]}"
[[ "$name" == "@" ]] && continue
- umount "/mnt$mountpoint" 2>/dev/null || true
+ umount "$MNTPOINT$mountpoint" 2>/dev/null || true
done
# Unmount root last
- umount /mnt 2>/dev/null || true
+ umount $MNTPOINT 2>/dev/null || true
info "Btrfs cleanup complete."
}
diff --git a/installer/lib/common.sh b/installer/lib/common.sh
index dfeb245..e5f7246 100644
--- a/installer/lib/common.sh
+++ b/installer/lib/common.sh
@@ -6,11 +6,19 @@
# Path Constants
#############################
+# Mount point for the install chroot's root. Sub-paths compose with
+# $MNTPOINT/etc/... (the host paths the installer writes into during
+# pacstrap and config). Bare $MNTPOINT works since `/` isn't an
+# identifier character; use ${MNTPOINT}${chroot_efi_dir} when the
+# next character would otherwise be parsed as part of the variable
+# name.
+MNTPOINT="/mnt"
+
# Mount point for the primary EFI partition during install. Sub-paths
# compose with ${EFI_DIR}/...; secondary EFI partitions in multi-disk
# layouts use ${EFI_DIR}${i} (no trailing slash, so the index appends
# cleanly).
-EFI_DIR="/mnt/efi"
+EFI_DIR="$MNTPOINT/efi"
#############################
# Output Functions
diff --git a/testing-strategy.org b/testing-strategy.org
index 6078631..f618bd1 100644
--- a/testing-strategy.org
+++ b/testing-strategy.org
@@ -61,7 +61,7 @@ Current coverage lives in =tests/unit/=:
| File | What it covers |
|------+----------------|
-| =test_common.bats= | =command_exists=, =require_command=, =info=/=warn=/=error=, =enable_color=, =log=, =prompt_password=, =pacstrap_packages=, =install_dropin=, =parse_efibootmgr_*=, =EFI_DIR=, =enable_sshd_root_login=, =prepend_grub_cmdline_linux= |
+| =test_common.bats= | =command_exists=, =require_command=, =info=/=warn=/=error=, =enable_color=, =log=, =prompt_password=, =pacstrap_packages=, =install_dropin=, =parse_efibootmgr_*=, =EFI_DIR=, =MNTPOINT=, =enable_sshd_root_login=, =prepend_grub_cmdline_linux= |
| =test_config.bats= | =parse_args=, =load_config=, =validate_config=, =validate_filesystem=, =check_config=, default values pinned in config.sh |
| =test_raid.bats= | =raid_valid_levels_for_count=, =raid_is_valid=, =raid_usable_bytes=, =raid_fault_tolerance= |
| =test_disk.bats= | =get_efi_partition=, =get_root_partition=, =partition_disks= (orchestration shape) |
diff --git a/tests/unit/test_common.bats b/tests/unit/test_common.bats
index 48efd15..abe3938 100644
--- a/tests/unit/test_common.bats
+++ b/tests/unit/test_common.bats
@@ -333,6 +333,10 @@ Boot0001* ZFSBootMenu"
[ "$EFI_DIR" = "/mnt/efi" ]
}
+@test "MNTPOINT is defined and equals /mnt" {
+ [ "$MNTPOINT" = "/mnt" ]
+}
+
#############################
# enable_sshd_root_login
#############################