Skip to content

Instantly share code, notes, and snippets.

@JoshuaJChan
Last active April 18, 2023 20:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshuaJChan/aaf4de5fb7d6c82209fe0314602c4679 to your computer and use it in GitHub Desktop.
Save JoshuaJChan/aaf4de5fb7d6c82209fe0314602c4679 to your computer and use it in GitHub Desktop.
script to install Arch Linux
#!/bin/bash
## Assumes ##
# Working internet connection
# Pre-installation work already done
## Instructions ##
# After booting into Arch ISO, run:
# curl -sL https://git.io/JexlX | /bin/bash
# and follow the instructions on the script.
# You can always Ctrl-C to interrupt the script.
## Safer Bash Scripting ###
set -euo pipefail #e: exits immediatly if failure, u:exit if using unset variables, o pipefail: if using pipeline, will exit where it failed since if not set, it would have still gone on to the last command even if prev command failed,
#set -x #shows the actually bash command before executing it(verbose)
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR #trap catches signals and execute code when they appear, on error, the echo command is called and ERR is the type error that trap catches
### Set up logging ###
exec 1> >(tee "stdout.log")
exec 2> >(tee "stderr.log")
### Start of Installer ###
printf "Welcome to my awesome Arch Installer. This script assumes a network is already configured.\n\n 0 - default partion scheme using ext4 fs(needs atleast 40.5 GB of storage): 260MiB /boot, 30GB root, 10GB swap, remaining space home;\n 1 - custom installation\n\nNOTE: If you have UEFI, systemd-boot will be installed. If you have BIOS, GRUB will be installed. Please modify the script to change the bootloader.\n\nHow would you like to install Arch? [0/1] "
read method < /dev/tty #must specific input from current tty otherwise will take stdin from other sources such as pipes
## default ##
if [ ${method:-0} -eq 0 ]; then
devicelist=$(lsblk -dplnx size -o name,size | grep -Ev "boot|rpmb|loop" | tac) #list devices of only their name and size, excluding non disks, tac prints in rev order
device=$(dialog --stdout --menu "Select disk to install to" 0 0 0 ${devicelist}) || exit 1 #redirect output to stdout to variable, if unsuccessful command exit 1
hostname=$(dialog --stdout --inputbox "Enter hostname" 0 0) || exit 1; #if LHS unsuccess, will exit with error 1, must put semicolon to clear properly, also needs backslash for properly reading inner variables
clear; # don't forget semicolon
: ${hostname:?"hostname cannot be empty"}
password=$(dialog --stdout --passwordbox "Enter admin password" 0 0) || exit 1; #setting root password
clear;
: ${password:?"password cannot be empty"}
password2=$(dialog --stdout --passwordbox "Enter admin password again" 0 0) || exit 1;
clear;
[[ "$password" == "$password2" ]] || ( echo "Passwords did not match"; exit 1; )
timedatectl set-ntp true
#echo "Would you like to modify the keyboard layout? (y/n)" #***needs to be fixed
#read method2 < /dev/tty
#if [ "$method2" == "y" ] || [ "$method2" == "Y" ]; then
#localectl list-keymaps # list possible layouts
#localectl status # current layout
#echo "Scroll back the see the options. The current keyboard settings are shown above"
#echo "Please enter a choice from the list of keymaps"
#read keymap < /dev/tty
#loadkeys $keymap # switch the layout to us
#: ${keymap:?"Can not be empty"}
#fi
if [ -d /sys/firmware/efi/efivars/ ]; then #for uefi
echo "Creating UEFI boot";
cpu=$(dialog --stdout --menu "Select your CPU type" 0 0 0 Intel "" AMD "") || exit 1
if [ cpu == Intel ]; then
cpu=intel;
else
cpu=amd;
fi
#no user intervention, use chosen device, end of command options, make gpt type
#efi boot partition **MUST BE fat32 WHEN USING GNU PARTED** recommended size of 260MiB
#flag the partition that contains the /boot directory as bootable
#root partition taking 30G
#swap partition matching the 10GB of ram
#home partition taking rest of space
#***IMPORTANT*** can not have spaces after backslash! or else will not run properly
parted --script "${device}" -- mklabel gpt \
mkpart ESP fat32 1MiB 261MiB \
set 1 esp on \
mkpart primary ext4 261MiB 30.5GiB \
mkpart primary linux-swap 30.5GiB 40.5GiB \
mkpart primary ext4 40.5GiB 100%;
else #for bios
echo "Creating BIOS boot"; # must do "bios_grub" as flag for the set command in parted; more info: https://www.gnu.org/software/parted/manual/html_node/set.html
parted --script "${device}" -- mklabel gpt \
mkpart primary ext4 1MiB 261MiB \
set 1 bios_grub on \
mkpart primary ext4 261MiB 30.5GiB \
mkpart primary linux-swap 30.5GiB 40.5GiB \
mkpart primary ext4 40.5GiB 100%;
fi
# device paths for each part created from parted
bootPath="$(ls ${device}* | grep -E "^${device}p?1$")"
rootPath="$(ls ${device}* | grep -E "^${device}p?2$")"
swapPath="$(ls ${device}* | grep -E "^${device}p?3$")"
homePath="$(ls ${device}* | grep -E "^${device}p?4$")"
wipefs "${bootPath}"
wipefs "${rootPath}"
wipefs "${swapPath}"
wipefs "${homePath}"
# format each path
if [ -d /sys/firmware/efi/efivars/ ]; then #if efi boot, need to change format of boot partition
mkfs.vfat -F32 "${bootPath}"
else
mkfs.ext4 "${bootPath}"
fi
mkfs.ext4 "${rootPath}" #root, swap and home in that order
mkswap "${swapPath}"
mkfs.ext4 "${homePath}"
swapon "${swapPath}"
mount "${rootPath}" /mnt #directory already exists ***IMPORTANT: Must mount other partitions AFTER /mnt otherwise, the other dirs will be hidden by the new /mnt filesystem
mkdir /mnt/boot; #IMPORTANT: DO NOT make /mnt/efi since systemd-boot by default searches and updates for configurations from /mnt/boot
mount "${bootPath}" /mnt/boot
mkdir /mnt/home
mount "${homePath}" /mnt/home
pacstrap /mnt base linux linux-firmware efibootmgr dosfstools os-prober mtools linux-headers vim base-devel redshift firefox network-manager-applet bluez-utils xorg-server xorg-xinit xf86-video-intel xorg-input-libinput paccache htop tlp;
genfstab -U /mnt >> /mnt/etc/fstab
#cool side note: if you put the - after the <<, the indents will be taken into account before lines in the heredoc; it's not used here tho
arch-chroot /mnt /bin/bash <<- EOF
ln -sf /mnt/usr/share/zoneinfo/Canada/Eastern /etc/localtime
hwclock --systohc
sed -e 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' -i /etc/locale.gen
locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf
# echo "KEYMAP=us" > /etc/vconsole.conf #changing the keyboard layout
echo "${hostname}" > /etc/hostname
cat << EOF1 > /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 ${hostname}.localdomain ${hostname}
EOF1
# mkinitcpio -P #only if have encrypt fs, raid, or lvm
EOF
#***IMPORTANT: for systemd-boot, must have add initrd intel-ucode.img as FIRST initrd (assuming I have an intel CPU, otherwise put amd)
# must install intel-ucode pkg (or amd-ucode pkg)
# on the contrary, GRUB automatically detects the cpu type so oh well...
if [ -d /sys/firmware/efi/efivars/ ]; then #install systemd-boot bootloader
arch-chroot /mnt bootctl install
cat <<EOF > /mnt/boot/loader/loader.conf
default arch
EOF
pacstrap /mnt $cpu-ucode linux # must do bootctl install THEN pacstrap/install $cpu-ucode. Why? So that the img files are found in the same directory! /boot directory, and im not sure why but you have to reinstall linux so that the other .img files will appear in /boot... I will have to re organize this later...
cat << EOF2 > /mnt/boot/loader/entries/arch.conf
title Arch Linux
linux /vmlinuz-linux
initrd /$cpu-ucode.img
initrd /initramfs-linux.img
options root=PARTUUID=$(blkid -s PARTUUID -o value "$rootPath") rw
EOF2
else #install grub bootloader
pacstrap /mnt grub
arch-chroot /mnt grub-install --target=i386-pc ${device}
#arch-chroot /mnt grub-install --target=x86_64-efi --bootloader-id=grub_uefi --efi-directory=/boot --recheck; # for uefi
arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg
fi
echo "root:$password" | arch-chroot /mnt chpasswd #chpasswd takes the format of changing passwords by (name):(new password)
elif [ $method -eq 1 ]; then
cgdisk;
hostname=$(dialog --stdout --inputbox "Enter hostname" 0 0) || exit 1; #if LHS unsuccess, will exit with error 1, must put semicolon to clear properly, also needs backslash for properly reading inner variables
clear; # don't forget semicolon
: ${hostname:?"hostname cannot be empty"}
password=$(dialog --stdout --passwordbox "Enter admin password" 0 0) || exit 1; #setting root password
clear;
: ${password:?"password cannot be empty"}
password2=$(dialog --stdout --passwordbox "Enter admin password again" 0 0) || exit 1;
clear;
[[ "$password" == "$password2" ]] || ( echo "Passwords did not match"; exit 1; )
timedatectl set-ntp true
fi
echo "--- Finished ---"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment