Files
StianNOR-ZSHkit/setup_zsh.sh
2026-03-25 15:19:54 +01:00

508 lines
21 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# =============================================================================
# 🚀 StianNOR — ZSH + NERD FONT SETUP v2.0
# Merges font_nerd_hack.sh + setup_zsh.sh
# Supports: Arch/Manjaro, Debian/Ubuntu, Fedora, openSUSE, Alpine
# =============================================================================
set -euo pipefail
# --- Colors ---
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
LINE="────────────────────────────────────────────────────────────────────────────"
info() { echo -e "${CYAN} $*${RESET}"; }
success() { echo -e "${GREEN}$*${RESET}"; }
warn() { echo -e "${YELLOW}$*${RESET}"; }
error() { echo -e "${RED}$*${RESET}"; }
step() { echo -e "\n${BOLD}${BLUE}$*${RESET}"; echo -e "${BLUE}${LINE}${RESET}"; }
die() { error "$*"; exit 1; }
# --- Error trap ---
trap 'error "Failed at line $LINENO — command: $BASH_COMMAND"; exit 1' ERR
# =============================================================================
# 1. DETECT PACKAGE MANAGER
# =============================================================================
step "Detecting package manager"
detect_pm() {
if command -v pacman >/dev/null 2>&1; then PM="pacman"
elif command -v apt >/dev/null 2>&1; then PM="apt"
elif command -v dnf >/dev/null 2>&1; then PM="dnf"
elif command -v zypper >/dev/null 2>&1; then PM="zypper"
elif command -v apk >/dev/null 2>&1; then PM="apk"
else die "No supported package manager found."; fi
success "Package manager: $PM"
}
detect_pm
pkg_install() {
case "$PM" in
pacman) sudo pacman -S --noconfirm --needed "$@" ;;
apt) sudo apt install -y "$@" ;;
dnf) sudo dnf install -y "$@" ;;
zypper) sudo zypper install -y "$@" ;;
apk) sudo apk add "$@" ;;
esac
}
pkg_update() {
case "$PM" in
pacman) sudo pacman -Syu --noconfirm ;;
apt) sudo apt update ;;
dnf) sudo dnf check-update || true ;;
zypper) sudo zypper refresh ;;
apk) sudo apk update ;;
esac
}
# =============================================================================
# 2. DETECT ENVIRONMENT
# =============================================================================
step "Detecting desktop environment and terminals"
get_distro() {
[[ -f /etc/os-release ]] && { source /etc/os-release; echo "$NAME"; return; }
uname -s
}
get_desktop() {
local de="${XDG_CURRENT_DESKTOP,,}"
[[ -z "$de" ]] && de="${DESKTOP_SESSION,,}"
echo "$de"
}
get_terminals() {
local terms=()
for t in gnome-terminal konsole alacritty kitty wezterm xfce4-terminal mate-terminal tilix; do
pgrep -x "$t" >/dev/null 2>&1 && terms+=("$t")
done
# Also check $TERM_PROGRAM
[[ -n "${TERM_PROGRAM:-}" ]] && terms+=("${TERM_PROGRAM,,}")
# Deduplicate
printf '%s\n' "${terms[@]}" | sort -u | tr '\n' ' '
}
DISTRO_NAME=$(get_distro)
DESKTOP_ENV=$(get_desktop)
TERMINALS=$(get_terminals)
info "Distro: $DISTRO_NAME"
info "Desktop: ${DESKTOP_ENV:-unknown}"
info "Terminals: ${TERMINALS:-none detected}"
# =============================================================================
# 3. INSTALL DEPENDENCIES
# =============================================================================
step "Installing core dependencies"
pkg_update
case "$PM" in
pacman) pkg_install curl git wget unzip ruby gcc make ;;
apt) sudo apt install -y curl git wget unzip ruby ruby-dev gcc make build-essential ;;
dnf) sudo dnf install -y curl git wget unzip ruby ruby-devel gcc make ;;
zypper) sudo zypper install -y curl git wget unzip ruby ruby-devel gcc make ;;
apk) sudo apk add curl git wget unzip ruby ruby-dev gcc make ;;
esac
success "Core dependencies installed"
# =============================================================================
# 4. INSTALL HACK NERD FONT
# =============================================================================
step "Installing Hack Nerd Font"
FONT_DIR="/usr/local/share/fonts/nerd-fonts"
FONT_ALIAS="Hack Nerd Font"
FONT_TMPDIR=$(mktemp -d)
FONT_VERSION="v3.4.0"
# Ensure fontconfig tools available
command -v fc-cache >/dev/null 2>&1 || pkg_install fontconfig
cleanup_font_tmp() { rm -rf "$FONT_TMPDIR"; }
trap 'cleanup_font_tmp; error "Failed at line $LINENO — command: $BASH_COMMAND"; exit 1' ERR
trap 'cleanup_font_tmp' EXIT
info "Downloading Hack Nerd Font $FONT_VERSION..."
FONT_URL="https://github.com/ryanoasis/nerd-fonts/releases/download/${FONT_VERSION}/Hack.zip"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$FONT_URL" -o "$FONT_TMPDIR/Hack.zip" || die "Font download failed"
else
wget -q "$FONT_URL" -O "$FONT_TMPDIR/Hack.zip" || die "Font download failed"
fi
info "Extracting fonts..."
unzip -q "$FONT_TMPDIR/Hack.zip" -d "$FONT_TMPDIR/Hack" || die "Font extraction failed"
sudo mkdir -p "$FONT_DIR"
# Only copy TTF/OTF — skip Windows-compat and licence files
find "$FONT_TMPDIR/Hack" -name "*.ttf" -o -name "*.otf" | while read -r f; do
[[ "$f" =~ (Windows|LICENSE|README) ]] && continue
sudo cp "$f" "$FONT_DIR/"
done
sudo fc-cache -f "$FONT_DIR"
success "Hack Nerd Font installed to $FONT_DIR"
# --- Apply to Desktop Environment ---
FONT_GENERAL="$FONT_ALIAS 12"
FONT_MONO="$FONT_ALIAS 11"
FONT_SMALL="$FONT_ALIAS 8"
FONT_OTHER="$FONT_ALIAS 10"
apply_kde_fonts() {
local kwrite
if command -v kwriteconfig6 >/dev/null 2>&1; then kwrite="kwriteconfig6"
elif command -v kwriteconfig5 >/dev/null 2>&1; then kwrite="kwriteconfig5"
else warn "kwriteconfig not found — KDE font config skipped"; return; fi
$kwrite --file kdeglobals --group General --key font "$FONT_ALIAS,12,-1,5,50,0,0,0,0,0"
$kwrite --file kdeglobals --group Fixed --key font "$FONT_ALIAS,11,-1,5,50,0,0,0,0,0"
$kwrite --file kdeglobals --group Small --key font "$FONT_ALIAS,8,-1,5,50,0,0,0,0,0"
$kwrite --file kdeglobals --group Toolbar --key font "$FONT_ALIAS,10,-1,5,50,0,0,0,0,0"
$kwrite --file kdeglobals --group Menu --key font "$FONT_ALIAS,10,-1,5,50,0,0,0,0,0"
$kwrite --file kdeglobals --group "Window Title" --key font "$FONT_ALIAS,10,-1,5,50,0,0,0,0,0"
$kwrite --file kdeglobals --group General --key antialiasing "true"
$kwrite --file kdeglobals --group General --key hinting "slight"
$kwrite --file kdeglobals --group General --key subpixelRendering "rgb"
$kwrite --file kdeglobals --group General --key forceFontDpi 96
# Konsole profiles
find ~/.local/share/konsole -name "*.profile" 2>/dev/null \
-exec sed -i "s/^Font=.*/Font=$FONT_ALIAS,12,-1,5,50,0,0,0,0,0,Regular/" {} \;
success "KDE fonts applied via $kwrite"
}
apply_gnome_fonts() {
command -v gsettings >/dev/null 2>&1 || { warn "gsettings not found — GNOME font config skipped"; return; }
gsettings set org.gnome.desktop.interface font-name "$FONT_GENERAL"
gsettings set org.gnome.desktop.interface monospace-font-name "$FONT_MONO"
gsettings set org.gnome.desktop.interface document-font-name "$FONT_GENERAL"
# GNOME Terminal profiles via dconf
if command -v dconf >/dev/null 2>&1; then
while IFS= read -r profile; do
[[ -z "$profile" ]] && continue
dconf write "/org/gnome/terminal/legacy/profiles:/:${profile}/use-system-font" "false"
dconf write "/org/gnome/terminal/legacy/profiles:/:${profile}/font" "'$FONT_MONO'"
done < <(dconf list /org/gnome/terminal/legacy/profiles:/ 2>/dev/null | grep -oP '[^/:]+' || true)
fi
success "GNOME/Cinnamon fonts applied"
}
apply_xfce_fonts() {
command -v xfconf-query >/dev/null 2>&1 || { warn "xfconf-query not found — XFCE font config skipped"; return; }
xfconf-query -c xsettings -p /Gtk/FontName -s "$FONT_GENERAL" 2>/dev/null || true
xfconf-query -c xfwm4 -p /general/font -s "$FONT_GENERAL" 2>/dev/null || true
local xterm_conf="$HOME/.config/xfce4/terminal/terminalrc"
if [[ -f "$xterm_conf" ]]; then
sed -i "s/^FontName=.*/FontName=$FONT_MONO/" "$xterm_conf"
fi
success "XFCE fonts applied"
}
apply_mate_fonts() {
command -v gsettings >/dev/null 2>&1 || { warn "gsettings not found — MATE font config skipped"; return; }
gsettings set org.mate.interface font-name "$FONT_GENERAL" 2>/dev/null || true
success "MATE fonts applied"
}
case "$DESKTOP_ENV" in
*kde*|*plasma*) apply_kde_fonts ;;
*gnome*|*cinnamon*|*unity*) apply_gnome_fonts ;;
*xfce*) apply_xfce_fonts ;;
*mate*) apply_mate_fonts ;;
*) warn "Desktop '$DESKTOP_ENV' not specifically handled — font installed system-wide via fontconfig" ;;
esac
# --- Apply to detected terminal emulators ---
apply_terminal_font() {
local term="$1"
case "$term" in
alacritty)
# Support both .yml (old) and .toml (new Alacritty >=0.13)
local conf_toml="$HOME/.config/alacritty/alacritty.toml"
local conf_yml="$HOME/.config/alacritty/alacritty.yml"
if [[ -f "$conf_toml" ]]; then
if grep -q '^\[font\]' "$conf_toml"; then
sed -i '/^\[font\]/,/^\[/{s/^family = .*/family = "'"$FONT_ALIAS"'"/}' "$conf_toml"
else
printf '\n[font]\n[font.normal]\nfamily = "%s"\n[font.bold]\nfamily = "%s"\n[font.italic]\nfamily = "%s"\n\n[font.size]\nsize = 12.0\n' \
"$FONT_ALIAS" "$FONT_ALIAS" "$FONT_ALIAS" >> "$conf_toml"
fi
success "Alacritty TOML font updated"
elif [[ -f "$conf_yml" ]]; then
sed -i "/^ *family:/c\\ family: \"$FONT_ALIAS\"" "$conf_yml"
sed -i "/^ *size:/c\\ size: 12" "$conf_yml"
success "Alacritty YAML font updated"
else
warn "Alacritty config not found at $conf_toml or $conf_yml — skipping"
fi
;;
kitty)
local kconf="$HOME/.config/kitty/kitty.conf"
mkdir -p "$(dirname "$kconf")"
if [[ -f "$kconf" ]]; then
sed -i "/^font_family/c\\font_family $FONT_ALIAS" "$kconf"
sed -i "/^font_size/c\\font_size 12.0" "$kconf"
else
printf 'font_family %s\nfont_size 12.0\n' "$FONT_ALIAS" > "$kconf"
fi
success "Kitty font configured"
;;
wezterm)
local wconf="$HOME/.config/wezterm/wezterm.lua"
if [[ -f "$wconf" ]]; then
warn "WezTerm config found at $wconf — add this manually:"
echo " config.font = wezterm.font('$FONT_ALIAS')"
fi
;;
konsole)
find ~/.local/share/konsole -name "*.profile" 2>/dev/null \
-exec sed -i "s/^Font=.*/Font=$FONT_ALIAS,12,-1,5,50,0,0,0,0,0,Regular/" {} \;
success "Konsole profiles updated"
;;
xfce4-terminal)
local xterm_conf="$HOME/.config/xfce4/terminal/terminalrc"
[[ -f "$xterm_conf" ]] && sed -i "s/^FontName=.*/FontName=$FONT_MONO/" "$xterm_conf"
success "XFCE Terminal font updated"
;;
gnome-terminal) ;; # handled by apply_gnome_fonts via dconf
*) warn "Terminal '$term' font config not supported — set manually" ;;
esac
}
for term in $TERMINALS; do
apply_terminal_font "$term"
done
# --- fontconfig fallback (monospace alias) ---
sudo tee /etc/fonts/local.conf > /dev/null <<FONTCONF
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
<alias>
<family>monospace</family>
<prefer>
<family>${FONT_ALIAS}</family>
</prefer>
</alias>
<alias>
<family>Hack Nerd Font Mono</family>
<prefer>
<family>${FONT_ALIAS}</family>
</prefer>
</alias>
</fontconfig>
FONTCONF
sudo fc-cache -f
success "fontconfig monospace alias set"
# NOTE: TTY/vconsole fonts must be bitmap/psf fonts — Nerd Font TTFs
# cannot be used in vconsole.conf. Skipping to avoid breaking TTY.
# =============================================================================
# 5. INSTALL ZSH
# =============================================================================
step "Installing Zsh"
if ! command -v zsh >/dev/null 2>&1; then
pkg_install zsh
success "Zsh installed"
else
success "Zsh already installed: $(zsh --version)"
fi
# =============================================================================
# 6. UPDATE RUBYGEMS + INSTALL COLORLS
# =============================================================================
step "Setting up Ruby gems (colorls)"
export GEM_HOME="$HOME/.gem"
export PATH="$PATH:$GEM_HOME/bin"
USER_GEM_BIN="$(ruby -e 'puts Gem.user_dir' 2>/dev/null)/bin"
# Update gems — skip system update on apt (breaks Debian policy)
if [[ "$PM" != "apt" ]]; then
gem update --system 2>/dev/null && success "RubyGems system updated" \
|| warn "RubyGems system update failed — continuing"
fi
gem update 2>/dev/null && success "All gems updated" || warn "gem update had warnings — continuing"
if gem list -i colorls >/dev/null 2>&1; then
success "colorls already installed"
else
info "Installing colorls..."
gem install --user-install colorls || die "colorls install failed"
success "colorls installed"
fi
# Add gem bin to PATH in both .zshrc and .bashrc so it works during this session too
for rc in "$HOME/.zshrc" "$HOME/.bashrc"; do
if [[ -f "$rc" ]] && ! grep -q "$USER_GEM_BIN" "$rc" 2>/dev/null; then
echo "export PATH=\"\$PATH:$USER_GEM_BIN\"" >> "$rc"
info "Added gem bin to PATH in $rc"
fi
done
export PATH="$PATH:$USER_GEM_BIN"
# =============================================================================
# 7. INSTALL FASTFETCH
# =============================================================================
step "Installing fastfetch"
if command -v fastfetch >/dev/null 2>&1; then
success "fastfetch already installed: $(fastfetch --version 2>/dev/null | head -1)"
else
info "Trying package manager first..."
installed=false
case "$PM" in
pacman) sudo pacman -S --noconfirm --needed fastfetch 2>/dev/null && installed=true ;;
apt)
# fastfetch not in standard apt — try PPA or direct .deb download
if sudo add-apt-repository -y ppa:zhangsongcui3371/fastfetch 2>/dev/null; then
sudo apt update && sudo apt install -y fastfetch && installed=true
fi ;;
dnf) sudo dnf install -y fastfetch 2>/dev/null && installed=true ;;
zypper) sudo zypper install -y fastfetch 2>/dev/null && installed=true ;;
apk) sudo apk add fastfetch 2>/dev/null && installed=true ;;
esac
if [[ "$installed" == false ]]; then
warn "Package manager install failed — downloading latest binary release..."
ARCH=$(uname -m)
[[ "$ARCH" == "x86_64" ]] && ARCH_NAME="amd64" || ARCH_NAME="$ARCH"
FF_URL=$(curl -s https://api.github.com/repos/fastfetch-cli/fastfetch/releases/latest \
| grep "browser_download_url" \
| grep "linux-${ARCH_NAME}.tar.gz" \
| head -1 | cut -d'"' -f4)
if [[ -n "$FF_URL" ]]; then
FF_TMP=$(mktemp -d)
curl -fsSL "$FF_URL" -o "$FF_TMP/fastfetch.tar.gz"
tar -xzf "$FF_TMP/fastfetch.tar.gz" -C "$FF_TMP"
sudo install -m755 "$FF_TMP"/fastfetch*/usr/bin/fastfetch /usr/local/bin/fastfetch 2>/dev/null \
|| sudo cp "$(find "$FF_TMP" -name fastfetch -type f | head -1)" /usr/local/bin/fastfetch
sudo chmod +x /usr/local/bin/fastfetch
rm -rf "$FF_TMP"
installed=true
fi
fi
if [[ "$installed" == true ]] && command -v fastfetch >/dev/null 2>&1; then
success "fastfetch installed: $(fastfetch --version 2>/dev/null | head -1)"
else
warn "fastfetch could not be installed automatically — install it manually"
fi
fi
# =============================================================================
# 8. INSTALL OH MY ZSH
# =============================================================================
step "Installing Oh My Zsh"
if [[ -f "$HOME/.oh-my-zsh/oh-my-zsh.sh" ]]; then
success "Oh My Zsh already installed"
else
info "Installing Oh My Zsh..."
rm -rf "$HOME/.oh-my-zsh"
RUNZSH=no KEEP_ZSHRC=yes \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \
|| die "Oh My Zsh install failed"
success "Oh My Zsh installed"
fi
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
# =============================================================================
# 9. INSTALL POWERLEVEL10K
# =============================================================================
step "Installing Powerlevel10k theme"
P10K_DIR="$ZSH_CUSTOM/themes/powerlevel10k"
if [[ -d "$P10K_DIR" ]]; then
info "Updating Powerlevel10k..."
git -C "$P10K_DIR" pull --ff-only 2>/dev/null && success "Powerlevel10k updated" || warn "p10k update failed — skipping"
else
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "$P10K_DIR" \
|| die "Powerlevel10k install failed"
success "Powerlevel10k installed"
fi
# =============================================================================
# 10. INSTALL ZSH PLUGINS
# =============================================================================
step "Installing Zsh plugins"
declare -A ZSH_PLUGINS=(
[zsh-autosuggestions]="https://github.com/zsh-users/zsh-autosuggestions"
[zsh-syntax-highlighting]="https://github.com/zsh-users/zsh-syntax-highlighting"
[zsh-completions]="https://github.com/zsh-users/zsh-completions"
)
for plugin in "${!ZSH_PLUGINS[@]}"; do
local_dir="$ZSH_CUSTOM/plugins/$plugin"
if [[ -d "$local_dir" ]]; then
git -C "$local_dir" pull --ff-only 2>/dev/null && success "$plugin updated" || warn "$plugin update skipped"
else
info "Installing $plugin..."
git clone "${ZSH_PLUGINS[$plugin]}" "$local_dir" || warn "$plugin install failed — continuing"
success "$plugin installed"
fi
done
# =============================================================================
# 11. COPY .zshrc AND .p10k.zsh
# =============================================================================
step "Applying .zshrc and .p10k.zsh"
# Use the directory where THIS script lives as the source
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
copy_if_exists() {
local src="$1" dst="$2" label="$3"
if [[ -f "$src" ]]; then
cp "$src" "$dst"
success "$label$dst"
else
warn "$src not found — $label not copied"
fi
}
copy_if_exists "$SCRIPT_DIR/.zshrc" "$HOME/.zshrc" ".zshrc"
copy_if_exists "$SCRIPT_DIR/.p10k.zsh" "$HOME/.p10k.zsh" ".p10k.zsh"
# Ensure gem bin and zsh path are in the copied .zshrc
for line in \
"export PATH=\"\$PATH:$USER_GEM_BIN\"" \
"export GEM_HOME=\"\$HOME/.gem\""; do
grep -qF "$line" "$HOME/.zshrc" 2>/dev/null || echo "$line" >> "$HOME/.zshrc"
done
# =============================================================================
# 12. SET ZSH AS DEFAULT SHELL
# =============================================================================
step "Setting Zsh as default shell"
ZSH_PATH=$(command -v zsh)
if [[ "$SHELL" == "$ZSH_PATH" ]]; then
success "Zsh is already the default shell"
else
# Make sure zsh is in /etc/shells
grep -qxF "$ZSH_PATH" /etc/shells || echo "$ZSH_PATH" | sudo tee -a /etc/shells
if chsh -s "$ZSH_PATH" 2>/dev/null; then
success "Default shell set to $ZSH_PATH"
info "Log out and back in for the shell change to take effect"
else
warn "Could not change default shell — run manually: chsh -s $ZSH_PATH"
fi
fi
# =============================================================================
# DONE
# =============================================================================
echo ""
echo -e "${LINE}"
echo -e "${BOLD}${GREEN} ✅ StianNOR Zsh + Nerd Font Setup Complete!${RESET}"
echo -e "${LINE}"
echo -e " ${CYAN}Font:${RESET} Hack Nerd Font installed system-wide"
echo -e " ${CYAN}Shell:${RESET} Zsh + Oh My Zsh + Powerlevel10k"
echo -e " ${CYAN}Plugins:${RESET} autosuggestions, syntax-highlighting, completions"
echo -e " ${CYAN}Tools:${RESET} colorls, fastfetch"
echo -e "${LINE}"
echo -e " ${YELLOW}Next steps:${RESET}"
echo -e " 1. Log out and back in (shell change + font)"
echo -e " 2. Run ${CYAN}p10k configure${RESET} to set up your prompt"
echo -e " 3. In your terminal settings, select ${CYAN}Hack Nerd Font${RESET} manually if needed"
echo -e "${LINE}"
echo ""