Last active
February 12, 2025 09:46
-
-
Save scottzach1/fdf42b03a45e52e170b9b2d92ee5cb8f to your computer and use it in GitHub Desktop.
Arch Setup Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Configuration variables | |
TARGET_DISK="XXX" # /dev/nvme0n1 | |
SWAP_SIZE="16G" | |
HOSTNAME="desktop" | |
USERNAME="zaci" | |
TIMEZONE="Pacific/Auckland" | |
LOCALE="en_NZ.utf8" | |
KEYMAP="us" | |
# Color codes for output | |
RED='\033[0;31m' | |
GREEN='\033[0;32m' | |
YELLOW='\033[1;33m' | |
NC='\033[0m' # No Color | |
# Exit on any error and show commands being executed | |
set -e # Exit immediately if a command exits with a non-zero status | |
set -x # Print commands and their arguments as they are executed | |
# Set up logging | |
exec 1> >(tee "install_log_$(date +%Y%m%d_%H%M%S).log") | |
exec 2>&1 | |
# Cleanup function | |
cleanup() { | |
local exit_code=$? | |
set +x # Turn off command echoing for cleanup | |
echo "Script exited with code: $exit_code" | |
if [ $exit_code -ne 0 ]; then | |
echo "Installation failed! Check the log file for details." | |
# Add any necessary cleanup here, like unmounting filesystems | |
if mountpoint -q /mnt/boot 2>/dev/null; then | |
umount /mnt/boot | |
fi | |
if mountpoint -q /mnt 2>/dev/null; then | |
umount /mnt | |
fi | |
fi | |
} | |
# Helper function for logging | |
log() { | |
local level=$1 | |
shift | |
case "$level" in | |
"INFO") | |
echo -e "${GREEN}[INFO]${NC} $*" | |
;; | |
"WARN") | |
echo -e "${YELLOW}[WARN]${NC} $*" | |
;; | |
"ERROR") | |
echo -e "${RED}[ERROR]${NC} $*" | |
;; | |
esac | |
} | |
# User validation | |
confirmation_prompt() { | |
local question="$1" | |
read -p "$question (y/N) " -n 1 -r | |
echo | |
if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
log "ERROR" "Operation cancelled by user" | |
exit 1 | |
fi | |
} | |
# Sanity checks | |
sanity_checks() { | |
# Check if script is run as root | |
if [ "$EUID" -ne 0 ]; then | |
log "ERROR" "This script must be run as root" | |
exit 1 | |
fi | |
# Check if running in UEFI mode | |
if [ ! -d "/sys/firmware/efi/efivars" ]; then | |
log "ERROR" "System not booted in UEFI mode" | |
exit 1 | |
fi | |
# Check if target disk exists | |
if [ ! -b "$TARGET_DISK" ]; then | |
log "ERROR" "Target disk $TARGET_DISK not found" | |
exit 1 | |
fi | |
# Check if target disk is mounted | |
if mount | grep -q "$TARGET_DISK"; then | |
log "ERROR" "Target disk $TARGET_DISK is currently mounted. Please unmount first" | |
exit 1 | |
fi | |
# Check internet connectivity | |
if ! ping -c 1 archlinux.org >/dev/null 2>&1; then | |
log "ERROR" "No internet connection" | |
exit 1 | |
fi | |
log "INFO" "All sanity checks passed" | |
} | |
# Disk preparation | |
prepare_disk() { | |
# Final warning before disk operations | |
log "WARN" "This will erase all data on $TARGET_DISK" | |
confirmation_prompt "Are you sure you want to continue?" | |
# Create partition table and partitions | |
log "INFO" "Wiping target disk" | |
wipefs -a "$TARGET_DISK" | |
parted -s "$TARGET_DISK" mklabel gpt | |
parted -s "$TARGET_DISK" mkpart ESP fat32 1MiB 513MiB | |
parted -s "$TARGET_DISK" set 1 boot on | |
parted -s "$TARGET_DISK" mkpart primary ext4 513MiB 100% | |
mapfile -t partitions < <(lsblk -ln -o NAME,TYPE | awk -v dev="$(basename "$TARGET_DISK")" '$2=="part" && $1 ~ dev {print "/dev/" $1}' | sort -V) | |
partition_efi="${partitions[0]}" | |
partition_luks="${partitions[1]}" | |
log "WARN" "EFI Partition: $partition_efi" | |
log "WARN" "LUKS Partition: $partition_luks" | |
confirmation_prompt "Does this look correct?" | |
# Format EFI partition | |
log "INFO" "Preparing EFI partition" | |
mkfs.fat -F32 "$partition_efi" | |
# Format luks partition | |
log "INFO" "Preparing Luks partition" | |
cryptsetup luksFormat "$partition_luks" | |
cryptsetup luksOpen "$partition_luks" luks | |
log "INFO" "Creating logical volumes" | |
pvcreate /dev/mapper/luks | |
vgcreate vg0 /dev/mapper/luks | |
lvcreate -L "$SWAP_SIZE" vg0 --name swap | |
lvcreate -l 100%FREE vg0 --name root | |
mkfs.ext4 /dev/vg0/root | |
mkswap /dev/vg0/swap | |
log "INFO" "Mounting drives" | |
mount /dev/vg0/root /mnt | |
swapon /dev/vg0/swap | |
mkdir -p /mnt/boot | |
mount "$partition_efi" /mnt/boot | |
} | |
# Base system installation | |
install_base_system() { | |
# Install base packages | |
log "INFO" "Install base packages" | |
pacstrap /mnt base base-devel linux linux-firmware grub efibootmgr lvm2 fish git neovim man-db man-pages texinfo os-prober | |
# Generate fstab | |
log "INFO" "Generate and update fstab" | |
genfstab -U /mnt >> /mnt/etc/fstab | |
# Reduce SSD wear (root partition only) | |
sed -i '0,/relatime/s//noatime/' /mnt/etc/fstab | |
# Add tmpfs entry for /tmp | |
echo "# /tmp" >> /mnt/etc/fstab | |
echo "tmpfs /tmp tmpfs rw,noatime,nodev,nosuid 0 0" >> /mnt/etc/fstab | |
} | |
# Run command in arch-chroot | |
chroot_cmd() { | |
arch-chroot /mnt /bin/bash -c "$1" | |
} | |
# System configuration | |
configure_system() { | |
# Set timezone | |
log "INFO" "Set timezone to $TIMEZONE" | |
chroot_cmd "ln -sf /usr/share/zoneinfo/$TIMEZONE /etc/localtime" | |
chroot_cmd "hwclock --systohc" | |
# Set locale | |
log "INFO" "Set locale to $LOCALE" | |
chroot_cmd locale-gen | |
chroot_cmd "echo \"LANG=$LOCALE\" > /etc/locale.conf" | |
# Set keyboard layout | |
log "INFO" "Set keyboard layout to $KEYMAP" | |
chroot_cmd "echo \"KEYMAP=$KEYMAP\" >> /etc/vconsole.conf" | |
# Set hostname | |
log "INFO" "Set hostname to $HOSTNAME" | |
chroot_cmd "echo \"$HOSTNAME\" > /etc/hostname" | |
# Add additional initramfs hooks (encrypt, lvm2, resume) | |
log "INFO" "Add additional initramfs hooks (encrypt, lvm2, resume)" | |
chroot_cmd "sed -i '/^HOOKS=/ s/filesystems/encrypt lvm2 filesystems resume/' /etc/mkinitcpio.conf" | |
chroot_cmd "mkinitcpio -P" | |
# Configure and install grub | |
log "INFO" "Configure grub" | |
mapfile -t partitions < <(lsblk -ln -o NAME,TYPE | awk -v dev="$(basename "$TARGET_DISK")" '$2=="part" && $1 ~ dev {print "/dev/" $1}' | sort -V) | |
partition_luks="${partitions[1]}" | |
sed_string="s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"cryptdevice=${partition_luks}:luks:allow-discards resume=/dev/vg0/swap\"|" | |
chroot_cmd "sed -i '$sed_string' /etc/default/grub" | |
chroot_cmd "grub-install --bootloader-id=Arch --efi-directory=/boot" | |
chroot_cmd "grub-mkconfig -o /boot/grub/grub.cfg" | |
# Set root password | |
log "INFO" "Set password for root:" | |
chroot_cmd "passwd" | |
# Create user | |
chroot_cmd "useradd -m -G wheel -s /bin/bash $USERNAME" | |
log "INFO" "Set password for $USERNAME:" | |
chroot_cmd "passwd $USERNAME" | |
# Configure sudo | |
chroot_cmd "echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel" | |
} | |
# Main installation process | |
main() { | |
sanity_checks | |
prepare_disk | |
install_base_system | |
configure_system | |
log "INFO" "Initial setup is complete, please reboot to continue" | |
} | |
# Run the installation | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment