main
59c7d64 · 8 days ago 139 commits
  1#!/usr/bin/env zsh
  2# ============================================================================
  3# Arch Linux Maintenance TUI
  4# ============================================================================
  5
  6center_box() {
  7  local content="$1"
  8  local term_width box_width pad
  9
 10  term_width=$(tput cols)
 11  
 12  # Get the width of the content (rough estimate)
 13  box_width=$(echo "$content" | wc -L)
 14  
 15  pad=$(( (term_width - box_width) / 2 ))
 16  (( pad < 0 )) && pad=0
 17
 18  # Add left padding to each line of the box
 19  echo "$content" | while IFS= read -r line; do
 20    printf "%*s%s\n" "$pad" "" "$line"
 21  done
 22}
 23
 24cleanup() {
 25  echo ""
 26  tput cnorm 2>/dev/null || true
 27  show_header
 28  gum style --foreground "$MOCHA_RED" --bold "Operation cancelled by user."
 29  sub_text "Press any key to exit..."
 30  read -n 1
 31  exit 130
 32}
 33
 34gum_safe() {
 35  local resultCode
 36  gum "$@"
 37  resultCode=$?
 38
 39  if (( resultCode == 130 )); then
 40    cleanup
 41  elif (( resultCode != 0 )); then
 42    return $resultCode
 43  fi
 44}
 45
 46
 47# -------------------------------------
 48
 49export MOCHA_MAUVE="#cba6f7"
 50export MOCHA_LAVENDER="#b4befe"
 51export MOCHA_GREEN="#a6e3a1"
 52export MOCHA_PEACH="#f5c2e7"
 53export MOCHA_RED="#f38ba8"
 54export MOCHA_TEXT="#cdd6f4"
 55export MOCHA_OVERLAY="#6c7086"
 56export MOCHA_BASE="#1e1e2e"
 57
 58# -- Gum Defaults --
 59export GUM_SPIN_SPINNER="dot"
 60export GUM_SPIN_SPINNER_FOREGROUND="$MOCHA_LAVENDER"
 61export GUM_SPIN_TITLE_FOREGROUND="$MOCHA_TEXT"
 62export GUM_CONFIRM_PROMPT_FOREGROUND="$MOCHA_MAUVE"
 63export GUM_CONFIRM_SELECTED_BACKGROUND="$MOCHA_MAUVE"
 64export GUM_CONFIRM_SELECTED_FOREGROUND="$MOCHA_BASE"
 65export GUM_CONFIRM_UNSELECTED_FOREGROUND="$MOCHA_OVERLAY"
 66export GUM_STYLE_BORDER="double"
 67export GUM_STYLE_BORDER_FOREGROUND="$MOCHA_MAUVE"
 68export GUM_STYLE_FOREGROUND="$MOCHA_TEXT"
 69
 70# -------------------------------------
 71# Dependency Checks
 72# -------------------------------------
 73
 74LOG_DIR="$HOME/.local/var/log"
 75mkdir -p "$LOG_DIR"
 76LOG_FILE="$LOG_DIR/maintenance-$(date +%F_%H-%M-%S).log"
 77
 78AUR_HELPER="yay"
 79
 80if ! command -v gum &>/dev/null || ! command -v yay &>/dev/null || ! command -v paccache &>/dev/null; then
 81  command -v gum &>/dev/null || { echo "❌ Error: 'gum' is not installed. Please install it to proceed."; exit 1; }
 82  command -v yay &>/dev/null || { gum style --foreground "$MOCHA_RED" "❌ Error: No AUR helper (paru/yay) found."; exit 1; }
 83  command -v paccache &>/dev/null || { gum style --foreground "$MOCHA_RED" "❌ Error: 'pacman-contrib' is missing." "Install it with: sudo pacman -S pacman-contrib"; exit 1; }
 84fi
 85
 86show_header() {
 87  clear
 88  header=$(gum_safe style \
 89    --align "center" \
 90    --border "double" \
 91    --margin "1" \
 92    --padding "1 2" \
 93    --border-foreground "$MOCHA_MAUVE" \
 94    "☕ Arch Linux Spring-Clean"
 95  )
 96  center_box "$header"
 97}
 98
 99run_task() {
100  local label="$1"
101  local cmd="$2"
102  
103  if gum_safe spin --title "$label" -- sh -c "$cmd >> $LOG_FILE 2>&1"; then
104    gum_safe style --foreground "$MOCHA_GREEN" --border none "$label completed"
105  else
106    gum_safe style --foreground "$MOCHA_RED" --border none "$label failed! Check logs."
107  fi
108}
109
110section_title() {
111  echo ""
112  gum_safe style --foreground "$MOCHA_MAUVE" --bold --border none "$1"
113}
114
115sub_text() {
116  gum_safe style --foreground "$MOCHA_OVERLAY" --border none "$1"
117}
118
119# -------------------------------------
120#  Execution
121# -------------------------------------
122
123show_header
124
125# get sudo upfront
126if ! sudo -v; then
127  gum_safe style --foreground "$MOCHA_RED" "❌ Error: Sudo authentication failed."
128  exit 1
129fi
130
131# --- Step 1 ---
132section_title "1. System Upgrade"
133if gum_safe confirm "Upgrade system packages?"; then
134  $AUR_HELPER -Syu
135else
136  sub_text "Skipped upgrade."
137fi
138
139# --- Step 2 ---
140section_title "2. Pacman Cache"
141current_pkg_cache=$(du -sh /var/cache/pacman/pkg 2>/dev/null | cut -f1)
142
143if gum_safe confirm "Clean pacman cache? (Current: $current_pkg_cache)"; then
144  run_task "Pruning package cache" "sudo paccache -vrk 2 && sudo paccache -ruk0"
145else
146  sub_text "Skipped package cache."
147fi
148
149# --- Step 3 ---
150section_title "3. Orphaned Packages"
151ORPHANS=$($AUR_HELPER -Qtdq)
152
153if [[ -z "$ORPHANS" ]]; then
154  gum_safe style --foreground "$MOCHA_GREEN" --border none "✓ No orphaned packages found."
155else
156  gum_safe style --foreground "$MOCHA_PEACH" --border none "Found orphaned packages:"
157  echo "$ORPHANS" | gum_safe format
158
159  if gum_safe confirm "Remove orphaned packages?"; then
160    ORPHAN_LIST=$(echo "$ORPHANS" | tr '\n' ' ')
161    run_task "Removing orphaned packages" "sudo pacman -Rns --noconfirm $ORPHAN_LIST" 
162  else
163    sub_text "Skipped orphan removal."
164  fi
165fi
166
167# --- Step 4 ---
168section_title "4. User Cache"
169sub_text "Targets: thumbnails, yay, paru, pip, npm"
170
171LARGE_CACHES=(${(f)"$(du -d 1 -h ~/.cache 2>/dev/null | grep -E '^([5-9][0-9][0-9]M|[0-9]+(\.[0-9]+)?G)' | grep -vE "\.cache$")"})
172
173if [[ ${#LARGE_CACHES[@]} -eq 0 ]]; then
174    sub_text "Everything is clean! No folders > 500MB."
175else
176    SELECTED=$(printf "%s\n" "${LARGE_CACHES[@]}" | gum choose --no-limit --header "Select caches to wash")
177
178    if [[ -z "$SELECTED" ]]; then
179        sub_text "Skipped user cache."
180    else
181        echo "$SELECTED" | while read -r line; do
182            TARGET_PATH=$(echo "$line" | awk '{print $2}')
183            if [[ -d "$TARGET_PATH" ]]; then
184                run_task "Cleaning ${TARGET_PATH}" "rm -rf ${TARGET_PATH:?}/*"
185            fi
186        done
187    fi
188fi
189
190# --- Step 5 ---
191section_title "5. System Logs"
192current_journal=$(journalctl --disk-usage | awk '{print $NF}')
193
194if gum_safe confirm "Vacuum logs > 7d? (Current: $current_journal)"; then
195  run_task "Rotating logs" "sudo journalctl --rotate"
196  run_task "Vacuuming logs" "sudo journalctl --vacuum-time=7d"
197else
198  sub_text "Skipped logs."
199fi
200
201# --- Step 6 ---
202section_title "6. Service Health"
203failed_count=$(systemctl list-units --state=failed --no-legend | wc -l)
204
205if (( failed_count == 0 )); then
206  gum_safe style --foreground "$MOCHA_GREEN" --border none "✓ All systems operational."
207else
208  gum_safe style --foreground "$MOCHA_RED" --border none "⚠️  $failed_count services failed:"
209  systemctl --failed --no-pager --plain | gum_safe format
210fi
211
212echo ""
213gum_safe style --padding "1" "✨ Maintenance Complete."
214gum_safe style --foreground "$MOCHA_GREEN" --bold "Review the log at: $LOG_FILE"
215gum spin --spinner "moon" --title "Press any key to close..." -- bash -c 'read -n 1 -s'