From: Tucker Johnson Date: Mon, 18 May 2026 13:27:10 +0000 (-0400) Subject: init X-Git-Url: https://git.newer.systems/?a=commitdiff_plain;p=TuCLS.git init --- 865278587bad38c3bb295686342b931c4b27786d diff --git a/programs.csv b/programs.csv new file mode 100644 index 0000000..79d42be --- /dev/null +++ b/programs.csv @@ -0,0 +1,82 @@ +#TAG,NAME IN REPO (or git url),DESCRIPTION +,xorg-server,"A display server for the X Window System." +,xorg-xwininfo,"A utility for querying information about X windows." +,xorg-xinit,"A program for starting the X display server." +,xorg-xset,"A utility for configuring X server settings." +,polkit,"A toolkit for managing user authorization policies." +,otf-libertinus,"A family of serif and sans-serif fonts." +,ttf-font-awesome,"An icon font with extended glyph support." +,ttf-dejavu,"A font package with broad Unicode and emoji coverage." +,ttf-junicode,"A medievalist font." +A,lf-git,"A terminal file manager with extensive customization." +,bc,"A command-line calculator and math language." +,xcompmgr,"A compositor for transparency and reducing screen tearing." +,xorg-xprop,"A tool for inspecting X window properties." +,arandr,"A graphical front-end for configuring monitor layouts." +,dosfstools,"A set of utilities for FAT filesystem management." +,libnotify,"A library for sending desktop notifications." +,dunst,"A lightweight, customizable notification daemon." +,exfat-utils,"A set of utilities for managing exFAT filesystems." +,nsxiv,"A simple, scriptable image viewer." +,xwallpaper,"A utility for setting the desktop wallpaper." +,ffmpeg,"A command-line tool for recording and converting audio and video." +,ffmpegthumbnailer,"A tool for generating video thumbnail previews." +,gnome-keyring,"A daemon for securely storing credentials and secrets." +A,gtk-theme-arc-gruvbox-git,"A dark GTK theme based on the Gruvbox color scheme." +,python-qdarkstyle,"A dark stylesheet for Qt applications." +,neovim,"An extensible, modern fork of Vim." +,mpd,"A daemon for playing music from a library." +,mpc,"A command-line client for MPD." +,mpv,"A versatile, scriptable media player." +,man-db,"A tool for reading and managing manual pages." +,ncmpcpp,"A terminal music player interface with tag editing." +,newsboat,"A terminal RSS and Atom feed reader." +A,librewolf-bin,"A privacy-focused browser based on Firefox." +A,librewolf-extension-localcdn-bin,"An extension that serves common web libraries locally." +A,librewolf-extension-istilldontcareaboutcookies-bin,"An extension that automatically dismisses cookie consent banners." +A,librewolf-extension-ublock-origin-bin,"A broad-spectrum content and ad blocker." +A,arkenfox-user.js,"A hardened Firefox/Librewolf user.js for privacy and security." +,noto-fonts,"A comprehensive font family with broad language coverage." +,noto-fonts-emoji,"An emoji font from the Noto family." +,ntfs-3g,"A driver for reading and writing NTFS partitions." +,wireplumber,"A session manager for PipeWire audio." +,pipewire-pulse,"A PulseAudio compatibility layer for PipeWire." +,pipewire-jack,"A JACK compatibility layer for PipeWire." +,pulsemixer,"A terminal mixer for controlling audio levels." +,qpwgraph,"A graphical patchbay for PipeWire audio and MIDI routing." +A,sc-im,"A terminal spreadsheet application." +,imagemagick,"A suite of tools for image conversion and manipulation." +,maim,"A command-line screenshot tool." +,unclutter,"A utility that hides the mouse cursor when idle." +,unzip,"A tool for extracting ZIP archives." +,lynx,"A text-based web browser." +,xcape,"A tool for remapping modifier keys to secondary key outputs." +,xclip,"A utility for reading and writing the X clipboard." +,xdotool,"A tool for scripting X window and input actions." +,yt-dlp,"A command-line downloader for YouTube and other video sites." +,zathura,"A document viewer with Vim-style keybindings." +,zathura-pdf-mupdf,"A PDF rendering backend for Zathura." +,poppler,"A PDF rendering library and command-line utilities." +,mediainfo,"A tool for displaying technical metadata about media files." +,atool,"A utility for managing and inspecting archive files." +,fzf,"A general-purpose fuzzy finder for the command line." +,bat,"A syntax-highlighting pager and file viewer." +,xorg-xbacklight,"A utility for adjusting screen backlight brightness." +A,zsh-fast-syntax-highlighting-git,"A syntax highlighting plugin for Zsh." +A,task-spooler,"A queue manager for running commands sequentially." +A,simple-mtpfs,"A tool for mounting MTP devices such as phones." +A,htop-vim,"A terminal system monitor with Vim-style keybindings." +,slock,"A minimal screen locker for X." +,socat,"A relay for bidirectional data transfer between two streams." +,moreutils,"A collection of supplementary Unix command-line utilities." +,tesseract,"An OCR engine for extracting text from images." +,tesseract-data-eng,"English language data for Tesseract OCR." +,reaper,"A digital audio workstation for recording and production." +,supercollider,"An audio synthesis and algorithmic composition environment." +,pd,"A visual programming environment for audio and multimedia." +,sox,"A powerful and scriptable set of audio processing tools." +,libreoffice-fresh,"An open-source office suite for documents, spreadsheets, and presentations." +,lilypond,"A text-based music notation and engraving program." +,remind,"A calendar and reminder program with a scripting language." +,tailscale,"A mesh VPN for securely connecting devices across networks." +,urlscan,"A utility for extracting and selecting URLs from terminal output." diff --git a/tucls.sh b/tucls.sh new file mode 100755 index 0000000..1731ac8 --- /dev/null +++ b/tucls.sh @@ -0,0 +1,333 @@ +#!/bin/sh + +# TuCLS - Tucker's Configured Linux System +# Installation Script + +### OPTIONS AND VARIABLES ### + +dwmrepo="https://git.newer.systems/dwm.git" +dmenurepo="https://git.newer.systems/dmenu.git" +strepo="https://git.newer.systems/st.git" +dwmblocksrepo="https://git.newer.systems/dwmblocks.git" +nvimrepo="https://git.newer.systems/nvim.git" +configsrepo="https://git.newer.systems/configs.git" +setclassrepo="https://git.newer.systems/setclass.git" +progsfile="$(dirname "$(readlink -f "$0")")/programs.csv" +aurhelper="yay" +export TERM=ansi + +### FUNCTIONS ### + +installpkg() { pacman --noconfirm --needed -S "$1" >/dev/null 2>&1; } + +error() { printf "%s\n" "$1" >&2; exit 1; } + +welcomemsg() { + whiptail --title "Welcome to TuCLS!" \ + --msgbox "This script will install Tucker's Configured Linux System.\n\nIt will install all needed programs, build the suckless tools, and deploy all configuration files for a complete Artix Linux desktop." 12 65 + + whiptail --title "Important Note!" --yes-button "All ready!" \ + --no-button "Return..." \ + --yesno "Be sure this machine has current pacman updates and refreshed Artix keyrings.\n\nIf it does not, some programs may fail to install." 8 70 +} + +getuserandpass() { + name=$(whiptail --inputbox "Enter a name for the user account." 10 60 3>&1 1>&2 2>&3 3>&1) || exit 1 + while ! echo "$name" | grep -q "^[a-z_][a-z0-9_-]*$"; do + name=$(whiptail --nocancel --inputbox "Username not valid. Give a username beginning with a letter, with only lowercase letters, - or _." 10 60 3>&1 1>&2 2>&3 3>&1) + done + pass1=$(whiptail --nocancel --passwordbox "Enter a password for that user." 10 60 3>&1 1>&2 2>&3 3>&1) + pass2=$(whiptail --nocancel --passwordbox "Retype password." 10 60 3>&1 1>&2 2>&3 3>&1) + while ! [ "$pass1" = "$pass2" ]; do + unset pass2 + pass1=$(whiptail --nocancel --passwordbox "Passwords do not match.\n\nEnter password again." 10 60 3>&1 1>&2 2>&3 3>&1) + pass2=$(whiptail --nocancel --passwordbox "Retype password." 10 60 3>&1 1>&2 2>&3 3>&1) + done +} + +usercheck() { + ! { id -u "$name" >/dev/null 2>&1; } || + whiptail --title "WARNING" --yes-button "CONTINUE" \ + --no-button "No wait..." \ + --yesno "The user \`$name\` already exists. TuCLS will OVERWRITE any conflicting dotfiles.\n\nYour personal files (documents, music, etc.) will not be touched.\n\nOnly continue if you want existing settings overwritten." 12 70 +} + +preinstallmsg() { + whiptail --title "Ready to install!" --yes-button "Let's go!" \ + --no-button "Nevermind" \ + --yesno "The rest of the installation will now be fully automated.\n\nThis will take some time depending on your connection speed." 10 60 || { clear; exit 1; } +} + +adduserandpass() { + whiptail --infobox "Adding user \"$name\"..." 7 50 + useradd -m -g wheel -s /bin/zsh "$name" >/dev/null 2>&1 || + usermod -a -G wheel "$name" && mkdir -p "/home/$name" && chown "$name":wheel "/home/$name" + export repodir="/home/$name/.local/src/TuCLS" + mkdir -p "$repodir" + chown -R "$name":wheel "$(dirname "$repodir")" + echo "$name:$pass1" | chpasswd + unset pass1 pass2 +} + +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 "Enabling Arch repositories and refreshing keyrings..." 7 40 + pacman --noconfirm --needed -S artix-keyring artix-archlinux-support >/dev/null 2>&1 + 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 +} + +manualinstall() { + 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" || + { 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 +} + +maininstall() { + whiptail --title "TuCLS Installation" \ + --infobox "Installing \`$1\` ($n of $total). $2" 9 70 + installpkg "$1" +} + +aurinstall() { + whiptail --title "TuCLS Installation" \ + --infobox "Installing \`$1\` ($n of $total) from the AUR. $2" 9 70 + echo "$aurinstalled" | grep -q "^$1$" && return 0 + sudo -u "$name" $aurhelper -S --noconfirm "$1" >/dev/null 2>&1 +} + +gitmakeinstall() { + progname="${1##*/}" + progname="${progname%.git}" + dir="$repodir/$progname" + whiptail --title "TuCLS Installation" \ + --infobox "Installing \`$progname\` ($n of $total) via git and make. $2" 8 70 + 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; } + cd "$dir" || exit 1 + make >/dev/null 2>&1 + make install >/dev/null 2>&1 + cd /tmp || return 1 +} + +pipinstall() { + whiptail --title "TuCLS Installation" \ + --infobox "Installing Python package \`$1\` ($n of $total). $2" 9 70 + [ -x "$(command -v pip)" ] || installpkg python-pip >/dev/null 2>&1 + yes | pip install "$1" +} + +installationloop() { + ([ -f "$progsfile" ] && cp "$progsfile" /tmp/progs.csv) || + curl -Ls "$progsfile" | sed '/^#/d' >/tmp/progs.csv + total=$(wc -l /dev/null || + { cd "$dir" || return 1; sudo -u "$name" git pull --force origin master 2>/dev/null || return 1; } + cd "$dir" || return 1 + make >/dev/null 2>&1 + make install >/dev/null 2>&1 + cd /tmp || return 1 +} + +cloneandstow() { + # Clone a config repo and deploy it to $HOME via stow. + # Also runs make install if a Makefile exists (for system-wide scripts). + progname="${1##*/}" + progname="${progname%.git}" + dir="$repodir/$progname" + whiptail --infobox "Cloning and deploying \`$progname\`..." 7 60 + sudo -u "$name" git -C "$repodir" clone -q "$1" "$dir" 2>/dev/null || + { cd "$dir" || return 1; sudo -u "$name" git pull --force origin master 2>/dev/null || return 1; } + cd "$repodir" || return 1 + sudo -u "$name" stow -t "/home/$name" "$progname" + [ -f "$dir/Makefile" ] && { cd "$dir" || return 1; make install >/dev/null 2>&1; } + cd /tmp || return 1 +} + +getenvanpaths() { + whiptail --title "Path Configuration" \ + --msgbox "The next prompts will configure paths for your personal files. Press Enter to accept the defaults." 8 65 + + notes=$(whiptail --inputbox "Where would you like to store your notes?" 10 60 \ + "/home/$name/Notes" 3>&1 1>&2 2>&3 3>&1) || notes="/home/$name/Notes" + + recordings=$(whiptail --inputbox "Where would you like to save screen recordings?" 10 60 \ + "/home/$name/Videos/Recordings" 3>&1 1>&2 2>&3 3>&1) || recordings="/home/$name/Videos/Recordings" + + cat > "/home/$name/.config/shell/localenvrc" << EOF +export NOTES="$notes" +export RECORDINGS="$recordings" +EOF + chown "$name:wheel" "/home/$name/.config/shell/localenvrc" +} + +vimplugininstall() { + whiptail --infobox "Installing neovim plugins..." 7 60 + sudo -u "$name" git -C "$repodir" clone -q "$setclassrepo" "$repodir/setclass" 2>/dev/null || + { cd "$repodir/setclass" || return 1; sudo -u "$name" git pull --force origin master 2>/dev/null; } + mkdir -p "/home/$name/.config/nvim/autoload" + curl -Ls "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" \ + > "/home/$name/.config/nvim/autoload/plug.vim" + chown -R "$name:wheel" "/home/$name/.config/nvim" + sudo -u "$name" nvim --headless -c "PlugInstall" -c "qa" >/dev/null 2>&1 +} + +finalize() { + whiptail --title "All done!" \ + --msgbox "Installation complete!\n\nLog out and log back in as $name. The graphical environment will start automatically on tty1.\n\nNote: populate ~/.config/newsboat/urls with your RSS feeds before running newsboat." 13 65 +} + +### THE ACTUAL SCRIPT ### + +# Check root and install whiptail +pacman --noconfirm --needed -Sy libnewt || + error "Are you sure you're running as root on an Arch-based distribution with an internet connection?" + +# User interaction - all prompts happen up front +welcomemsg || error "User exited." +getuserandpass || error "User exited." +usercheck || error "User exited." +preinstallmsg || error "User exited." + +### No more user input until getenvanpaths ### + +# Refresh keyrings +refreshkeys || error "Error refreshing Arch keyring. Consider doing so manually." + +# Base dependencies (includes stow for config deployment) +for x in curl ca-certificates base-devel git ntp zsh dash stow; do + whiptail --title "TuCLS Installation" \ + --infobox "Installing \`$x\`, required for installation." 8 70 + installpkg "$x" +done + +# Sync time +whiptail --title "TuCLS Installation" \ + --infobox "Synchronizing system time..." 8 70 +ntpd -q -g >/dev/null 2>&1 + +# Create user account +adduserandpass || error "Error adding username and/or password." + +# Temporarily allow passwordless sudo for AUR builds +[ -f /etc/sudoers.pacnew ] && cp /etc/sudoers.pacnew /etc/sudoers +trap 'rm -f /etc/sudoers.d/tucls-temp' HUP INT QUIT TERM PWR EXIT +echo "%wheel ALL=(ALL) NOPASSWD: ALL +Defaults:%wheel,root runcwd=*" >/etc/sudoers.d/tucls-temp + +# Configure pacman +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 + +# Install AUR helper +manualinstall $aurhelper || error "Failed to install AUR helper." +$aurhelper -Y --save --devel + +# Install all programs from progs.csv +installationloop + +# Build and install suckless tools +for repo in "$dwmrepo" "$dmenurepo" "$strepo" "$dwmblocksrepo"; do + cloneandmake "$repo" || error "Failed to install $(basename "$repo" .git)." +done + +# Deploy config repos via stow +cloneandstow "$configsrepo" || error "Failed to deploy configs." +cloneandstow "$nvimrepo" || error "Failed to deploy nvim config." + +# Generate shell/lf shortcut files now that configs are in place +sudo -u "$name" shortcuts >/dev/null 2>&1 + +# Prompt for machine-specific paths and write localenvrc +getenvanpaths + +# Clone setclass and install neovim plugins +vimplugininstall + +# Create directories that programs expect to exist +sudo -u "$name" mkdir -p \ + "/home/$name/.cache/zsh" \ + "/home/$name/.config/mpd/playlists" \ + "/home/$name/.local/share/groff" \ + "/home/$name/.config/newsboat" + +# Blank newsboat urls - user populates this themselves +[ -f "/home/$name/.config/newsboat/urls" ] || + sudo -u "$name" touch "/home/$name/.config/newsboat/urls" + +# Disable terminal bell +rmmod pcspkr 2>/dev/null +echo "blacklist pcspkr" >/etc/modprobe.d/nobeep.conf + +# Set zsh as default shell +chsh -s /bin/zsh "$name" >/dev/null 2>&1 + +# Make dash the default /bin/sh +ln -sfT /bin/dash /bin/sh >/dev/null 2>&1 + +# dbus UUID for Artix runit +dbus-uuidgen >/var/lib/dbus/machine-id 2>/dev/null + +# dbus environment for non-systemd init +[ "$(readlink -f /sbin/init)" != "/usr/lib/systemd/systemd" ] && + echo "export \$(dbus-launch)" >/etc/profile.d/dbus.sh + +# Enable tap-to-click for touchpads +[ ! -f /etc/X11/xorg.conf.d/40-libinput.conf ] && 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 + +# Final sudoers: wheel can sudo with password, passwordless for common system commands +echo "%wheel ALL=(ALL:ALL) ALL" >/etc/sudoers.d/00-tucls-wheel-can-sudo +echo "%wheel ALL=(ALL:ALL) NOPASSWD: /usr/bin/shutdown,/usr/bin/reboot,/usr/bin/systemctl 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-tucls-cmds-without-password +echo "Defaults editor=/usr/bin/nvim" >/etc/sudoers.d/02-tucls-visudo-editor +mkdir -p /etc/sysctl.d +echo "kernel.dmesg_restrict = 0" >/etc/sysctl.d/dmesg.conf + +# Remove temp sudoers (also handled by trap above) +rm -f /etc/sudoers.d/tucls-temp + +finalize