arch-setup/install_arch_solphyr.sh

214 lines
7.6 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# ───────────────────────────────────────────────
# Solphyr Arch Linux Auto-Installer
# LUKS2 • Btrfs subvolumes • systemd-boot • yay
# ───────────────────────────────────────────────
b=$(tput bold); r=$(tput sgr0); g=$(tput setaf 2); y=$(tput setaf 3); c=$(tput setaf 6); e=$(tput setaf 1)
info(){ echo "${c}==>${r} $*"; }
warn(){ echo "${y}!${r} $*"; }
die(){ echo "${e}x${r} $*" >&2; exit 1; }
NO_PROMPT=0
[[ "${1:-}" =~ (--no-prompt|--auto) ]] && NO_PROMPT=1
[[ ! -f .env ]] && { warn ".env missing, creating from template"; cp .env.template .env 2>/dev/null || touch .env; }
source .env || true
# ── Prompt helpers ─────────────────────────────
prompt_var() {
local var_name="$1"
local prompt_text="$2"
local default_val
default_val="$(eval "echo \${$var_name:-}")"
if [[ "$NO_PROMPT" == "1" ]]; then
info "Using $var_name=$default_val"
return 0
fi
local input
# disable errexit for the read itself
set +e
read -e -p "$prompt_text [${default_val}]: " input
local rc=$?
set -e
[[ $rc -ne 0 ]] && die "Input cancelled"
if [[ -n "$input" ]]; then
eval "$var_name=\"\$input\""
fi
return 0
}
prompt_secret() {
local var_name="$1"
local prompt_text="$2"
local default_val
default_val="$(eval "echo \${$var_name:-}")"
if [[ -z "$default_val" ]]; then
set +e
read -s -p "$prompt_text: " input
local rc=$?
set -e
echo
[[ $rc -ne 0 ]] && die "Input cancelled"
eval "$var_name=\"\$input\""
else
info "Using $var_name from .env"
fi
return 0
}
save_env() {
local var_name="$1"
local var_value
var_value="$(eval "echo \${$var_name}")"
sed -i "/^${var_name}=/d" .env
echo "${var_name}=\"${var_value}\"" >> .env
return 0
}
# ── Gather config ──────────────────────────────
prompt_var HOSTNAME "Hostname"; prompt_var USERNAME "Username"
prompt_var KEYMAP "Keymap"; prompt_var TIMEZONE "Timezone"; prompt_var LOCALE "Locale"
save_env HOSTNAME; save_env USERNAME; save_env KEYMAP; save_env TIMEZONE; save_env LOCALE
# ── Network ────────────────────────────────────
timedatectl set-ntp true
if [[ -z "$(ip link | grep -E 'state UP')" ]]; then
warn "No active network found"
prompt_var WIFI_SSID "Wi-Fi SSID"; prompt_secret WIFI_PASS "Wi-Fi password"
iwctl station wlan0 scan || true
iwctl station wlan0 connect "$WIFI_SSID" --passphrase "$WIFI_PASS" || die "Wi-Fi failed"
else info "Network detected"; fi
# ── Disk ───────────────────────────────────────
if [[ -z "${DISK:-}" ]]; then
info "Available disks:"; lsblk -dno NAME,SIZE,MODEL | grep -v loop | nl -w2 -s'. '
[[ $NO_PROMPT == 1 ]] && die "No DISK specified in .env"
read -p "Select disk number: " n; DISK="/dev/$(lsblk -dno NAME | grep -v loop | sed -n "${n}p")"
fi; save_env DISK
EFI="${DISK}p1"; ROOT="${DISK}p2"
read -p "${e}${b}⚠️ Erase $DISK? ENTER to confirm${r}"
sgdisk --zap-all "$DISK"
sgdisk -n1:0:+1G -t1:ef00 -c1:"EFI System" "$DISK"
sgdisk -n2:0:0 -t2:8300 -c2:"Linux LUKS" "$DISK"; partprobe "$DISK"
prompt_secret LUKS_PASSWORD "LUKS password"; save_env LUKS_PASSWORD
echo -n "$LUKS_PASSWORD" | cryptsetup luksFormat --batch-mode --type luks2 \
--cipher aes-xts-plain64 --key-size 512 --hash sha512 "$ROOT" -
echo -n "$LUKS_PASSWORD" | cryptsetup open "$ROOT" cryptroot -
# ── Create Btrfs filesystem & subvolumes ───────
mkfs.btrfs -L ArchRoot /dev/mapper/cryptroot
mount /dev/mapper/cryptroot /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@snapshots
umount /mnt
# Mount subvolumes
mount -o subvol=@,compress=zstd,noatime,space_cache=v2 /dev/mapper/cryptroot /mnt
mkdir -p /mnt/{boot,home,var/log,.snapshots}
mount -o subvol=@home,compress=zstd,noatime,space_cache=v2 /dev/mapper/cryptroot /mnt/home
mount -o subvol=@log,compress=zstd,noatime,space_cache=v2 /dev/mapper/cryptroot /mnt/var/log
mount -o subvol=@snapshots,compress=zstd,noatime,space_cache=v2 /dev/mapper/cryptroot /mnt/.snapshots
mkfs.fat -F32 "$EFI"
mount "$EFI" /mnt/boot
# ── Base system ────────────────────────────────
pacman -Sy --noconfirm reflector
reflector --country "United Kingdom" --latest 20 --sort rate --save /etc/pacman.d/mirrorlist
pacstrap -K /mnt base linux linux-firmware btrfs-progs vim networkmanager sudo base-devel git
genfstab -U /mnt >> /mnt/etc/fstab
export INSTALL_YAY
# ── Configure system ───────────────────────────
arch-chroot /mnt /bin/bash <<CHROOT
set -euo pipefail
ln -sf /usr/share/zoneinfo/$TIMEZONE /etc/localtime
hwclock --systohc
sed -i "s/^#${LOCALE}/${LOCALE}/" /etc/locale.gen
locale-gen
echo "LANG=$LOCALE" > /etc/locale.conf
echo "$HOSTNAME" > /etc/hostname
cat <<EOF >/etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 $HOSTNAME.localdomain $HOSTNAME
EOF
echo "KEYMAP=$KEYMAP" > /etc/vconsole.conf
# mkinitcpio encrypt hook
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems btrfs fsck)/' /etc/mkinitcpio.conf
mkinitcpio -P
# ── systemd-boot setup ──
bootctl install || true
# FAT32 can't store UNIX perms, so remount and fix what we can
mount -o remount,rw /boot || true
find /boot -type f -exec chmod 600 {} + 2>/dev/null || true
find /boot -type d -exec chmod 700 {} + 2>/dev/null || true
chown -R root:root /boot || true
LUKS_UUID=\$(cryptsetup luksUUID "${ROOT}")
cat <<EOF >/boot/loader/loader.conf
default arch.conf
timeout 3
console-mode max
editor no
EOF
cat <<EOF >/boot/loader/entries/arch.conf
title Arch Linux
linux /vmlinuz-linux
initrd /initramfs-linux.img
options cryptdevice=UUID=\${LUKS_UUID}:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw
EOF
# User & yay
useradd -m -G wheel -s /bin/bash $USERNAME
sed -i 's/^# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' /etc/sudoers
systemctl enable NetworkManager
if [[ "\${INSTALL_YAY,,}" == "yes" ]]; then
pacman -S --noconfirm base-devel git go
echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/00-yay-temp
su - "$USERNAME" -c '
set -e
cd /tmp
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si --noconfirm
cd
rm -rf /tmp/yay
'
rm -f /etc/sudoers.d/00-yay-temp
fi
CHROOT
# ── Passwords ─────────────────────────────────
info "Set root password"
until arch-chroot /mnt passwd; do echo "Try again."; done
info "Set password for ${USERNAME}"
until arch-chroot /mnt passwd "${USERNAME}"; do echo "Try again."; done
# ── Finish ────────────────────────────────────
umount -R /mnt
cryptsetup close cryptroot
info "Installation complete"
cat <<EOF
${g}${b}✅ Encrypted Btrfs Arch installation complete!${r}
Remove install media and reboot:
${b}reboot${r}
Then log in as '${USERNAME}' and run your post-install script:
${c}wget https://your.gitea.instance/raw/setup_postinstall.sh && bash setup_postinstall.sh${r}
EOF