Feature: Bootstrap script for new installs

bootstrap.sh installs everything listed in system/packages.txt (pacman,
AUR via yay, flatpak, official install scripts), auto-detects CPU
microcode and GPU drivers, enables services, links dotfiles with stow
and runs system/install.sh. Optional --svt-av1 installs
svt-av1-essential-git and rebuilds ffmpeg against it via
system/rebuild-ffmpeg.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-11 18:10:27 +01:00
parent 1201e6a4cc
commit 4a57e9902f
4 changed files with 399 additions and 1 deletions

166
bootstrap.sh Executable file
View File

@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# Sets up a fresh Arch install: drivers, packages, dotfiles and system config.
# Safe to re-run; anything already installed is skipped.
#
# Usage: ./bootstrap.sh [--svt-av1 | --no-svt-av1]
# --svt-av1 also install svt-av1-essential-git and rebuild ffmpeg against it
# --no-svt-av1 skip that without asking
# (neither) ask
set -euo pipefail
repo="$(cd "$(dirname "$0")" && pwd)"
list="$repo/system/packages.txt"
svt_av1=ask
for arg in "$@"; do
case "$arg" in
--svt-av1) svt_av1=yes ;;
--no-svt-av1) svt_av1=no ;;
*) echo "Unknown option: $arg" >&2; exit 1 ;;
esac
done
if (( EUID == 0 )); then
echo "Run this as your normal user, not root (it uses sudo where needed)." >&2
exit 1
fi
step() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
# Prints the packages under a [section] of packages.txt, skipping comments
packages() {
awk -v want="[$1]" '
/^\[/ { section = $1; next }
section == want { sub(/#.*/, ""); if (NF) print $1 }
' "$list"
}
# --- System config -----------------------------------------------------------
step "System config (pacman options)"
"$repo/system/install.sh"
step "Enabling multilib (needed for Steam and 32-bit drivers)"
if ! grep -q '^\[multilib\]' /etc/pacman.conf; then
sudo sed -i '/^#\[multilib\]/,/^#Include/ s/^#//' /etc/pacman.conf
fi
sudo pacman -Syu --needed --noconfirm pciutils git base-devel
# --- Hardware ----------------------------------------------------------------
step "Detecting CPU and GPU"
hw=()
case "$(grep -m1 '^vendor_id' /proc/cpuinfo | awk '{print $3}')" in
AuthenticAMD) hw+=(amd-ucode); echo "CPU: AMD" ;;
GenuineIntel) hw+=(intel-ucode); echo "CPU: Intel" ;;
*) echo "CPU: unknown vendor, skipping microcode" ;;
esac
# Vendor column of every display controller, e.g. "NVIDIA Corporation"
gpus="$(lspci -mm | awk -F'"' '$2 ~ /VGA|3D|Display/ { print $4 }')"
if grep -q NVIDIA <<<"$gpus"; then
echo "GPU: NVIDIA"
# nvidia-open only ships modules for the stock `linux` kernel; any other
# kernel needs the DKMS build. Supports Turing (GTX 16xx / RTX 20xx) and newer.
if pacman -Qq linux-lts linux-zen linux-hardened >/dev/null 2>&1; then
hw+=(nvidia-open-dkms linux-headers)
else
hw+=(nvidia-open)
fi
hw+=(nvidia-utils lib32-nvidia-utils libva-nvidia-driver)
fi
if grep -q 'Advanced Micro Devices' <<<"$gpus"; then
echo "GPU: AMD"
hw+=(mesa lib32-mesa vulkan-radeon lib32-vulkan-radeon)
fi
if grep -q 'Intel Corporation' <<<"$gpus"; then
echo "GPU: Intel"
hw+=(mesa lib32-mesa vulkan-intel lib32-vulkan-intel intel-media-driver)
fi
# Installed before the main list so Steam's 32-bit Vulkan dependency is
# already satisfied by the right driver
if (( ${#hw[@]} )); then
sudo pacman -S --needed --noconfirm "${hw[@]}"
fi
# --- Packages ----------------------------------------------------------------
step "Official repo packages"
mapfile -t pkgs < <(packages pacman)
sudo pacman -S --needed --noconfirm "${pkgs[@]}"
step "yay (AUR helper)"
if ! command -v yay >/dev/null; then
build="$(mktemp -d)"
git clone https://aur.archlinux.org/yay-bin.git "$build/yay-bin"
(cd "$build/yay-bin" && makepkg -si --noconfirm)
rm -rf "$build"
else
echo "already installed"
fi
step "AUR packages"
mapfile -t pkgs < <(packages aur)
yay -S --needed --noconfirm "${pkgs[@]}"
step "Flatpak apps"
sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
mapfile -t pkgs < <(packages flatpak)
sudo flatpak install -y --noninteractive flathub "${pkgs[@]}"
step "Apps from official install scripts"
mkdir -p "$HOME/.local/bin"
for app in $(packages installer); do
if command -v "$app" >/dev/null || [[ -x "$HOME/.local/bin/$app" ]]; then
echo "$app: already installed"
continue
fi
case "$app" in
claude) curl -fsSL https://claude.ai/install.sh | bash ;;
oh-my-posh) curl -fsSL https://ohmyposh.dev/install.sh | bash -s -- -d "$HOME/.local/bin" ;;
*) echo "$app: no install recipe in bootstrap.sh, skipping" >&2 ;;
esac
done
# --- Optional: SVT-AV1 essential + ffmpeg rebuild ----------------------------
if [[ $svt_av1 == ask ]]; then
read -rp $'\nInstall svt-av1-essential-git and rebuild ffmpeg against it? (slow) [y/N] ' reply
[[ $reply == [yY]* ]] && svt_av1=yes || svt_av1=no
fi
if [[ $svt_av1 == yes ]]; then
step "svt-av1-essential-git"
# Interactive on purpose: pacman asks to replace the conflicting svt-av1,
# and --noconfirm would answer no
yay -S --needed svt-av1-essential-git
step "Rebuilding ffmpeg against svt-av1-essential-git"
"$repo/system/rebuild-ffmpeg.sh"
fi
# --- Services ----------------------------------------------------------------
step "Enabling services"
# Not started now: sddm would take over the screen mid-script. They come up on reboot.
mapfile -t units < <(packages services)
sudo systemctl enable "${units[@]}"
# --- Dotfiles ----------------------------------------------------------------
step "Linking dotfiles with stow"
# Create real parent dirs first, otherwise stow symlinks the whole directory
# (e.g. ~/.config) into the repo and every app's files end up in git
mkdir -p "$HOME/.config" "$HOME/.local/bin" "$HOME/.local/share" "$HOME/Pictures"
mkdir -p -m 700 "$HOME/.ssh"
if ! stow -d "$repo" -t "$HOME" --restow dotfiles; then
echo "stow hit conflicts (listed above). Move those files aside, then run:" >&2
echo " stow -d \"$repo\" -t \"\$HOME\" dotfiles" >&2
fi
if [[ "$(getent passwd "$USER" | cut -d: -f7)" != */zsh ]]; then
step "Setting zsh as login shell"
chsh -s /usr/bin/zsh
fi
step "Done — reboot to load drivers and start Hyprland"