Last active
August 17, 2025 03:11
-
-
Save dyeo/b44ee68b59f66386f7621cec159bf34d to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| #!/usr/bin/env bash | |
| # Arch gaming + studio bootstrap (dual-kernel, i3-gaps, NVIDIA, PipeWire, yay, fonts/theme) | |
| # Usage: sudo ./bootstrap.sh <non-root-username> | |
| set -euo pipefail | |
| TARGET_USER="${1:-}"; [[ -n "$TARGET_USER" && "$TARGET_USER" != "root" ]] || { echo "Usage: sudo $0 <non-root-username>"; exit 1; } | |
| USER_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"; [[ -d "$USER_HOME" ]] || { echo "User home not found"; exit 1; } | |
| # ---------- helpers ---------- | |
| run_as_user() { | |
| # run yay/makepkg without requiring a user bus | |
| sudo -u "$TARGET_USER" env -i HOME="$USER_HOME" USER="$TARGET_USER" LOGNAME="$TARGET_USER" \ | |
| PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin" \ | |
| TERM=xterm-256color \ | |
| bash -lc "$*" | |
| } | |
| # ---------- multilib + keyring ---------- | |
| if ! grep -q "^\[multilib\]" /etc/pacman.conf; then | |
| printf "\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n" >> /etc/pacman.conf | |
| else | |
| sed -i '/^\#\[multilib\]/,/^\#Include = \/etc\/pacman.d\/mirrorlist/ s/^#//' /etc/pacman.conf || true | |
| fi | |
| pacman -Syyu --noconfirm | |
| pacman -Sy --noconfirm archlinux-keyring | |
| # ---------- core packages ---------- | |
| pacman -S --noconfirm --needed \ | |
| base-devel git sed grep which curl wget \ | |
| grub efibootmgr os-prober \ | |
| xorg-server xorg-xinit i3-gaps i3status dmenu alacritty \ | |
| nvidia-dkms nvidia-utils vulkan-icd-loader \ | |
| lib32-nvidia-utils lib32-vulkan-icd-loader \ | |
| pipewire pipewire-alsa pipewire-pulse pipewire-jack wireplumber \ | |
| realtime-privileges rtkit \ | |
| steam lutris wine-staging winetricks gamemode \ | |
| seatd dbus \ | |
| picom polybar ttf-dejavu ttf-liberation noto-fonts earlyoom | |
| # ---------- services (no user-bus calls) ---------- | |
| systemctl enable seatd | |
| systemctl enable --now earlyoom | |
| # make gamemoded available for all users; user can start on login | |
| systemctl --global enable gamemoded.service || true | |
| # ---------- GRUB (UEFI assumed with EFI at /boot) ---------- | |
| if [[ -d /boot/EFI || -d /boot/efi || -d /efi ]]; then | |
| grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB || true | |
| fi | |
| if grep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub; then | |
| sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="quiet nvidia-drm.modeset=1"/' /etc/default/grub | |
| else | |
| echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nvidia-drm.modeset=1"' >> /etc/default/grub | |
| fi | |
| grub-mkconfig -o /boot/grub/grub.cfg | |
| # ---------- user X session: startx → i3 ---------- | |
| install -d -o "$TARGET_USER" -g "$TARGET_USER" "${USER_HOME}/.config/i3" | |
| if [[ ! -f "${USER_HOME}/.xinitrc" ]]; then | |
| cat > "${USER_HOME}/.xinitrc" <<'EOF' | |
| #!/bin/sh | |
| exec i3 | |
| EOF | |
| chown "$TARGET_USER:$TARGET_USER" "${USER_HOME}/.xinitrc"; chmod +x "${USER_HOME}/.xinitrc" | |
| fi | |
| if [[ ! -f "${USER_HOME}/.bash_profile" ]]; then | |
| cat > "${USER_HOME}/.bash_profile" <<'EOF' | |
| # auto-start X on TTY1 | |
| if [[ -z "$DISPLAY" && "${XDG_VTNR}" = "1" ]]; then | |
| exec startx | |
| fi | |
| EOF | |
| chown "$TARGET_USER:$TARGET_USER" "${USER_HOME}/.bash_profile" | |
| fi | |
| # ---------- PipeWire low-latency + realtime ---------- | |
| usermod -aG realtime "$TARGET_USER" | |
| mkdir -p /etc/pipewire | |
| if [[ -f /usr/share/pipewire/pipewire.conf ]]; then | |
| sed -r '/^#?default\.clock\.(quantum|min-quantum|max-quantum)/d' /usr/share/pipewire/pipewire.conf \ | |
| > /etc/pipewire/pipewire.conf | |
| { | |
| echo | |
| echo "default.clock.quantum = 64" | |
| echo "default.clock.min-quantum = 32" | |
| echo "default.clock.max-quantum = 128" | |
| } >> /etc/pipewire/pipewire.conf | |
| fi | |
| # ---------- yay (AUR helper) ---------- | |
| if ! command -v yay >/dev/null 2>&1; then | |
| pacman -S --noconfirm --needed git base-devel | |
| cd /opt | |
| run_as_user "git clone https://aur.archlinux.org/yay.git" | |
| chown -R "$TARGET_USER:$TARGET_USER" /opt/yay | |
| cd /opt/yay | |
| run_as_user "cd /opt/yay && makepkg -si --noconfirm" | |
| fi | |
| # ---------- resilient xanmod + xanmod-rt ---------- | |
| # noninteractive yay flags | |
| YAYFLAGS="--noconfirm --needed --noredownload --norebuild --answerdiff N --answerclean N --mflags --skipinteg" | |
| # 1) try normal | |
| run_as_user "yay -Syu --pgpfetch || true" | |
| if ! run_as_user "yay -S $YAYFLAGS linux-xanmod linux-xanmod-headers linux-xanmod-rt linux-xanmod-rt-headers"; then | |
| # 2) alternate keyservers | |
| install -d -m 700 -o "$TARGET_USER" -g "$TARGET_USER" "$USER_HOME/.gnupg" | |
| cat > "$USER_HOME/.gnupg/dirmngr.conf" <<'EOF' | |
| keyserver hkps://keys.openpgp.org | |
| keyserver hkps://keyserver.ubuntu.com | |
| keyserver hkp://keyserver.ubuntu.com:80 | |
| honor-http-proxy | |
| EOF | |
| chown "$TARGET_USER:$TARGET_USER" "$USER_HOME/.gnupg/dirmngr.conf" | |
| run_as_user "gpgconf --kill dirmngr || true" | |
| if ! run_as_user "yay -S $YAYFLAGS linux-xanmod linux-xanmod-headers linux-xanmod-rt linux-xanmod-rt-headers"; then | |
| # 3) last resort: skip PGP just for these packages | |
| run_as_user "yay -S --noconfirm --needed --mflags '--skippgpcheck' linux-xanmod linux-xanmod-headers linux-xanmod-rt linux-xanmod-rt-headers" | |
| fi | |
| fi | |
| grub-mkconfig -o /boot/grub/grub.cfg | |
| # ---------- optional: fonts + Alacritty theme (never fail) ---------- | |
| set +e | |
| run_as_user "yay -S --noconfirm --needed ttf-iosevka-term-curly ttf-nerd-fonts-symbols ttf-nerd-fonts-symbols-mono" || true | |
| fc-cache -fv || true | |
| grep -q '^font ' "${USER_HOME}/.config/i3/config" 2>/dev/null || \ | |
| echo 'font pango:Iosevka Term Curly 11' >> "${USER_HOME}/.config/i3/config" 2>/dev/null | |
| mkdir -p "${USER_HOME}/.config/alacritty" | |
| cat > "${USER_HOME}/.config/alacritty/alacritty.yml" <<'YAML' | |
| font: | |
| normal: { family: "Iosevka Term Curly", style: Regular } | |
| bold: { family: "Iosevka Term Curly", style: Bold } | |
| italic: { family: "Iosevka Term Curly", style: Italic } | |
| size: 11.0 | |
| colors: | |
| primary: { background: '0x0a0a0f', foreground: '0xffd9f2' } | |
| cursor: { text: '0x0a0a0f', cursor: '0xff9ad6' } | |
| selection: { text: '0xffd9f2', background: '0x3a243d' } | |
| normal: { black:'0x15121a', red:'0xff6b9f', green:'0x6ee6b7', yellow:'0xffc3e1', blue:'0xb7a1ff', magenta:'0xff9ad6', cyan:'0x9fe8ff', white:'0xffe9f6' } | |
| bright: { black:'0x2a2533', red:'0xff84b0', green:'0x8cf0c9', yellow:'0xffd1e9', blue:'0xc8b7ff', magenta:'0xffb0e0', cyan:'0xb8f1ff', white:'0xffffff' } | |
| YAML | |
| chown -R "$TARGET_USER:$TARGET_USER" "${USER_HOME}/.config/alacritty" "${USER_HOME}/.config/i3" || true | |
| set -e | |
| echo | |
| echo "Done. Reboot. Pick linux-xanmod (gaming) or linux-xanmod-rt (audio) in GRUB." | |
| echo "Login on tty1 → startx → i3-gaps." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment