#!/bin/bash # ============================================================================= # 🗑️ StianNOR — COMPLETE UNINSTALLER v1.0 # Uninstalls everything installed by: # - setup_zsh_font.sh (Zsh, Oh My Zsh, p10k, plugins, colorls, fastfetch, Hack Nerd Font) # - portainerup.sh (Portainer CE, Docker, Docker Compose) # ============================================================================= set -uo pipefail 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}"; } skip() { echo -e " ${BLUE}↷ $* — not found, skipping${RESET}"; } # ============================================================================= # DETECT 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 PM="unknown"; fi } detect_pm pkg_remove() { case "$PM" in pacman) sudo pacman -Rns --noconfirm "$@" 2>/dev/null || true ;; apt) sudo apt remove -y --purge "$@" 2>/dev/null || true; sudo apt autoremove -y 2>/dev/null || true ;; dnf) sudo dnf remove -y "$@" 2>/dev/null || true ;; zypper) sudo zypper remove -y "$@" 2>/dev/null || true ;; apk) sudo apk del "$@" 2>/dev/null || true ;; esac } # ============================================================================= # CONFIRM — show full plan before doing anything # ============================================================================= clear echo -e "${LINE}" echo -e "${BOLD}${RED} 🗑️ StianNOR COMPLETE UNINSTALLER${RESET}" echo -e "${LINE}" echo "" echo -e " This will remove the following — ${BOLD}select what to uninstall:${RESET}" echo "" echo -e " ${BOLD}${CYAN}[A] ZSH + FONT SETUP${RESET}" echo -e " • Hack Nerd Font (system-wide)" echo -e " • fontconfig monospace alias (/etc/fonts/local.conf)" echo -e " • Oh My Zsh + Powerlevel10k + all plugins" echo -e " • colorls Ruby gem" echo -e " • fastfetch" echo -e " • ~/.zshrc and ~/.p10k.zsh" echo -e " • Default shell reverted to bash" echo "" echo -e " ${BOLD}${CYAN}[B] PORTAINER + DOCKER${RESET}" echo -e " • Portainer CE container + data volume" echo -e " • Docker CE + Docker Compose" echo -e " • docker group membership for $USER" echo -e " • Docker config dir (~/.docker)" echo "" echo -e " ${BOLD}${CYAN}[C] BOTH (full wipe)${RESET}" echo "" echo -e "${LINE}" echo -e " ${YELLOW}[q]${RESET} cancel and exit" echo -e "${LINE}" echo "" printf " Choice: " read -r CHOICE echo "" case "${CHOICE,,}" in a) DO_ZSH=true; DO_DOCKER=false ;; b) DO_ZSH=false; DO_DOCKER=true ;; c) DO_ZSH=true; DO_DOCKER=true ;; q|"") echo -e " ${YELLOW}Cancelled.${RESET}"; exit 0 ;; *) echo -e " ${RED}Invalid choice. Exiting.${RESET}"; exit 1 ;; esac # Final confirm echo -e "${LINE}" echo -e " ${BOLD}${RED}This is destructive and cannot be undone.${RESET}" printf " Type ${BOLD}YES${RESET} to confirm: " read -r CONFIRM echo "" [[ "$CONFIRM" != "YES" ]] && echo -e " ${YELLOW}Aborted.${RESET}" && exit 0 # ============================================================================= # LOG FILE # ============================================================================= LOG_DIR="$HOME/Homelab/logs" mkdir -p "$LOG_DIR" 2>/dev/null || LOG_DIR="/tmp" LOG_FILE="$LOG_DIR/uninstall-$(date +%Y%m%d-%H%M%S).log" exec > >(tee -a "$LOG_FILE") 2>&1 info "Logging to $LOG_FILE" # ============================================================================= # A — UNINSTALL ZSH + FONT SETUP # ============================================================================= uninstall_zsh_font() { # ── 1. Revert default shell to bash ────────────────────────────────────── step "Reverting default shell to bash" BASH_PATH=$(command -v bash 2>/dev/null || echo "/bin/bash") CURRENT_SHELL=$(getent passwd "$USER" | cut -d: -f7) if [[ "$CURRENT_SHELL" == *zsh* ]]; then grep -qxF "$BASH_PATH" /etc/shells || echo "$BASH_PATH" | sudo tee -a /etc/shells if chsh -s "$BASH_PATH" 2>/dev/null; then success "Default shell reverted to $BASH_PATH" else warn "Could not change shell automatically — run: chsh -s $BASH_PATH" fi else skip "Shell was not zsh ($CURRENT_SHELL)" fi # ── 2. Remove Oh My Zsh ────────────────────────────────────────────────── step "Removing Oh My Zsh" if [[ -d "$HOME/.oh-my-zsh" ]]; then rm -rf "$HOME/.oh-my-zsh" success "Oh My Zsh removed" else skip "~/.oh-my-zsh" fi # ── 3. Remove Powerlevel10k (standalone clone, if outside OMZ) ─────────── step "Removing Powerlevel10k" for p10k_path in \ "$HOME/.oh-my-zsh/custom/themes/powerlevel10k" \ "$HOME/powerlevel10k" \ "${ZSH_CUSTOM:-}/themes/powerlevel10k"; do if [[ -d "$p10k_path" ]]; then rm -rf "$p10k_path" success "Removed $p10k_path" fi done # ── 4. Remove Zsh plugin dirs ───────────────────────────────────────────── step "Removing Zsh plugins" for plugin in zsh-autosuggestions zsh-syntax-highlighting zsh-completions; do PLUGIN_DIR="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/$plugin" if [[ -d "$PLUGIN_DIR" ]]; then rm -rf "$PLUGIN_DIR" success "Removed plugin: $plugin" else skip "Plugin $plugin" fi done # ── 5. Remove colorls gem ───────────────────────────────────────────────── step "Removing colorls Ruby gem" if gem list -i colorls >/dev/null 2>&1; then gem uninstall colorls -a -x 2>/dev/null && success "colorls removed" \ || warn "colorls uninstall had warnings" else skip "colorls gem" fi # Clean up gem home if empty GEM_HOME_DIR="$HOME/.gem" if [[ -d "$GEM_HOME_DIR" ]]; then REMAINING=$(find "$GEM_HOME_DIR" -name "*.gemspec" 2>/dev/null | wc -l) if [[ "$REMAINING" -eq 0 ]]; then rm -rf "$GEM_HOME_DIR" success "Removed empty ~/.gem" else info "$REMAINING other gems remain in ~/.gem — leaving it" fi fi # ── 6. Remove fastfetch ─────────────────────────────────────────────────── step "Removing fastfetch" if command -v fastfetch >/dev/null 2>&1; then FF_PATH=$(command -v fastfetch) # If installed via package manager removed=false case "$PM" in pacman) sudo pacman -Rns --noconfirm fastfetch 2>/dev/null && removed=true ;; apt) sudo apt remove -y --purge fastfetch 2>/dev/null && removed=true ;; dnf) sudo dnf remove -y fastfetch 2>/dev/null && removed=true ;; zypper) sudo zypper remove -y fastfetch 2>/dev/null && removed=true ;; apk) sudo apk del fastfetch 2>/dev/null && removed=true ;; esac # If it was a manually installed binary if [[ "$removed" == false ]] || command -v fastfetch >/dev/null 2>&1; then sudo rm -f /usr/local/bin/fastfetch /usr/bin/fastfetch 2>/dev/null || true fi command -v fastfetch >/dev/null 2>&1 \ && warn "fastfetch still found at $(command -v fastfetch) — remove manually" \ || success "fastfetch removed" else skip "fastfetch" fi # ── 7. Remove Zsh itself ────────────────────────────────────────────────── step "Removing Zsh" if command -v zsh >/dev/null 2>&1; then printf " Remove zsh package itself? (leaves system zsh on some distros) [y/N]: " read -r yn if [[ "${yn,,}" == "y" ]]; then pkg_remove zsh command -v zsh >/dev/null 2>&1 \ && warn "zsh still present (may be a system dependency)" \ || success "zsh removed" else info "Zsh package kept (shell reverted to bash above)" fi else skip "zsh (not installed)" fi # ── 8. Remove dot files ─────────────────────────────────────────────────── step "Removing ~/.zshrc and ~/.p10k.zsh" for f in "$HOME/.zshrc" "$HOME/.p10k.zsh" "$HOME/.zsh_history"; do if [[ -f "$f" ]]; then # Back up before deleting cp "$f" "${f}.uninstall-backup-$(date +%Y%m%d)" 2>/dev/null || true rm -f "$f" success "Removed $f (backup saved as ${f}.uninstall-backup-*)" else skip "$f" fi done # ── 9. Remove Hack Nerd Font ────────────────────────────────────────────── step "Removing Hack Nerd Font" FONT_DIR="/usr/local/share/fonts/nerd-fonts" if [[ -d "$FONT_DIR" ]]; then sudo rm -rf "$FONT_DIR" sudo fc-cache -f 2>/dev/null || true success "Hack Nerd Font removed from $FONT_DIR" else skip "$FONT_DIR" fi # Also check user font dir USER_FONT_DIR="$HOME/.local/share/fonts/nerd-fonts" if [[ -d "$USER_FONT_DIR" ]]; then rm -rf "$USER_FONT_DIR" fc-cache -f "$HOME/.local/share/fonts" 2>/dev/null || true success "Removed user font dir $USER_FONT_DIR" fi # ── 10. Remove fontconfig override ─────────────────────────────────────── step "Removing fontconfig monospace alias" if [[ -f /etc/fonts/local.conf ]]; then if grep -q "Hack Nerd Font" /etc/fonts/local.conf 2>/dev/null; then sudo rm -f /etc/fonts/local.conf sudo fc-cache -f 2>/dev/null || true success "Removed /etc/fonts/local.conf" else info "/etc/fonts/local.conf exists but doesn't contain Hack Nerd Font — leaving it" fi else skip "/etc/fonts/local.conf" fi # ── 11. Clean up PATH entries added to shell rc files ──────────────────── step "Cleaning gem PATH entries from shell rc files" for rc in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.bash_profile" "$HOME/.profile"; do if [[ -f "$rc" ]]; then if grep -q "\.gem" "$rc" 2>/dev/null; then sed -i '/\.gem\/ruby/d' "$rc" 2>/dev/null || true sed -i '/GEM_HOME/d' "$rc" 2>/dev/null || true success "Cleaned gem PATH from $rc" fi fi done } # ============================================================================= # B — UNINSTALL PORTAINER + DOCKER # ============================================================================= uninstall_docker_portainer() { # ── 1. Stop and remove Portainer ───────────────────────────────────────── step "Removing Portainer CE" if command -v docker >/dev/null 2>&1; then DOCKER_CMD="docker" docker info >/dev/null 2>&1 || DOCKER_CMD="sudo docker" if $DOCKER_CMD ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^portainer$"; then $DOCKER_CMD stop portainer 2>/dev/null || true $DOCKER_CMD rm portainer 2>/dev/null || true success "Portainer container stopped and removed" else skip "Portainer container" fi # Remove portainer data volume if $DOCKER_CMD volume ls --format '{{.Name}}' 2>/dev/null | grep -q "^portainer_data$"; then printf " Remove portainer_data volume? This deletes all Portainer settings [y/N]: " read -r yn if [[ "${yn,,}" == "y" ]]; then $DOCKER_CMD volume rm portainer_data 2>/dev/null || true success "portainer_data volume removed" else info "portainer_data volume kept" fi else skip "portainer_data volume" fi # Remove the image if $DOCKER_CMD image ls --format '{{.Repository}}:{{.Tag}}' 2>/dev/null | grep -q "portainer/portainer-ce"; then printf " Remove Portainer CE docker image? [y/N]: " read -r yn if [[ "${yn,,}" == "y" ]]; then $DOCKER_CMD rmi portainer/portainer-ce:latest 2>/dev/null || true success "Portainer CE image removed" else info "Portainer CE image kept" fi fi else skip "Docker not installed — Portainer check skipped" fi # ── 2. Remove Docker ────────────────────────────────────────────────────── step "Removing Docker" if command -v docker >/dev/null 2>&1; then printf " ${BOLD}${RED}Remove Docker CE completely?${RESET} This removes ALL containers, images and volumes [y/N]: " read -r yn if [[ "${yn,,}" != "y" ]]; then info "Docker kept — only Portainer was removed" return fi # Stop daemon first sudo systemctl stop docker docker.socket 2>/dev/null || sudo service docker stop 2>/dev/null || true sudo systemctl disable docker docker.socket 2>/dev/null || true case "$PM" in pacman) sudo pacman -Rns --noconfirm docker docker-compose 2>/dev/null || true ;; apt) sudo apt remove -y --purge \ docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin \ docker-compose 2>/dev/null || true sudo apt autoremove -y 2>/dev/null || true # Remove Docker apt repo sudo rm -f /etc/apt/sources.list.d/docker.list sudo rm -f /etc/apt/keyrings/docker.gpg sudo apt update -qq 2>/dev/null || true ;; dnf) sudo dnf remove -y \ docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin 2>/dev/null || true sudo rm -f /etc/yum.repos.d/docker-ce.repo 2>/dev/null || true ;; zypper) sudo zypper remove -y docker docker-compose 2>/dev/null || true ;; apk) sudo apk del docker docker-compose 2>/dev/null || true sudo rc-update del docker boot 2>/dev/null || true ;; esac # Remove standalone docker-compose binary if present [[ -f /usr/local/bin/docker-compose ]] && sudo rm -f /usr/local/bin/docker-compose \ && success "Removed standalone docker-compose" success "Docker CE removed" else skip "Docker (not installed)" fi # ── 3. Remove Docker data directories ──────────────────────────────────── step "Removing Docker data directories" for dir in /var/lib/docker /var/lib/containerd /etc/docker; do if [[ -d "$dir" ]]; then printf " Remove %s? [y/N]: " "$dir" read -r yn if [[ "${yn,,}" == "y" ]]; then sudo rm -rf "$dir" success "Removed $dir" else info "Kept $dir" fi else skip "$dir" fi done # Remove user docker config if [[ -d "$HOME/.docker" ]]; then rm -rf "$HOME/.docker" success "Removed ~/.docker" fi # ── 4. Remove user from docker group ───────────────────────────────────── step "Removing $USER from docker group" if getent group docker >/dev/null 2>&1; then if groups "$USER" | grep -qw docker; then sudo gpasswd -d "$USER" docker 2>/dev/null \ && success "$USER removed from docker group" \ || warn "Could not remove $USER from docker group" else skip "$USER was not in docker group" fi printf " Delete the docker group itself? [y/N]: " read -r yn if [[ "${yn,,}" == "y" ]]; then sudo groupdel docker 2>/dev/null && success "docker group deleted" \ || warn "Could not delete docker group" fi else skip "docker group (does not exist)" fi # ── 5. Remove Docker socket and service files (if any remain) ──────────── step "Cleaning up Docker service files" for f in \ /lib/systemd/system/docker.service \ /lib/systemd/system/docker.socket \ /etc/systemd/system/docker.service \ /etc/systemd/system/docker.socket; do if [[ -f "$f" ]]; then sudo rm -f "$f" success "Removed $f" fi done sudo systemctl daemon-reload 2>/dev/null || true } # ============================================================================= # RUN SELECTED SECTIONS # ============================================================================= [[ "$DO_ZSH" == true ]] && uninstall_zsh_font [[ "$DO_DOCKER" == true ]] && uninstall_docker_portainer # ============================================================================= # DONE # ============================================================================= echo "" echo -e "${LINE}" echo -e "${BOLD}${GREEN} ✅ Uninstall complete!${RESET}" echo -e "${LINE}" [[ "$DO_ZSH" == true ]] && echo -e " ${CYAN}Zsh/Font:${RESET} removed — default shell reverted to bash" [[ "$DO_DOCKER" == true ]] && echo -e " ${CYAN}Docker:${RESET} removed — log out and back in to clear group session" echo -e " ${YELLOW}Log:${RESET} $LOG_FILE" echo -e "${LINE}" echo "" if [[ "$DO_ZSH" == true ]]; then echo -e " ${YELLOW}Note:${RESET} Log out and back in for shell change to take full effect." fi if [[ "$DO_DOCKER" == true ]]; then echo -e " ${YELLOW}Note:${RESET} Log out and back in to clear docker group from your session." fi echo ""