#!/usr/bin/env bash # # Arch Linux installation # # Bootable USB: # - [Download](https://archlinux.org/download/) ISO and GPG files # - Verify the ISO file: `$ pacman-key -v archlinux--x86_64.iso.sig` # - Create a bootable USB with: `# dd if=archlinux*.iso of=/dev/sdX && sync` # # UEFI setup: # # - Set boot mode to UEFI, disable Legacy mode entirely. # - Temporarily disable Secure Boot. # - Make sure a strong UEFI administrator password is set. # - Delete preloaded OEM keys for Secure Boot, allow custom ones. # - Set SATA operation to AHCI mode. # # Run installation: # # - Connect to wifi via: `# iwctl station wlan0 connect WIFI-NETWORK` # - Run: `# bash <(curl -sL https://link.rafe.li/dot)` # # WARNING: this script will destroy data on the selected disk. # set -uo pipefail trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR exec 1> >(tee "stdout.log") exec 2> >(tee "stderr.log" >&2) export SNAP_PAC_SKIP=y # Dialog BACKTITLE="Arch Linux installation" get_input() { title="$1" description="$2" input=$(dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --inputbox "$description" 0 0) echo "$input" } get_password() { title="$1" description="$2" while : ; do init_pass=$(dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --passwordbox "$description" 0 0) : "${init_pass:?dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --msgbox "Password cannot be empty.\nTry again." 0 0}" test_pass=$(dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --passwordbox "$description again" 0 0) if [[ "$init_pass" != "$test_pass" ]]; then dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --msgbox "Passwords did not match.\nTry again." 0 0 else break fi done echo "$init_pass" } get_choice() { title="$1" description="$2" shift 2 options=("$@") dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --menu "$description" 0 0 0 "${options[@]}" } echo -e "\n### Checking UEFI boot mode" if [ ! -f /sys/firmware/efi/fw_platform_size ]; then echo >&2 "You must boot in UEFI mode to continue" exit 2 fi echo -e "\n### Ensure the system clock is accurate" timedatectl set-ntp true hwclock --systohc --utc echo -e "\n### Setting keyboard layout to de-latin1" loadkeys de-latin1 echo -e "\n### Installing additional tools" pacman -Sy --noconfirm --needed git reflector terminus-font dialog wget echo -e "\n### HiDPI screens" noyes=("Yes" "The font is too small" "No" "The font size is just fine") hidpi=$(get_choice "Font size" "Is your screen HiDPI?" "${noyes[@]}") || exit 1 clear [[ "$hidpi" == "Yes" ]] && font="ter-132n" || font="ter-716n" setfont "$font" hostname=$(get_input "Hostname" "Enter hostname") || exit 1 clear : "${hostname:?"hostname cannot be empty"}" user=$(get_input "User" "Enter username") || exit 1 clear : "${user:?"user cannot be empty"}" password=$(get_password "User" "Enter password") || exit 1 clear : "${password:?"password cannot be empty"}" devicelist=$(lsblk -dplnx size -o name,size | grep -Ev "boot|rpmb|loop" | tac | tr '\n' ' ') read -r -a devicelist <<< "$devicelist" device=$(get_choice "Installation" "Select installation disk" "${devicelist[@]}") || exit 1 clear echo -e "\n### Setting up fastest mirrors" reflector --country 'Germany,France,' --protocol https --sort rate --save /etc/pacman.d/mirrorlist echo -e "\n### Setting up partitions" umount -R /mnt 2> /dev/null || true cryptsetup luksClose luks 2> /dev/null || true lsblk -plnx size -o name "${device}" | xargs -n1 wipefs --all sgdisk --clear "${device}" --new 1::-551MiB "${device}" --new 2::0 --typecode 2:ef00 "${device}" sgdisk --change-name=1:primary --change-name=2:ESP "${device}" part_root="$(ls "${device}"* | grep -E "^${device}p?1$")" part_boot="$(ls "${device}"* | grep -E "^${device}p?2$")" echo -e "\n### Formatting partitions" mkfs.vfat -n "EFI" -F 32 "${part_boot}" echo -n "${password}" | cryptsetup luksFormat --type luks2 --pbkdf argon2id --label luks "${part_root}" echo -n "${password}" | cryptsetup luksOpen --allow-discards --persistent "${part_root}" luks mkfs.btrfs -L btrfs /dev/mapper/luks echo -e "\n### Setting up BTRFS subvolumes" mount /dev/mapper/luks /mnt btrfs subvolume create /mnt/root btrfs subvolume create /mnt/home btrfs subvolume create /mnt/pkgs btrfs subvolume create /mnt/aurbuild btrfs subvolume create /mnt/archbuild btrfs subvolume create /mnt/docker btrfs subvolume create /mnt/logs btrfs subvolume create /mnt/temp btrfs subvolume create /mnt/swap btrfs subvolume create /mnt/snapshots umount /mnt mount -o noatime,compress=zstd,subvol=root /dev/mapper/luks /mnt mkdir -p /mnt/{mnt/btrfs-root,efi,home,var/{cache/pacman,log,tmp,lib/{aurbuild,archbuild,docker}},swap,.snapshots} mount "${part_boot}" /mnt/efi mount -o noatime,compress=zstd,subvol=/ /dev/mapper/luks /mnt/mnt/btrfs-root mount -o noatime,compress=zstd,subvol=home /dev/mapper/luks /mnt/home mount -o noatime,compress=zstd,subvol=pkgs /dev/mapper/luks /mnt/var/cache/pacman mount -o noatime,compress=zstd,subvol=aurbuild /dev/mapper/luks /mnt/var/lib/aurbuild mount -o noatime,compress=zstd,subvol=archbuild /dev/mapper/luks /mnt/var/lib/archbuild mount -o noatime,compress=zstd,subvol=docker /dev/mapper/luks /mnt/var/lib/docker mount -o noatime,compress=zstd,subvol=logs /dev/mapper/luks /mnt/var/log mount -o noatime,compress=zstd,subvol=temp /dev/mapper/luks /mnt/var/tmp mount -o noatime,compress=zstd,subvol=swap /dev/mapper/luks /mnt/swap mount -o noatime,compress=zstd,subvol=snapshots /dev/mapper/luks /mnt/.snapshots echo -e "\n### Configuring custom repo" mkdir "/mnt/var/cache/pacman/${user}-local" # if [[ "${user}" == "maximbaz" && "${hostname}" == "home-"* ]]; then # wget -m -nH -np -q --show-progress --progress=bar:force --reject='index.html*' --cut-dirs=2 -P "/mnt/var/cache/pacman/${user}-local" 'https://pkgbuild.com/~maximbaz/repo/' # rename -- 'maximbaz.' "${user}-local." "/mnt/var/cache/pacman/${user}-local"/* # else repo-add "/mnt/var/cache/pacman/${user}-local/${user}-local.db.tar" # fi if ! grep "${user}" /etc/pacman.conf > /dev/null; then cat >> /etc/pacman.conf << EOF [${user}-local] Server = file:///mnt/var/cache/pacman/${user}-local [maximbaz] Server = https://pkgbuild.com/~maximbaz/repo [options] CacheDir = /mnt/var/cache/pacman/pkg CacheDir = /mnt/var/cache/pacman/${user}-local EOF fi echo -e "\n### Installing packages" kernel_packages=( "linux" "linux-headers" "linux-lts" "linux-firmware" "intel-ucode" ) fs_packages=( "btrfs-progs" "dosfstools" "e2fsprogs" ) network_packages=( "iwd" "systemd-resolvconf" ) basic_packages=( "man-db" "man-pages" "pacman-contrib" "neovim" "bash-completion" "git" "rsync" "openssh" "htop" "fzf" "sudo" "pipewire" "pipewire-pulse" "pipewire-jack" "wireplumber" "firefox" "firefox-i18n-de" "firefox-ublock-origin" "firefox-dark-reader" "aurutils" # from maximbaz repo "devtools" # tools for aurutils "docbook-xls" # depenency of plymouth-git "efitools" # provides KeyTool "libfido2" # for systemd-cryptenroll ) all_packages=( ${kernel_packages[@]} ${fs_packages[@]} ${network_packages[@]} ${basic_packages[@]} ) pacstrap /mnt base base-devel arch-secure-boot ${all_packages[@]} echo -e "\n### Generating base config files" echo "cryptdevice=PARTLABEL=primary:luks:allow-discards root=LABEL=btrfs rootflags=subvol=root rw quiet mem_sleep_default=deep" > /mnt/etc/kernel/cmdline genfstab -L /mnt >> /mnt/etc/fstab echo "FONT=$font" > /mnt/etc/vconsole.conf echo "KEYMAP=de-latin1" >> /mnt/etc/vconsole.conf echo "${hostname}" > /mnt/etc/hostname sed -i 's/^#en_US\.UTF-8/en_US\.UTF-8/' /mnt/etc/locale.gen sed -i 's/^#de_DE\.UTF-8/de_DE\.UTF-8/' /mnt/etc/locale.gen echo "LANG=en_US.UTF-8" > /mnt/etc/locale.conf arch-chroot /mnt locale-gen ln -sf /usr/share/zoneinfo/Europe/Berlin /mnt/etc/localtime echo "$hostname" > /mnt/etc/hostname echo -e "127.0.0.1\tlocalhost" >>/mnt/etc/hosts echo -e "127.0.1.1\t$hostname" >>/mnt/etc/hosts echo -e "\n::1\tlocalhost" >>/mnt/etc/hosts # Propagate the systemd-resolved managed configuration to all clients (stub mode) ln -sf /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf cat >/mnt/etc/systemd/network/20-wired.network </mnt/etc/systemd/network/25-wireless.network </mnt/etc/iwd/main.conf </mnt/etc/mkinitcpio.conf <> /mnt/etc/fstab echo -e "\n### Creating user" arch-chroot /mnt useradd -m "$user" for group in wheel network video audio input storage power; do arch-chroot /mnt groupadd -rf "$group" arch-chroot /mnt gpasswd -a "$user" "$group" done echo "$user:$password" | arch-chroot /mnt chpasswd echo -e "\n### Disabling root login" arch-chroot /mnt passwd -dl root echo -e "\n### Setting permissions on the custom repo" arch-chroot /mnt chown -R "$user:$user" "/var/cache/pacman/${user}-local/" echo -e "\n### Cloning dotfiles" arch-chroot /mnt sudo -u $user bash -c 'git clone --recursive https://code.strobeto.de/strobeltobias/dotfiles.git ~/.dotfiles' echo -e "\n### Running initial setup" arch-chroot /mnt /home/$user/.dotfiles/setup-system.sh arch-chroot /mnt sudo -u $user /home/$user/.dotfiles/setup-user.sh echo -e "\n### DONE - reboot and re-run both ~/.local/share/chezmoi/setup-*.sh scripts"