chore: add maintenance scripts and update editor preference

- Add scripts/cleanup.sh for package cleanup (dry-run/apply modes)
- Add scripts/nvidia-recovery.sh for broken GUI recovery
- Update AGENTS.md with new script references
- Change default editor from vim to nvim in bashrc

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
ja
2026-06-15 06:43:43 +02:00
co-authored by Junie
parent fe7f133136
commit de24990208
4 changed files with 607 additions and 1 deletions
+372
View File
@@ -0,0 +1,372 @@
#!/bin/bash
set -euo pipefail
# Workstation Package Cleanup Script
# Version: 1.0.0
# Target: mw-pfeddersheim-workstation
# Purpose: Remove identified cleanup candidates and log for verification
MODE="apply"
if [[ "${1:-}" == "--check" ]]; then
MODE="check"
echo "=== Running in CHECK mode (dry-run, no packages removed) ==="
elif [[ "${1:-}" == "--help" ]]; then
echo "Usage: $0 [--check | --apply | --help]"
echo " --check Dry-run: show what would be removed (default)"
echo " --apply Actually remove packages"
echo " --help Show this help"
exit 0
fi
# Ensure log directory exists
mkdir -p "$HOME/logs" 2>/dev/null || true
LOCAL_LOG="$HOME/logs/cleanup-$(date +%Y%m%d-%H%M%S).log"
BOLD=$(tput bold 2>/dev/null || true)
RED=$(tput setaf 1 2>/dev/null || true)
GREEN=$(tput setaf 2 2>/dev/null || true)
RESET=$(tput sgr0 2>/dev/null || true)
log() { echo -e "$*" | tee -a "$LOCAL_LOG"; }
section() { log ""; log "${BOLD}--- $1 ---${RESET}"; }
echo "" | tee -a "$LOCAL_LOG"
log "============================================="
log " Workstation Package Cleanup"
log " Mode: $MODE | Date: $(date)"
log " Log: $LOCAL_LOG"
log "============================================="
# Track totals
TOTAL_CANDIDATES=0
TOTAL_REMOVED=0
TOTAL_ERRORS=0
SKIPPED=0
# Helper: add package to candidate list file
add_candidates() {
local category="$1"
shift
echo "$@" >> "$HOME/logs/.cleanup-candidates-${category}.txt"
}
# ============================================================
# 1. PRE-FLIGHT CHECKS
# ============================================================
section "Pre-flight Checks"
# Verify pacman is available
if ! command -v pacman &> /dev/null; then
log "${RED}ERROR: pacman not found. This script must be run on Arch/Manjaro.${RESET}"
exit 1
fi
# Check disk space (need room for transaction)
DISK_PCT=$(df / | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
if [[ "$DISK_PCT" -gt 95 ]]; then
log "${RED}WARNING: Disk usage at ${DISK_PCT}%. Consider freeing space first.${RESET}"
fi
DISK_AVAIL=$(df -h / | awk 'NR==2 {print $4}')
log "Disk available: $DISK_AVAIL"
# Check for existing packages (only list ones that are installed)
log "Scanning for installed cleanup candidates..."
# ============================================================
# 2. OLD MULTIMEDIA PACKAGES
# ============================================================
section "[1/7] Old Multimedia Packages"
CANDIDATES=()
# Old ffmpeg version
if pacman -Qe ffmpeg4.4 &>/dev/null || pacman -Q ffmpeg4.4 &>/dev/null; then
CANDIDATES+=(ffmpeg4.4)
fi
# VLC plugins (keep base vlc)
while IFS= read -r pkg; do
[[ "$pkg" == "vlc" ]] && continue # skip base vlc
CANDIDATES+=("$pkg")
done < <(pacman -Qq 2>/dev/null | grep -E '^vlc-' || true)
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
log "Found ${#CANDIDATES[@]} multimedia candidates: ${CANDIDATES[*]:0:5}..."
if [[ ${#CANDIDATES[@]} -gt 0 ]]; then
log "Candidates: ${CANDIDATES[*]}"
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm ${CANDIDATES[*]}"
else
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
fi
fi
# ============================================================
# 3. INACTIVE NVIDIA/MHWHD DRIVERS
# ============================================================
section "[2/7] Inactive NVIDIA Drivers (mhwd-nvidia-*)"
CANDIDATES=()
while IFS= read -r pkg; do
[[ -n "$pkg" ]] && CANDIDATES+=("$pkg")
done < <(pacman -Qq 2>/dev/null | grep -E '^mhwd-nvidia-' || true)
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
if [[ ${#CANDIDATES[@]} -gt 0 ]]; then
log "Found ${#CANDIDATES[@]} mhwd-nvidia candidates: ${CANDIDATES[*]}"
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm ${CANDIDATES[*]}"
else
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
fi
else
log "No mhwd-nvidia packages found."
fi
# ============================================================
# 4. OLD KERNELS
# ============================================================
section "[3/7] Old Kernel Packages"
CANDIDATES=()
# linux618 and linux70 (keep current running kernel and its modules)
CURRENT_KERNEL=$(uname -r)
KMVR="${CURRENT_KERNEL%-MANJARO}" # 6.18.33-1
KMJ="${KMVR%%.*}" # 6
KMREST="${KMVR#*.}" # 18.33-1
KMN="${KMREST%%.*}" # 18
CURRENT_KERNEL_PKG="linux${KMJ}${KMN}" # linux618
log "Current running kernel: $CURRENT_KERNEL (package: $CURRENT_KERNEL_PKG)"
for kernel in linux618 linux70; do
# Skip if this is the running kernel package
if [[ "$kernel" == "linux${CURRENT_KERNEL_PKG}" ]]; then
log "Skipping $kernel — this is the running kernel."
continue
fi
if pacman -Q "$kernel" &>/dev/null; then
CANDIDATES+=("$kernel")
# Also remove its modules/headers/etc (but not nvidia/r8168 for running kernel — already skipped above)
while IFS= read -r pkg; do
[[ -n "$pkg" ]] && CANDIDATES+=("$pkg")
done < <(pacman -Qq 2>/dev/null | grep -E "^${kernel}-" || true)
fi
done
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
if [[ ${#CANDIDATES[@]} -gt 0 ]]; then
log "Found ${#CANDIDATES[@]} kernel candidates: ${CANDIDATES[*]}"
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm ${CANDIDATES[*]}"
else
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
fi
else
log "No old kernel packages found."
fi
# ============================================================
# 5. lib32 PACKAGES
# ============================================================
section "[4/7] lib32 Packages"
CANDIDATES=()
while IFS= read -r pkg; do
[[ -n "$pkg" ]] && CANDIDATES+=("$pkg")
done < <(pacman -Qq 2>/dev/null | grep -E '^lib32-' || true)
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
log "Found ${#CANDIDATES[@]} lib32 packages"
if [[ ${#CANDIDATES[@]} -gt 0 ]]; then
# List first 10, then total
log "First 10 candidates: ${CANDIDATES[*]:0:10}..."
log "Total: ${#CANDIDATES[@]} lib32 packages"
# Interactive: ask user if they want to remove lib32 packages
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm ${CANDIDATES[*]}"
else
log "${BOLD}This will remove ALL ${#CANDIDATES[@]} lib32 packages.${RESET}"
log "Only remove if you don't run 32-bit applications (games, wine, etc.)"
read -rp "Remove all lib32 packages? [y/N] " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
else
log "SKIPPED lib32 removal."
SKIPPED=$((SKIPPED + ${#CANDIDATES[@]}))
fi
fi
fi
# ============================================================
# 6. REDUNDANT FONTS (Nerd Font variants)
# ============================================================
section "[5/7] Redundant Font Packages"
CANDIDATES=()
# Nerd Font packages (keep the one in active use)
while IFS= read -r pkg; do
[[ -n "$pkg" ]] && CANDIDATES+=("$pkg")
done < <(pacman -Qq 2>/dev/null | grep -iE 'nerd-font' || true)
# Also check for duplicate font collections
while IFS= read -r pkg; do
[[ -n "$pkg" ]] && CANDIDATES+=("$pkg")
done < <(pacman -Qq 2>/dev/null | grep -iE 'ttf-(jetbrains|iosevka|fira|hack|noto)' || true)
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
if [[ ${#CANDIDATES[@]} -gt 0 ]]; then
log "Found ${#CANDIDATES[@]} font candidates:"
for pkg in "${CANDIDATES[@]}"; do
log " - $pkg"
done
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would show above packages for manual review"
else
log "${RED}CRITICAL: Review font packages carefully - removing wrong fonts can break UI.${RESET}"
read -rp "Remove these ${#CANDIDATES[@]} font packages? [y/N] " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
else
log "SKIPPED font removal."
SKIPPED=$((SKIPPED + ${#CANDIDATES[@]}))
fi
fi
else
log "No redundant font packages found."
fi
# ============================================================
# 7. BROWSERS (keep current active, remove extras)
# ============================================================
section "[6/7] Browser Packages"
CANDIDATES=()
# Check which browsers are installed
BROWSERS=()
for browser in firefox chromium google-chrome firefox-developer-edition brave-bin microsoft-edge; do
if pacman -Q "$browser" &>/dev/null; then
BROWSERS+=("$browser")
fi
done
log "Installed browsers: ${BROWSERS[*]}"
log "${BOLD}Recommended: Keep firefox-developer-edition + one other${RESET}"
if [[ ${#BROWSERS[@]} -gt 2 ]]; then
# Suggest removal of non-essential browsers
SUGGESTED_REMOVAL=()
for b in "${BROWSERS[@]}"; do
# Keep firefox-developer-edition and chromium/google-chrome
if [[ "$b" != "firefox-developer-edition" && "$b" != "chromium" && "$b" != "google-chrome" ]]; then
SUGGESTED_REMOVAL+=("$b")
fi
done
if [[ ${#SUGGESTED_REMOVAL[@]} -gt 0 ]]; then
CANDIDATES=("${SUGGESTED_REMOVAL[@]}")
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
log "Suggested removal: ${CANDIDATES[*]}"
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm ${CANDIDATES[*]}"
else
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
fi
fi
else
log "Browser count is manageable (${#BROWSERS[@]}). No action needed."
fi
# ============================================================
# 8. UNUSED PYTHON PACKAGES
# ============================================================
section "[7/7] Unused Python GUI Packages"
CANDIDATES=()
PYTHON_CANDIDATES=(
wxpython
wxpython-gtk3
pyqt5
pyqt5-sip
pyqt6
pyqt6-sip
fluidity
tk
tkinter
python-tk
)
for pkg in "${PYTHON_CANDIDATES[@]}"; do
if pacman -Q "$pkg" &>/dev/null; then
CANDIDATES+=("$pkg")
fi
done
TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + ${#CANDIDATES[@]}))
if [[ ${#CANDIDATES[@]} -gt 0 ]]; then
log "Found ${#CANDIDATES[@]} Python GUI candidates: ${CANDIDATES[*]}"
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm ${CANDIDATES[*]}"
else
sudo pacman -Rns --noconfirm "${CANDIDATES[@]}" 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ${#CANDIDATES[@]}))
fi
else
log "No unused Python GUI packages found."
fi
# ============================================================
# CLEANUP ORPHANS & CACHE
# ============================================================
section "Post-Cleanup: Orphans & Cache"
log "Finding orphaned dependencies..."
ORPHANS=$(pacman -Qtdq 2>/dev/null || true)
if [[ -n "$ORPHANS" ]]; then
ORPHAN_COUNT=$(echo "$ORPHANS" | wc -l)
log "Found $ORPHAN_COUNT orphaned packages"
if [[ "$MODE" == "check" ]]; then
log "DRY-RUN: Would run: sudo pacman -Rns --noconfirm $(echo $ORPHANS)"
else
sudo pacman -Rns --noconfirm $ORPHANS 2>&1 | tee -a "$LOCAL_LOG" || true
TOTAL_REMOVED=$((TOTAL_REMOVED + ORPHAN_COUNT))
fi
else
log "No orphaned packages found."
fi
log "Cleaning pacman cache (keep last 2 versions)..."
if [[ "$MODE" == "check" ]]; then
CACHE_SIZE=$(du -sh /var/cache/pacman/pkg/ 2>/dev/null | cut -f1)
log "Cache size: $CACHE_SIZE"
log "DRY-RUN: Would run: sudo paccache -rk2"
else
sudo paccache -rk2 2>&1 | tee -a "$LOCAL_LOG" || true
fi
# ============================================================
# SUMMARY
# ============================================================
section "Cleanup Summary"
FINAL_DISK=$(df -h / | awk 'NR==2 {print $4}')
log "Disk space available after cleanup: $FINAL_DISK"
log "Total candidates identified: $TOTAL_CANDIDATES"
log "Packages removed: $TOTAL_REMOVED"
log "Packages skipped: $SKIPPED"
log "Log saved to: $LOCAL_LOG"
log ""
log "============================================="
log " Cleanup Complete"
log "============================================="
# Cleanup temp files
rm -f "$HOME/logs/.cleanup-candidates-"*.txt 2>/dev/null || true