460 lines
19 KiB
Bash
460 lines
19 KiB
Bash
#!/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 ""
|