257 lines
14 KiB
Bash
Executable File
257 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
# ║ setup/modules/shell-setup.sh — Shell environment deployment ║
|
|
# ║ ║
|
|
# ║ PURPOSE: ║
|
|
# ║ Installs and configures the full shell environment: zsh, oh-my-zsh, ║
|
|
# ║ plugins, neovim with lazy.nvim plugins, yazi, starship prompt, and ║
|
|
# ║ deploys all dotfile symlinks to their correct locations. ║
|
|
# ║ ║
|
|
# ║ WHEN TO RUN: ║
|
|
# ║ After core-packages.sh (needs base packages). Runs as the logged-in ║
|
|
# ║ user — must NOT run as root. ║
|
|
# ║ ║
|
|
# ║ WHAT GETS DEPLOYED: ║
|
|
# ║ ~/.bashrc, ~/.zshrc — symlinks to Dotfiles repo ║
|
|
# ║ ~/.config/starship.toml — symlink to repo ║
|
|
# ║ ~/.config/nvim/ — symlink to repo/nvim/ ║
|
|
# ║ ~/.config/micro/ — copy from repo/micro/ (plugin state needs cp)║
|
|
# ║ ~/.config/alot/ — symlink for email client config ║
|
|
# ║ ~/.config/yazi/ — symlink for file manager config ║
|
|
# ║ ~/Pictures/fflogo.svg — logo asset ║
|
|
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
|
|
set -euo pipefail
|
|
# -e: any failed package install or symlink operation aborts the module
|
|
# -u: unset variable references are errors
|
|
# -o pipefail: catch pipe failures
|
|
|
|
# Load shared logging helpers (log, skip, warn, err functions)
|
|
source "$(dirname "${BASH_SOURCE[0]}")/lib/logging.sh"
|
|
|
|
# ── System update ──────────────────────────────────────────────────────────────
|
|
# WHY: Ensure the package database is current before installing to avoid
|
|
# dependency conflicts on a rolling-release distro like Arch.
|
|
log "Updating system..."
|
|
sudo pacman -Syu --noconfirm
|
|
|
|
# ── Base shell packages ────────────────────────────────────────────────────────
|
|
# WHY: Install package-by-package with idempotency checks rather than one big
|
|
# pacman call. This lets us skip already-installed packages individually
|
|
# and produce useful "already installed" log messages.
|
|
# Each package in PACKAGES is checked with `pacman -Qi` (query installed).
|
|
#
|
|
# Package notes:
|
|
# zsh — primary shell (replaces bash as default)
|
|
# neovim — primary editor (configured via dotfiles/nvim/)
|
|
# pyright — Python LSP server for neovim (coc/LSP plugin)
|
|
# bash-language-server — bash LSP for neovim
|
|
# btop — interactive resource monitor
|
|
# clang — C/C++ toolchain; needed by treesitter + some nvim plugins
|
|
# fastfetch — system info display on terminal open
|
|
# fzf — fuzzy finder; used by shell history, yazi, neovim
|
|
# hyfetch — pride-themed neofetch variant
|
|
# lua-language-server — Lua LSP for Hyprland config editing in neovim
|
|
# micro — beginner-friendly terminal text editor (alternative to nano)
|
|
# pulsemixer — TUI PulseAudio/PipeWire mixer
|
|
# yazi — terminal file manager with image preview
|
|
# z — smart directory jumper (learns frequent paths)
|
|
# qrencode — QR code generator for terminal
|
|
# distrobox — run other Linux distros in containers
|
|
# dysk — disk usage summary (prettier df alternative)
|
|
# glow — render markdown in the terminal
|
|
# notmuch — fast email indexer (used by alot mail client)
|
|
# alot — TUI email client built on notmuch
|
|
log "Installing base shell packages..."
|
|
PACKAGES=(zsh neovim curl pyright bash atftp bash-language-server btop clang fastfetch fzf hyfetch lua-language-server micro nano pulsemixer yazi z qrencode distrobox dysk python python-pip glow notmuch alot)
|
|
for pkg in "${PACKAGES[@]}"; do
|
|
# -Qi queries the local package database; if it returns non-zero, pkg is not installed
|
|
if ! pacman -Qi "$pkg" &>/dev/null; then
|
|
log "Installing $pkg..."
|
|
# --needed: skip if already at latest version (double-safety with -Qi check)
|
|
sudo pacman -S --noconfirm --needed "$pkg"
|
|
else
|
|
skip "$pkg already installed."
|
|
fi
|
|
done
|
|
|
|
# ── abook (AUR) ───────────────────────────────────────────────────────────────
|
|
# WHY: abook is an address book app that integrates with mail clients like alot.
|
|
# It is only available in the AUR (not in official repos), so we use yay.
|
|
# IDEMPOTENCY: Check if `abook` binary exists before installing.
|
|
if ! command -v abook &>/dev/null; then
|
|
log "Installing abook (AUR)..."
|
|
yay -S --noconfirm --needed abook
|
|
else
|
|
skip "abook already installed."
|
|
fi
|
|
|
|
# ── yay fallback ──────────────────────────────────────────────────────────────
|
|
# WHY: shell-setup.sh can run standalone (e.g. on an existing system that didn't
|
|
# go through package-managers.sh first). This guards against yay being absent.
|
|
# HOW: Build yay from the AUR using its PKGBUILD. /tmp/yay is ephemeral but fine
|
|
# for a build that completes synchronously.
|
|
if ! command -v yay &>/dev/null; then
|
|
log "Installing yay..."
|
|
sudo pacman -S --noconfirm --needed git base-devel
|
|
git clone https://aur.archlinux.org/yay.git /tmp/yay
|
|
cd /tmp/yay && makepkg -si --noconfirm
|
|
cd ~
|
|
else
|
|
skip "yay already installed."
|
|
fi
|
|
|
|
# ── Rust / Cargo ──────────────────────────────────────────────────────────────
|
|
# WHY: Some shell tools are installed via `cargo install`. This provides the
|
|
# Rust toolchain in the user's home directory (~/.cargo/) separate from
|
|
# any system-wide Rust installation.
|
|
# NOTE: Uses the official rustup installer script rather than the pacman package
|
|
# because it installs to ~/.cargo which is user-owned (no sudo needed for
|
|
# cargo install later). The -y flag disables interactive prompts.
|
|
if ! command -v cargo &>/dev/null; then
|
|
log "Installing Rust & Cargo..."
|
|
# --proto: restrict to HTTPS only for security
|
|
# --tlsv1.2: require TLS 1.2 minimum
|
|
# -s -- -y: pass -y to the installer script (non-interactive)
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
# Source cargo env so `cargo` is available in this shell session immediately
|
|
. "$HOME/.cargo/env"
|
|
else
|
|
skip "Rust & Cargo already installed."
|
|
fi
|
|
|
|
# ── nvm + Node.js ─────────────────────────────────────────────────────────────
|
|
# WHY: Same rationale as in package-managers.sh. shell-setup.sh can run
|
|
# independently so nvm is bootstrapped here as a fallback.
|
|
if ! command -v node &>/dev/null; then
|
|
log "Installing nvm and Node.js..."
|
|
if [ ! -d "$HOME/.nvm" ]; then
|
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
|
|
fi
|
|
# Source nvm into current shell to make the `nvm` command available immediately
|
|
. "$HOME/.nvm/nvm.sh"
|
|
nvm install 22
|
|
else
|
|
skip "Node.js already installed."
|
|
fi
|
|
|
|
# ── Git global configuration ───────────────────────────────────────────────────
|
|
# WHY: Set neovim as the commit message editor so that `git commit` without -m
|
|
# opens neovim instead of vi/nano. This is user-global config (not per-repo).
|
|
log "Configuring git..."
|
|
git config --global core.editor nvim
|
|
|
|
# ── Dotfile deployment ────────────────────────────────────────────────────────
|
|
# WHY: We use symlinks (ln -sf) rather than copies wherever possible so that
|
|
# edits to files in ~/Dotfiles/ are immediately reflected in the live config
|
|
# without needing to re-run this script.
|
|
# EXCEPTION: micro/ is copied because micro stores plugin state files in its
|
|
# config directory — symlinks would write plugin data back to the repo.
|
|
log "Deploying dotfiles..."
|
|
mkdir -p ~/.config ~/Pictures
|
|
|
|
# Shell init files — symlink so edits in the repo apply immediately
|
|
ln -sf ~/Dotfiles/.bashrc ~/.bashrc
|
|
ln -sf ~/Dotfiles/.zshrc ~/.zshrc
|
|
|
|
# Starship prompt config — symlink into ~/.config/
|
|
ln -sf ~/Dotfiles/starship.toml ~/.config/starship.toml
|
|
|
|
# Micro editor — copy (not symlink) because micro writes plugin/state data into
|
|
# its config directory and we don't want those writes going into the git repo
|
|
rm -rf ~/.config/micro
|
|
cp -r ~/Dotfiles/micro ~/.config/
|
|
|
|
# Neovim — symlink the entire config directory to the repo
|
|
rm -rf ~/.config/nvim
|
|
ln -sf ~/Dotfiles/nvim ~/.config/nvim
|
|
|
|
# ── Neovim plugin sync ────────────────────────────────────────────────────────
|
|
# WHY: lazy.nvim (the Neovim plugin manager) needs to download and compile plugins
|
|
# on first launch. We run it headlessly here so the first interactive launch
|
|
# is fast and plugins are ready.
|
|
# HOW: --headless runs neovim without a UI; +Lazy! sync triggers lazy.nvim's
|
|
# sync command; +qa quits after sync completes.
|
|
# || true: a failed sync is non-fatal — user can run :Lazy sync manually later.
|
|
log "Syncing neovim plugins (lazy.nvim)..."
|
|
nvim --headless "+Lazy! sync" +qa 2>/dev/null || true
|
|
|
|
# alot email client config — symlink
|
|
rm -rf ~/.config/alot
|
|
ln -sf ~/Dotfiles/alot ~/.config/alot
|
|
|
|
# yazi file manager config — symlink
|
|
rm -rf ~/.config/yazi
|
|
ln -sf ~/Dotfiles/yazi ~/.config/yazi
|
|
|
|
# spotify-tui config — symlink (for the TUI Spotify client)
|
|
rm -rf ~/.config/spotify-tui
|
|
ln -sf ~/Dotfiles/spotify-tui ~/.config/spotify-tui
|
|
|
|
# Copy FF (Fastfetch) logo SVG to ~/Pictures for the fastfetch config to reference
|
|
cp -f ~/Dotfiles/resources/fflogo.svg ~/Pictures/fflogo.svg
|
|
|
|
# ── Starship prompt ────────────────────────────────────────────────────────────
|
|
# WHY: Starship is a fast, customizable cross-shell prompt. The config is in
|
|
# dotfiles/starship.toml (symlinked above). The binary itself must be
|
|
# installed separately — the official install script handles arch/platform
|
|
# detection automatically.
|
|
# IDEMPOTENCY: Check for the `starship` binary before running the installer.
|
|
if ! command -v starship &>/dev/null; then
|
|
log "Installing Starship..."
|
|
# -sS: silent but show errors; --yes: non-interactive
|
|
curl -sS https://starship.rs/install.sh | sh -s -- --yes
|
|
else
|
|
skip "Starship already installed."
|
|
fi
|
|
|
|
# ── oh-my-zsh ─────────────────────────────────────────────────────────────────
|
|
# WHY: oh-my-zsh provides the plugin framework, completion system, and themes
|
|
# that our .zshrc configuration depends on. It installs to ~/.oh-my-zsh/.
|
|
# HOW: Download and run the official install script.
|
|
# RUNZSH=no: don't switch to zsh and start a new shell during install
|
|
# CHSH=no: don't automatically change the default shell (we do that explicitly below)
|
|
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
|
log "Installing oh-my-zsh..."
|
|
RUNZSH=no CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
|
|
else
|
|
skip "oh-my-zsh already installed."
|
|
fi
|
|
|
|
# ── oh-my-zsh plugins ─────────────────────────────────────────────────────────
|
|
# WHY: These two plugins are referenced in .zshrc and must exist in ZSH_CUSTOM.
|
|
# They are not bundled with oh-my-zsh and must be cloned separately.
|
|
# ZSH_CUSTOM defaults to ~/.oh-my-zsh/custom/ — the standard plugin install location.
|
|
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
|
|
|
|
# zsh-syntax-highlighting: colors commands as you type (green=valid, red=invalid)
|
|
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ]; then
|
|
log "Installing zsh-syntax-highlighting..."
|
|
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git \
|
|
"$ZSH_CUSTOM/plugins/zsh-syntax-highlighting"
|
|
else
|
|
skip "zsh-syntax-highlighting already installed."
|
|
fi
|
|
|
|
# zsh-autosuggestions: suggests previously-typed commands in gray as you type
|
|
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ]; then
|
|
log "Installing zsh-autosuggestions..."
|
|
git clone https://github.com/zsh-users/zsh-autosuggestions \
|
|
"$ZSH_CUSTOM/plugins/zsh-autosuggestions"
|
|
else
|
|
skip "zsh-autosuggestions already installed."
|
|
fi
|
|
|
|
# ── Default shell change ───────────────────────────────────────────────────────
|
|
# WHY: New login shells still default to bash unless explicitly changed.
|
|
# `chsh` writes the new shell to /etc/passwd for this user.
|
|
# HOW: Compare current $SHELL to /usr/bin/zsh and change if different.
|
|
if [ "$SHELL" != "/usr/bin/zsh" ]; then
|
|
log "Setting zsh as default shell..."
|
|
chsh -s /usr/bin/zsh
|
|
else
|
|
skip "zsh is already the default shell."
|
|
fi
|
|
|
|
log "Shell setup complete."
|