#!/bin/sh # ============================================================================ # # ██╗ █████╗ ███████╗██╗ ██╗██╗ ██╗██████╗ ██╗███████╗ Z # ██║ ██╔══██╗╚══███╔╝╚██╗ ██╔╝██║ ██╔╝██╔══██╗██║██╔════╝ Z # ██║ ███████║ ███╔╝ ╚████╔╝ █████╔╝ ██████╔╝██║███████╗ z # ██║ ██╔══██║ ███╔╝ ╚██╔╝ ██╔═██╗ ██╔══██╗██║╚════██║ z # ███████╗██║ ██║███████╗ ██║ ██║ ██╗██║ ██║██║███████║ # ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ # # LazyKris - Kris's Auto Rice Bootstrapping Scripts # "The only way to do great work is to automate the boring parts." # - Every Linux User Ever # # Author: Kris Yotam (aka. khr1st) # Contact: krisyotam@protonmail.com # Date: 2026.01.23 # License: GNU GPLv3 # Repository: https://github.com/krisyotam/sdeploy # ============================================================================ # ============================================================================ # CONFIGURATION VARIABLES # ============================================================================ # Repository URLs and branches DOTFILES_REPO="https://github.com/krisyotam/srice.git" DOTFILES_BRANCH="main" PROGS_FILE="https://raw.githubusercontent.com/krisyotam/sdeploy/main/sd-artix/progs.csv" NOTES_FILE="https://raw.githubusercontent.com/krisyotam/sdeploy/main/sd-artix/notes.csv" DEV_FILE="https://raw.githubusercontent.com/krisyotam/sdeploy/main/sd-artix/dev.csv" # Doom Emacs repository DOOM_REPO="https://github.com/doomemacs/doomemacs" # Dev directory top-level directories DEV_TLDS="sites systems formal foss labs parsing" # AUR helper of choice (yay is recommended) AUR_HELPER="yay" # BlackArch repository setup script (pentest machines only, not enabled by default) BLACKARCH_STRAP="https://blackarch.org/strap.sh" # Source directory for git builds (suckless, etc) SRC_DIR=".local/src" # Terminal settings export TERM=ansi # Device type (set during installation) # Options: "desktop" or "macbook-2015" DEVICE_TYPE="" # Optional installations (set during installation) INSTALL_NOTES="no" INSTALL_DEV="no" # ============================================================================ # UTILITY FUNCTIONS # ============================================================================ # Display error message and exit error() { printf "\033[1;31mError:\033[0m %s\n" "$1" >&2 exit 1 } # Display info message info() { printf "\033[1;34m==>\033[0m %s\n" "$1" } # Display success message success() { printf "\033[1;32m==>\033[0m %s\n" "$1" } # Display warning message warn() { printf "\033[1;33m==>\033[0m %s\n" "$1" } # Install a package from official repos (pacman) installpkg() { pacman --noconfirm --needed -S "$1" >/dev/null 2>&1 } # ============================================================================ # DEVICE SELECTION # ============================================================================ # Ask user which device they're installing on selectdevice() { DEVICE_TYPE=$(whiptail --title "Device Selection" \ --menu "Which device are you installing on?\n\nThis determines HiDPI settings, bar sizes, etc." 14 60 2 \ "desktop" "Custom PC / Standard Display" \ "macbook-2015" "MacBook Pro 2015 Retina (13\")" \ 3>&1 1>&2 2>&3 3>&1) || exit 1 } # Run device-specific post-installation scripts run_device_setup() { case "$DEVICE_TYPE" in "macbook-2015") whiptail --infobox "Applying MacBook Pro 2015 Retina settings..." 7 55 # Run the laptop HiDPI script if [ -x "/home/$name/.local/bin/laptop" ]; then sudo -u "$name" /home/$name/.local/bin/laptop else warn "Laptop script not found or not executable" fi ;; "desktop") whiptail --infobox "Desktop setup - no additional configuration needed." 7 55 sleep 1 ;; *) # Unknown device, skip ;; esac } # ============================================================================ # GITHUB AUTHENTICATION # ============================================================================ # Authenticate with GitHub for private repo access setup_github_auth() { # Check if gh is installed if ! command -v gh >/dev/null 2>&1; then warn "GitHub CLI not installed, skipping authentication" return 1 fi # Check if already authenticated if sudo -u "$name" gh auth status >/dev/null 2>&1; then whiptail --infobox "Already authenticated with GitHub." 7 50 sleep 1 return 0 fi # Explain the process to user whiptail --title "GitHub Authentication" --msgbox "To clone private repositories, you need to authenticate with GitHub. The next screen will show a one-time code. 1. Visit: github.com/login/device 2. Enter the code shown 3. Approve access Press OK to continue." 14 55 # Run gh auth login with device flow # Using --git-protocol https so git operations use the gh credential helper clear echo "" echo "==========================================" echo " GitHub Device Authentication" echo "==========================================" echo "" sudo -u "$name" gh auth login --hostname github.com --git-protocol https --scopes repo,read:org # Verify authentication succeeded if sudo -u "$name" gh auth status >/dev/null 2>&1; then whiptail --infobox "GitHub authentication successful!" 7 45 sleep 1 return 0 else warn "GitHub authentication failed or was cancelled" return 1 fi } # ============================================================================ # OPTIONAL INSTALLATIONS # ============================================================================ # Ask user about optional repo installations (notes, dev) selectoptional() { # Notes repositories (slipbox, etc.) if whiptail --title "Notes Repositories" \ --yesno "Install notes repositories (slipbox, etc.)? This clones Kris's personal notes repos into ~/notes/. These are private repos and require GitHub authentication. Skip this if you're not Kris or don't have access." 14 60; then INSTALL_NOTES="yes" else INSTALL_NOTES="no" fi # Dev repositories if whiptail --title "Dev Repositories" \ --yesno "Install development repositories? This creates ~/dev/ structure with TLDs: sites, systems, formal, foss, labs, parsing And clones repos from dev.csv (many are private). Skip this if you're not Kris or don't have access." 16 60; then INSTALL_DEV="yes" else INSTALL_DEV="no" fi } # ============================================================================ # WELCOME AND USER INPUT # ============================================================================ # Display welcome message with ASCII art welcomemsg() { whiptail --title "Welcome!" --msgbox " ██╗ █████╗ ███████╗██╗ ██╗██╗ ██╗██████╗ ██╗███████╗ Z ██║ ██╔══██╗╚══███╔╝╚██╗ ██╔╝██║ ██╔╝██╔══██╗██║██╔════╝ Z ██║ ███████║ ███╔╝ ╚████╔╝ █████╔╝ ██████╔╝██║███████╗ z ██║ ██╔══██║ ███╔╝ ╚██╔╝ ██╔═██╗ ██╔══██╗██║╚════██║ z ███████╗██║ ██║███████╗ ██║ ██║ ██╗██║ ██║██║███████║ ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ Welcome to LazyKris! This script will automatically install a fully-featured Artix Linux desktop with my custom builds of dwm, dmenu, dwmblocks, st, and surf. In addition several extreme privacy configs for the rotation of browsers ran. Sit back, grab a coffee, and let the automation begin. -Kris" 24 80 whiptail --title "Important Note!" \ --yes-button "All ready!" \ --no-button "Return..." \ --yesno "Before we begin, please ensure: 1. You are running this as root 2. Pacman is updated (pacman -Sy) 3. Arch keyrings are current 4. You have a stable internet connection If any of these are not met, the installation may fail partway through." 14 60 } # Get username and password from user getuserandpass() { name=$(whiptail --inputbox "Enter a username for the new account. This will be the user that owns the dotfiles and runs the desktop environment." 12 60 3>&1 1>&2 2>&3 3>&1) || exit 1 # Validate username format while ! echo "$name" | grep -q "^[a-z_][a-z0-9_-]*$"; do name=$(whiptail --nocancel --inputbox "Invalid username. Username must: - Start with a lowercase letter or underscore - Contain only lowercase letters, numbers, - or _ Enter a valid username:" 14 60 3>&1 1>&2 2>&3 3>&1) done # Get password pass1=$(whiptail --nocancel --passwordbox "Enter a password for $name." 10 60 3>&1 1>&2 2>&3 3>&1) pass2=$(whiptail --nocancel --passwordbox "Confirm the password." 10 60 3>&1 1>&2 2>&3 3>&1) # Verify password match while [ "$pass1" != "$pass2" ]; do unset pass2 pass1=$(whiptail --nocancel --passwordbox "Passwords did not match. Enter password again:" 12 60 3>&1 1>&2 2>&3 3>&1) pass2=$(whiptail --nocancel --passwordbox "Confirm the password." 10 60 3>&1 1>&2 2>&3 3>&1) done } # Warn if user already exists usercheck() { if id -u "$name" >/dev/null 2>&1; then whiptail --title "User Exists" \ --yes-button "Continue" \ --no-button "Cancel" \ --yesno "The user '$name' already exists on this system. LazyKris can install for an existing user, but it will OVERWRITE any conflicting dotfiles and settings. Your personal files (documents, videos, etc.) will NOT be touched. Note: The password will be changed to what you entered. Continue with installation?" 16 65 fi } # Final confirmation before install preinstallmsg() { whiptail --title "Ready to Install" \ --yes-button "Let's go!" \ --no-button "Cancel" \ --yesno "Everything is ready! The installation will now proceed automatically. This may take 30-60 minutes depending on your internet speed and the number of packages. You can watch the progress, but no input is needed. Press 'Let's go!' to begin installation." 14 60 || { clear exit 1 } } # ============================================================================ # USER MANAGEMENT # ============================================================================ # Create user account and set password adduserandpass() { whiptail --infobox "Creating user '$name'..." 7 50 # Create user with fish shell, add to wheel group useradd -m -g wheel -s /usr/bin/fish "$name" >/dev/null 2>&1 || usermod -a -G wheel "$name" && mkdir -p /home/"$name" && chown "$name":wheel /home/"$name" # Set up source directory for git builds export repodir="/home/$name/$SRC_DIR" mkdir -p "$repodir" chown -R "$name":wheel "$(dirname "$repodir")" # Set password echo "$name:$pass1" | chpasswd unset pass1 pass2 } # ============================================================================ # SYSTEM PREPARATION # ============================================================================ # Refresh Arch keyrings (handles both Arch and Artix) refreshkeys() { case "$(readlink -f /sbin/init)" in *systemd*) whiptail --infobox "Refreshing Arch keyring..." 7 40 pacman --noconfirm -S archlinux-keyring >/dev/null 2>&1 ;; *) whiptail --infobox "Setting up Arch repos for Artix..." 7 50 pacman --noconfirm --needed -S \ artix-keyring artix-archlinux-support >/dev/null 2>&1 # Add Arch extra repo if not present grep -q "^\[extra\]" /etc/pacman.conf || { echo "[extra] Include = /etc/pacman.d/mirrorlist-arch" >>/etc/pacman.conf } pacman -Sy --noconfirm >/dev/null 2>&1 pacman-key --populate archlinux >/dev/null 2>&1 ;; esac } # ============================================================================ # PACKAGE INSTALLATION FUNCTIONS # ============================================================================ # Install AUR helper manually (yay) manualinstall() { # Skip if already installed pacman -Qq "$1" >/dev/null 2>&1 && return 0 whiptail --infobox "Installing $1 manually..." 7 50 sudo -u "$name" mkdir -p "$repodir/$1" sudo -u "$name" git -C "$repodir" clone --depth 1 --single-branch \ --no-tags -q "https://aur.archlinux.org/$1.git" "$repodir/$1" 2>/dev/null || { cd "$repodir/$1" || return 1 sudo -u "$name" git pull --force origin master } cd "$repodir/$1" || exit 1 sudo -u "$name" makepkg --noconfirm -si >/dev/null 2>&1 || return 1 } # Install from official repos (pacman) maininstall() { whiptail --title "LazyKris Installation" \ --infobox "[$n/$total] Installing: $1 $2" 8 70 installpkg "$1" } # Install from AUR via helper aurinstall() { whiptail --title "LazyKris Installation" \ --infobox "[$n/$total] Installing from AUR: $1 $2" 8 70 # Skip if already installed echo "$aurinstalled" | grep -q "^$1$" && return 0 sudo -u "$name" $AUR_HELPER -S --noconfirm "$1" >/dev/null 2>&1 } # Install via git clone and make gitmakeinstall() { # Extract program name from URL progname="${1##*/}" progname="${progname%.git}" dir="$repodir/$progname" whiptail --title "LazyKris Installation" \ --infobox "[$n/$total] Building from source: $progname $2" 8 70 # Clone or update repository sudo -u "$name" git -C "$repodir" clone --depth 1 --single-branch \ --no-tags -q "$1" "$dir" 2>/dev/null || { cd "$dir" || return 1 sudo -u "$name" git pull --force origin master } # Build and install cd "$dir" || exit 1 make >/dev/null 2>&1 make install >/dev/null 2>&1 cd /tmp || return 1 } # ============================================================================ # MAIN INSTALLATION LOOP # ============================================================================ installationloop() { # Download or copy progs.csv if [ -f "$PROGS_FILE" ]; then cp "$PROGS_FILE" /tmp/progs.csv else curl -Ls "$PROGS_FILE" >/tmp/progs.csv fi # Remove comments and empty lines for counting sed '/^#/d;/^$/d' /tmp/progs.csv >/tmp/progs-clean.csv total=$(wc -l /dev/null) n=0 while IFS=, read -r tag program comment; do n=$((n + 1)) # Strip quotes from comment if present echo "$comment" | grep -q "^\".*\"$" && comment="$(echo "$comment" | sed -E "s/(^\"|\"$)//g")" case "$tag" in "A") aurinstall "$program" "$comment" ;; "G") gitmakeinstall "$program" "$comment" ;; *) maininstall "$program" "$comment" ;; esac done /tmp/notes.csv fi # Remove comments and empty lines sed '/^#/d;/^$/d' /tmp/notes.csv >/tmp/notes-clean.csv notesdir="/home/$name/notes" sudo -u "$name" mkdir -p "$notesdir" # Clone each repository while IFS=, read -r repo_url target_dir description; do # Strip quotes if present repo_url=$(echo "$repo_url" | sed -E "s/(^\"|\"$)//g") target_dir=$(echo "$target_dir" | sed -E "s/(^\"|\"$)//g") description=$(echo "$description" | sed -E "s/(^\"|\"$)//g") whiptail --infobox "Cloning notes repo: $description" 7 60 if [ "$target_dir" = "." ] || [ -z "$target_dir" ]; then # Clone directly into notes directory (merge contents) clone_dir="$notesdir" else # Clone into subdirectory clone_dir="$notesdir/$target_dir" fi # Clone or update the repository if [ -d "$clone_dir/.git" ]; then # Already exists, pull updates sudo -u "$name" git -C "$clone_dir" pull --ff-only >/dev/null 2>&1 elif [ "$target_dir" = "." ] || [ -z "$target_dir" ]; then # Clone into existing notes dir (init if empty) sudo -u "$name" git clone --depth 1 "$repo_url" "$notesdir.tmp" >/dev/null 2>&1 # Move contents (except .git initially, then move .git) sudo -u "$name" cp -rfT "$notesdir.tmp" "$notesdir" rm -rf "$notesdir.tmp" else sudo -u "$name" git clone --depth 1 "$repo_url" "$clone_dir" >/dev/null 2>&1 fi done /tmp/dev.csv fi # Remove comments and empty lines sed '/^#/d;/^$/d' /tmp/dev.csv >/tmp/dev-clean.csv # Count repos for progress total_repos=$(wc -l /dev/null 2>&1 else # Ensure parent directory exists sudo -u "$name" mkdir -p "$(dirname "$clone_dir")" # Clone the repo sudo -u "$name" git clone --depth 1 "$repo_url" "$clone_dir" >/dev/null 2>&1 fi done /dev/null 2>&1 fi # Ensure doom config directory exists (should be from dotfiles) sudo -u "$name" mkdir -p "$doomdir" # Run doom install (non-interactive) whiptail --infobox "Running Doom Emacs install (this may take a while)..." 7 60 sudo -u "$name" DOOMDIR="$doomdir" "$emacsdir/bin/doom" install --no-config --no-env --no-fonts >/dev/null 2>&1 # Sync doom packages whiptail --infobox "Syncing Doom Emacs packages..." 7 50 sudo -u "$name" DOOMDIR="$doomdir" "$emacsdir/bin/doom" sync >/dev/null 2>&1 # Note: doom bin path is already in fish config } # Set up Neovim (LazyVim auto-bootstraps, just ensure first run works) setup_neovim() { whiptail --infobox "Preparing Neovim (LazyVim)..." 7 50 nvimdir="/home/$name/.config/nvim" nvimdata="/home/$name/.local/share/nvim" # Ensure directories exist sudo -u "$name" mkdir -p "$nvimdir" sudo -u "$name" mkdir -p "$nvimdata" # LazyVim bootstraps itself on first run via lazy.lua # Run nvim headless to trigger plugin installation whiptail --infobox "Installing Neovim plugins (LazyVim)..." 7 55 sudo -u "$name" nvim --headless "+Lazy! sync" +qa >/dev/null 2>&1 || true chown -R "$name":wheel "$nvimdir" "$nvimdata" } # Create necessary directories create_directories() { whiptail --infobox "Creating directory structure..." 7 50 homedir="/home/$name" # Core directories sudo -u "$name" mkdir -p "$homedir/downloads" sudo -u "$name" mkdir -p "$homedir/dev" sudo -u "$name" mkdir -p "$homedir/notes" sudo -u "$name" mkdir -p "$homedir/mail" sudo -u "$name" mkdir -p "$homedir/tmp" sudo -u "$name" mkdir -p "$homedir/archive" # Media directories sudo -u "$name" mkdir -p "$homedir/media/music" sudo -u "$name" mkdir -p "$homedir/media/pics" sudo -u "$name" mkdir -p "$homedir/media/videos" sudo -u "$name" mkdir -p "$homedir/media/screenshots" sudo -u "$name" mkdir -p "$homedir/media/wallpapers" sudo -u "$name" mkdir -p "$homedir/media/recordings" sudo -u "$name" mkdir -p "$homedir/media/books" sudo -u "$name" mkdir -p "$homedir/media/docs" # XDG directories (.local) sudo -u "$name" mkdir -p "$homedir/.local/bin" sudo -u "$name" mkdir -p "$homedir/.local/bin/statusbar" sudo -u "$name" mkdir -p "$homedir/.local/src" sudo -u "$name" mkdir -p "$homedir/.local/share" sudo -u "$name" mkdir -p "$homedir/.local/share/man" sudo -u "$name" mkdir -p "$homedir/.local/state" # XDG config and cache sudo -u "$name" mkdir -p "$homedir/.config" sudo -u "$name" mkdir -p "$homedir/.cache" sudo -u "$name" mkdir -p "$homedir/.cache/fish" # Keys and security (empty, managed by tools) sudo -u "$name" mkdir -p "$homedir/.ssh" sudo -u "$name" mkdir -p "$homedir/.gnupg" chmod 700 "$homedir/.ssh" "$homedir/.gnupg" # Claude Code sudo -u "$name" mkdir -p "$homedir/.claude" # Application-specific config directories sudo -u "$name" mkdir -p "$homedir/.config/abook" sudo -u "$name" mkdir -p "$homedir/.config/mpd/playlists" sudo -u "$name" mkdir -p "$homedir/.config/newsboat" sudo -u "$name" mkdir -p "$homedir/.config/btop/themes" sudo -u "$name" mkdir -p "$homedir/.config/dunst" sudo -u "$name" mkdir -p "$homedir/.config/calcurse" sudo -u "$name" mkdir -p "$homedir/.config/rtorrent" sudo -u "$name" mkdir -p "$homedir/.config/zathura" sudo -u "$name" mkdir -p "$homedir/.config/wal/templates" # Set proper ownership chown -R "$name":wheel "$homedir" } # Set fish as default shell setup_fish_shell() { whiptail --infobox "Setting fish as default shell..." 7 50 chsh -s /usr/bin/fish "$name" >/dev/null 2>&1 } # Make all user bin scripts executable setup_bin_scripts() { whiptail --infobox "Setting up user scripts..." 7 50 bindir="/home/$name/.local/bin" # Make all scripts in .local/bin executable if [ -d "$bindir" ]; then find "$bindir" -type f -exec chmod +x {} \; chown -R "$name":wheel "$bindir" fi # Also handle statusbar scripts if [ -d "$bindir/statusbar" ]; then find "$bindir/statusbar" -type f -exec chmod +x {} \; fi } # System configuration tweaks system_tweaks() { whiptail --infobox "Applying system tweaks..." 7 50 # Disable system beep rmmod pcspkr 2>/dev/null echo "blacklist pcspkr" >/etc/modprobe.d/nobeep.conf # Make dash the default /bin/sh (faster scripts) ln -sfT /bin/dash /bin/sh 2>/dev/null # Generate dbus UUID (needed for some systems) dbus-uuidgen >/var/lib/dbus/machine-id 2>/dev/null # Pacman tweaks: colors, parallel downloads, candy grep -q "ILoveCandy" /etc/pacman.conf || \ sed -i "/#VerbosePkgLists/a ILoveCandy" /etc/pacman.conf sed -Ei "s/^#(ParallelDownloads).*/\1 = 5/;/^#Color$/s/#//" /etc/pacman.conf # Use all cores for compilation sed -i "s/-j2/-j$(nproc)/;/^#MAKEFLAGS/s/^#//" /etc/makepkg.conf } # Enable tap to click for touchpads setup_touchpad() { [ ! -f /etc/X11/xorg.conf.d/40-libinput.conf ] && { mkdir -p /etc/X11/xorg.conf.d printf 'Section "InputClass" Identifier "libinput touchpad catchall" MatchIsTouchpad "on" MatchDevicePath "/dev/input/event*" Driver "libinput" Option "Tapping" "on" EndSection' >/etc/X11/xorg.conf.d/40-libinput.conf } } # Set up sudoers for wheel group setup_sudoers() { whiptail --infobox "Configuring sudo permissions..." 7 50 # Allow wheel users to sudo with password echo "%wheel ALL=(ALL:ALL) ALL" >/etc/sudoers.d/00-wheel-can-sudo # Allow certain commands without password echo "%wheel ALL=(ALL:ALL) NOPASSWD: /usr/bin/shutdown,/usr/bin/reboot,/usr/bin/loginctl suspend,/usr/bin/mount,/usr/bin/umount,/usr/bin/pacman -Syu,/usr/bin/pacman -Syyu,/usr/bin/pacman -Syyu --noconfirm,/usr/bin/loadkeys,/usr/bin/pacman -Syyuw --noconfirm" >/etc/sudoers.d/01-cmds-without-password # Set neovim as default editor for visudo echo "Defaults editor=/usr/bin/nvim" >/etc/sudoers.d/02-visudo-editor # Allow dmesg for non-root mkdir -p /etc/sysctl.d echo "kernel.dmesg_restrict = 0" >/etc/sysctl.d/dmesg.conf # Disable pam_faillock (prevents account lockouts on failed logins) for pamfile in /etc/pam.d/system-login /etc/pam.d/system-auth /etc/pam.d/login /etc/pam.d/sudo; do [ -f "$pamfile" ] && sed -i 's/^\([^#].*pam_faillock.*\)$/# \1/' "$pamfile" done } # Clean up temporary files cleanup() { rm -f /tmp/progs.csv /tmp/progs-clean.csv rm -f /etc/sudoers.d/sdeploy-temp } # Display completion message finalize() { whiptail --title "Installation Complete!" --msgbox " ██╗ █████╗ ███████╗██╗ ██╗██╗ ██╗██████╗ ██╗███████╗ Z ██║ ██╔══██╗╚══███╔╝╚██╗ ██╔╝██║ ██╔╝██╔══██╗██║██╔════╝ Z ██║ ███████║ ███╔╝ ╚████╔╝ █████╔╝ ██████╔╝██║███████╗ z ██║ ██╔══██║ ███╔╝ ╚██╔╝ ██╔═██╗ ██╔══██╗██║╚════██║ z ███████╗██║ ██║███████╗ ██║ ██║ ██╗██║ ██║██║███████║ ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ Congratulations! LazyKris installation is complete! To start using your new system: 1. Log out of root 2. Log in as '$name' 3. Run 'startx' to start dwm Your default shell is fish, and all your dotfiles have been deployed. Enjoy your new setup! -Kris" 28 80 } # ============================================================================ # MAIN SCRIPT EXECUTION # ============================================================================ # Ensure we're running as root with necessary tools pacman --noconfirm --needed -Sy libnewt || error "This script must be run as root on an Arch-based system with internet." # Welcome and get user info welcomemsg || error "User cancelled." selectdevice || error "User cancelled." selectoptional getuserandpass || error "User cancelled." usercheck || error "User cancelled." preinstallmsg || error "User cancelled." ### Automated installation begins ### # Refresh keyrings for package verification refreshkeys || error "Failed to refresh keyrings." # Install essential bootstrap packages for pkg in curl ca-certificates base-devel git ntp fish dash; do whiptail --infobox "Installing bootstrap package: $pkg" 7 60 installpkg "$pkg" done # Sync system time for package verification whiptail --infobox "Synchronizing system time..." 7 50 ntpd -q -g >/dev/null 2>&1 # Create user account adduserandpass || error "Failed to create user account." # Handle any pacnew sudoers file [ -f /etc/sudoers.pacnew ] && cp /etc/sudoers.pacnew /etc/sudoers # Temporarily allow passwordless sudo for builds trap 'rm -f /etc/sudoers.d/sdeploy-temp' HUP INT QUIT TERM PWR EXIT echo "%wheel ALL=(ALL) NOPASSWD: ALL Defaults:%wheel,root runcwd=*" >/etc/sudoers.d/sdeploy-temp # Apply system tweaks system_tweaks # Install AUR helper manualinstall "$AUR_HELPER" || error "Failed to install AUR helper." # Enable automatic updates for git AUR packages $AUR_HELPER -Y --save --devel # Main installation loop - install all packages from progs.csv installationloop # Deploy dotfiles to home directory putgitrepo "$DOTFILES_REPO" "/home/$name" # Create directory structure create_directories # Make all bin scripts executable setup_bin_scripts # Set wallpaper setup_wallpaper # Set up fish shell setup_fish_shell # GitHub authentication (only if user wants private repos) if [ "$INSTALL_NOTES" = "yes" ] || [ "$INSTALL_DEV" = "yes" ]; then setup_github_auth fi # Clone notes repositories (slipbox, etc.) - optional [ "$INSTALL_NOTES" = "yes" ] && notesinstallloop # Clone dev repositories and create TLD structure - optional [ "$INSTALL_DEV" = "yes" ] && devinstallloop # Set up Doom Emacs (install + sync packages) setup_doom_emacs # Set up Neovim (LazyVim plugin installation) setup_neovim # Enable touchpad tap-to-click setup_touchpad # Configure sudoers setup_sudoers # Run device-specific setup (laptop HiDPI, etc.) run_device_setup # Clean up cleanup # All done! finalize # vim: ft=sh ts=4 sw=4 et