diff --git a/clean.sh b/housekeeping.sh similarity index 61% rename from clean.sh rename to housekeeping.sh index 481aba1..4fbfcfb 100644 --- a/clean.sh +++ b/housekeeping.sh @@ -1,6 +1,8 @@ #!/bin/bash -# System Cleanup and Maintenance Script +# Housekeeping Script +# Unified system maintenance: package index update, system cleanup, and oh-my-zsh update. +# Combines the former update.sh and clean.sh into a single script with subcommand dispatch. set -euo pipefail @@ -24,6 +26,8 @@ readonly CACHE_RETENTION_DAYS=7 readonly TEMP_DIRS=("/tmp" "/var/tmp") readonly CACHE_DIRS=("/var/cache/apt" "/root/.cache") +readonly SCRIPT_NAME="$(basename "$0")" + #============================================================================== # UTILITY FUNCTIONS #============================================================================== @@ -61,31 +65,31 @@ get_dir_size() { clean_directory() { local dir="$1" local description="$2" - + if [[ ! -d "$dir" ]]; then return 0 fi - + local size_before size_before=$(get_dir_size "$dir") - + if [[ "$size_before" == "0B" ]]; then log_info "$description: already clean" return 0 fi - + log_step "$description (was $size_before)..." - + # Use find to delete files older than retention period # -type f -mtime +N: only files older than N days (not directories) local deleted=false if find "$dir" -type f -mtime +"${CACHE_RETENTION_DAYS:-7}" -delete 2>/dev/null; then deleted=true fi - + # Clean up empty directories left behind (but don't remove the dir itself) find "$dir" -mindepth 1 -type d -empty -delete 2>/dev/null || true - + if $deleted; then local size_after size_after=$(get_dir_size "$dir") @@ -102,7 +106,7 @@ clean_directory() { # Get system information for reporting get_system_info() { local info="" - + # Memory info if [[ -f /proc/meminfo ]]; then local mem_total mem_available @@ -112,7 +116,7 @@ get_system_info() { info+="Memory: $((mem_available/1024))MB available of $((mem_total/1024))MB total" fi fi - + # Disk space info if command_exists df; then local disk_info @@ -121,10 +125,163 @@ get_system_info() { info+="${info:+, }Disk: $disk_info" fi fi - + echo "$info" } +#============================================================================== +# OS DETECTION (for package index update) +#============================================================================== + +# Detect the running OS and its ID string. +# Prints two values: the distro id (lowercase) and the family id (lowercase). +detect_os() { + local id="" + local family="" + + if [[ -f /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + id="${ID:-unknown}" + family="${ID_LIKE:-}" + elif [[ -f /etc/alpine-release ]]; then + id="alpine" + family="alpine" + else + # Fall back to package manager detection + if command_exists apk; then + id="alpine"; family="alpine" + elif command_exists pacman; then + id="arch"; family="arch" + elif command_exists apt-get; then + id="debian"; family="debian" + elif command_exists dnf; then + id="fedora"; family="rhel" + elif command_exists yum; then + id="centos"; family="rhel" + elif command_exists zypper; then + id="opensuse"; family="suse" + elif command_exists xbps-install; then + id="void"; family="void" + elif command_exists emerge; then + id="gentoo"; family="gentoo" + else + id="unknown"; family="unknown" + fi + fi + + echo "${id} ${family}" +} + +#============================================================================== +# OS-SPECIFIC PACKAGE INDEX UPDATE FUNCTIONS +#============================================================================== + +update_alpine() { + if ! command_exists apk; then + log_warning "apk not found on Alpine-type system" + return 0 + fi + + log_info "Running apk update..." + if apk update 2>&1; then + log_success "Alpine package index updated" + else + log_warning "apk update failed" + fi +} + +update_arch() { + if ! command_exists pacman; then + log_warning "pacman not found on Arch-type system" + return 0 + fi + + log_info "Refreshing pacman database (pacman -Sy)..." + # -S = sync, -y = refresh local database only (no package installation) + if pacman -Sy --noconfirm 2>&1; then + log_success "Arch package database refreshed" + else + log_warning "pacman -Sy failed" + fi +} + +update_debian() { + if ! command_exists apt-get; then + log_warning "apt-get not found on Debian-type system" + return 0 + fi + + log_info "Running apt-get update..." + if apt-get update 2>&1; then + log_success "Debian/Ubuntu package index updated" + else + log_warning "apt-get update failed" + fi +} + +update_rhel() { + if command_exists dnf; then + log_info "Running dnf makecache..." + if dnf makecache 2>&1; then + log_success "RHEL/Fedora metadata cache refreshed (dnf)" + else + log_warning "dnf makecache failed" + fi + elif command_exists yum; then + log_info "Running yum makecache..." + if yum makecache fast 2>&1; then + log_success "CentOS/RHEL metadata cache refreshed (yum)" + else + log_warning "yum makecache failed" + fi + else + log_warning "No DNF or YUM found on RHEL-type system" + fi +} + +update_suse() { + if ! command_exists zypper; then + log_warning "zypper not found on openSUSE-type system" + return 0 + fi + + log_info "Running zypper refresh..." + if zypper --non-interactive refresh 2>&1; then + log_success "openSUSE repositories refreshed" + else + log_warning "zypper refresh failed" + fi +} + +update_void() { + if ! command_exists xbps-install; then + log_warning "xbps-install not found on Void system" + return 0 + fi + + log_info "Running xbps-install -S (sync repository index)..." + if xbps-install -S 2>&1; then + log_success "Void package index synced" + else + log_warning "xbps-install -S failed" + fi +} + +update_gentoo() { + if ! command_exists emerge; then + log_warning "emerge not found on Gentoo system" + return 0 + fi + + log_info "Running emaint sync -a..." + if emaint sync -a 2>&1; then + log_success "Gentoo Portage tree synced" + else + log_warning "emaint sync failed" + fi +} + #============================================================================== # DOCKER CLEANUP FUNCTIONS #============================================================================== @@ -135,15 +292,15 @@ cleanup_docker() { log_info "Docker not found, skipping Docker cleanup" return 0 fi - + log_step "Starting Docker cleanup..." - + # Check if Docker daemon is running if ! docker info >/dev/null 2>&1; then log_warning "Docker daemon not running, skipping Docker cleanup" return 0 fi - + # Remove unused images log_info "Removing unused Docker images..." if docker image prune -af >/dev/null 2>&1; then @@ -151,7 +308,7 @@ cleanup_docker() { else log_warning "Docker image cleanup failed" fi - + # Remove stopped containers log_info "Removing stopped Docker containers..." if docker container prune -f >/dev/null 2>&1; then @@ -159,7 +316,7 @@ cleanup_docker() { else log_warning "Docker container cleanup failed" fi - + # Remove unused volumes log_info "Removing unused Docker volumes..." if docker volume prune -f >/dev/null 2>&1; then @@ -167,7 +324,7 @@ cleanup_docker() { else log_warning "Docker volume cleanup failed" fi - + # Remove unused networks log_info "Removing unused Docker networks..." if docker network prune -f >/dev/null 2>&1; then @@ -175,7 +332,7 @@ cleanup_docker() { else log_warning "Docker network cleanup failed" fi - + # Complete system cleanup log_info "Running Docker system cleanup..." if docker system prune -af >/dev/null 2>&1; then @@ -194,19 +351,19 @@ cleanup_apk() { if ! command_exists apk; then return 0 fi - + log_step "Cleaning APK cache..." - + # Clean APK cache if [[ -d /var/cache/apk ]]; then clean_directory "/var/cache/apk" "APK cache directory" fi - + # Clean APK cache using apk command if apk cache clean >/dev/null 2>&1; then log_success "APK cache cleaned" fi - + # Update package index log_info "Updating APK package index..." if apk update >/dev/null 2>&1; then @@ -221,30 +378,30 @@ cleanup_apt() { if ! command_exists apt-get; then return 0 fi - + log_step "Cleaning APT cache..." - + # Clean downloaded packages if apt-get clean >/dev/null 2>&1; then log_success "APT cache cleaned" else log_warning "APT clean failed" fi - + # Remove orphaned packages if apt-get autoclean >/dev/null 2>&1; then log_success "APT autocleaned" else log_warning "APT autoclean failed" fi - + # Remove unnecessary packages if apt-get autoremove -y >/dev/null 2>&1; then log_success "Unnecessary packages removed" else log_warning "APT autoremove failed" fi - + # Update package index log_info "Updating APT package index..." if apt-get update >/dev/null 2>&1; then @@ -261,7 +418,7 @@ cleanup_apt() { # Clean system temporary directories cleanup_temp_dirs() { log_step "Cleaning temporary directories..." - + for temp_dir in "${TEMP_DIRS[@]}"; do if [[ -d "$temp_dir" ]]; then # Clean contents but preserve the directory @@ -274,13 +431,13 @@ cleanup_temp_dirs() { # Clean system cache directories cleanup_cache_dirs() { log_step "Cleaning cache directories..." - + for cache_dir in "${CACHE_DIRS[@]}"; do if [[ -d "$cache_dir" ]]; then clean_directory "$cache_dir" "Cache directory $cache_dir" fi done - + # Clean safe cache locations only # NOTE: We do NOT recursively delete /var/cache or /root/.cache as a whole, # as that can break package managers and remove important data. @@ -297,7 +454,7 @@ cleanup_cache_dirs() { "/home/*/.npm" "/home/*/.pip" ) - + for cache_pattern in "${additional_caches[@]}"; do # Use shell expansion for patterns for cache_path in $cache_pattern; do @@ -311,32 +468,32 @@ cleanup_cache_dirs() { # Clean old log files cleanup_logs() { log_step "Cleaning old log files..." - + # Clean logs older than retention period if [[ -d /var/log ]]; then local cleaned_count=0 - + # Find and remove old log files while IFS= read -r -d '' logfile; do if rm -f "$logfile" 2>/dev/null; then ((++cleaned_count)) || true fi done < <(find /var/log -type f -name "*.log" -mtime +"$LOG_RETENTION_DAYS" -print0 2>/dev/null || true) - + # Clean compressed logs while IFS= read -r -d '' logfile; do if rm -f "$logfile" 2>/dev/null; then ((++cleaned_count)) || true fi done < <(find /var/log -type f \( -name "*.log.gz" -o -name "*.log.bz2" -o -name "*.log.xz" \) -mtime +"$LOG_RETENTION_DAYS" -print0 2>/dev/null || true) - + if [[ $cleaned_count -gt 0 ]]; then log_success "Removed $cleaned_count old log files" else log_info "No old log files to remove" fi fi - + # Truncate large active log files local large_logs while IFS= read -r -d '' logfile; do @@ -351,16 +508,16 @@ cleanup_journal() { if ! command_exists journalctl; then return 0 fi - + log_step "Cleaning systemd journal..." - + # Clean journal older than retention period if journalctl --vacuum-time="${JOURNAL_RETENTION_DAYS}d" >/dev/null 2>&1; then log_success "Journal cleaned (older than $JOURNAL_RETENTION_DAYS days)" else log_warning "Journal cleanup failed" fi - + # Limit journal size if journalctl --vacuum-size=100M >/dev/null 2>&1; then log_success "Journal size limited to 100MB" @@ -370,14 +527,14 @@ cleanup_journal() { # Clean thumbnail caches cleanup_thumbnails() { log_step "Cleaning thumbnail caches..." - + local thumbnail_dirs=( "/root/.thumbnails" "/root/.cache/thumbnails" "/home/*/.thumbnails" "/home/*/.cache/thumbnails" ) - + for thumb_pattern in "${thumbnail_dirs[@]}"; do for thumb_dir in $thumb_pattern; do if [[ -d "$thumb_dir" ]]; then @@ -390,12 +547,12 @@ cleanup_thumbnails() { # Optimize memory caches optimize_memory() { log_step "Optimizing memory caches..." - + # Sync filesystem if sync; then log_info "Filesystem synced" fi - + # Drop caches (page cache, dentries and inodes) if [[ -w /proc/sys/vm/drop_caches ]]; then echo 3 > /proc/sys/vm/drop_caches 2>/dev/null && log_success "Memory caches dropped" || log_warning "Failed to drop memory caches" @@ -409,14 +566,14 @@ optimize_memory() { # Generate cleanup summary generate_summary() { log_step "Generating cleanup summary..." - + local system_info system_info=$(get_system_info) - + if [[ -n "$system_info" ]]; then log_info "System status: $system_info" fi - + # Show disk usage of important directories local important_dirs=("/" "/var" "/tmp" "/var/log" "/var/cache") for dir in "${important_dirs[@]}"; do @@ -429,13 +586,90 @@ generate_summary() { } #============================================================================== -# MAIN EXECUTION +# SUBCOMMAND HANDLERS #============================================================================== -main() { +# update: refresh the OS package index (no upgrades) +run_update() { + log_step "Starting package list update (${SCRIPT_NAME})" + echo + + # Detect the OS + local distro_info distro_id family_id + distro_info="$(detect_os)" + distro_id="${distro_info%% *}" + family_id="${distro_info##* }" + + log_info "Detected OS: ${distro_id}" + if [[ -n "${family_id}" && "${family_id}" != "${distro_id}" ]]; then + log_info "OS family: ${family_id}" + fi + echo + + # Dispatch to the correct handler based on distro family + # Check distro_id first, then fall back to family_id for aliases + case "${distro_id}" in + alpine) + update_alpine + ;; + arch|artix|manjaro|garuda) + update_arch + ;; + debian|ubuntu|linuxmint|kali|raspbian|pop) + update_debian + ;; + fedora|rhel|centos|rocky|almalinux|amazon|oracle|clear-linux-os) + update_rhel + ;; + opensuse*|suse|sles) + update_suse + ;; + void) + update_void + ;; + gentoo|funtoo) + update_gentoo + ;; + *) + # Try matching on family ID as fallback + case "${family_id}" in + *alpine*) + update_alpine + ;; + *arch*) + update_arch + ;; + *debian*|*ubuntu*) + update_debian + ;; + *rhel*|*fedora*|*centos*) + update_rhel + ;; + *suse*|*opensuse*) + update_suse + ;; + *void*) + update_void + ;; + *gentoo*) + update_gentoo + ;; + *) + die "Unsupported OS: ${distro_id} (${family_id}). Cannot update package index." + ;; + esac + ;; + esac + + echo + log_success "Package list update completed!" +} + +# clean: full system cleanup and maintenance +run_clean() { log_step "Starting System Cleanup and Maintenance" echo - + # Show initial system status local initial_info initial_info=$(get_system_info) @@ -443,31 +677,106 @@ main() { log_info "Initial system status: $initial_info" echo fi - + # Docker cleanup cleanup_docker - + # Package manager cleanup cleanup_apk cleanup_apt - + # System cleanup cleanup_temp_dirs cleanup_cache_dirs cleanup_logs cleanup_journal cleanup_thumbnails - + # Memory optimization optimize_memory - + # Generate summary echo generate_summary - + echo log_success "System cleanup and maintenance completed!" } -# Execute main function with all arguments -main "$@" \ No newline at end of file +# omz: update oh-my-zsh (safe fallback if not installed) +run_omz() { + log_step "Starting oh-my-zsh update (${SCRIPT_NAME})" + echo + + if ! command_exists omz; then + log_warning "oh-my-zsh (omz) is not installed on this system, skipping omz update" + return 0 + fi + + log_info "Running omz update..." + if omz update 2>&1; then + log_success "oh-my-zsh updated" + else + log_warning "omz update failed" + fi +} + +# all: run update, clean, then omz in order +run_all() { + run_update + echo + run_clean + echo + run_omz +} + +# Print usage information +print_usage() { + cat <&2; } -log_step() { echo -e "${LIGHT_BLUE}[i] $1${NC}"; } - -die() { - log_error "$1" - exit 1 -} - -command_exists() { - command -v "$1" >/dev/null 2>&1 -} - -# Detect the running OS and its ID string. -# Prints two values: the distro id (lowercase) and the family id (lowercase). -detect_os() { - local id="" - local family="" - - if [[ -f /etc/os-release ]]; then - # shellcheck disable=SC1091 - . /etc/os-release - id="${ID:-unknown}" - family="${ID_LIKE:-}" - elif [[ -f /etc/alpine-release ]]; then - id="alpine" - family="alpine" - else - # Fall back to package manager detection - if command_exists apk; then - id="alpine"; family="alpine" - elif command_exists pacman; then - id="arch"; family="arch" - elif command_exists apt-get; then - id="debian"; family="debian" - elif command_exists dnf; then - id="fedora"; family="rhel" - elif command_exists yum; then - id="centos"; family="rhel" - elif command_exists zypper; then - id="opensuse"; family="suse" - elif command_exists xbps-install; then - id="void"; family="void" - elif command_exists emerge; then - id="gentoo"; family="gentoo" - else - id="unknown"; family="unknown" - fi - fi - - echo "${id} ${family}" -} - -#============================================================================== -# OS-SPECIFIC UPDATE FUNCTIONS -#============================================================================== - -update_alpine() { - if ! command_exists apk; then - log_warning "apk not found on Alpine-type system" - return 0 - fi - - log_info "Running apk update..." - if apk update 2>&1; then - log_success "Alpine package index updated" - else - log_warning "apk update failed" - fi -} - -update_arch() { - if ! command_exists pacman; then - log_warning "pacman not found on Arch-type system" - return 0 - fi - - log_info "Refreshing pacman database (pacman -Sy)..." - # -S = sync, -y = refresh local database only (no package installation) - if pacman -Sy --noconfirm 2>&1; then - log_success "Arch package database refreshed" - else - log_warning "pacman -Sy failed" - fi -} - -update_debian() { - if ! command_exists apt-get; then - log_warning "apt-get not found on Debian-type system" - return 0 - fi - - log_info "Running apt-get update..." - if apt-get update 2>&1; then - log_success "Debian/Ubuntu package index updated" - else - log_warning "apt-get update failed" - fi -} - -update_rhel() { - if command_exists dnf; then - log_info "Running dnf makecache..." - if dnf makecache 2>&1; then - log_success "RHEL/Fedora metadata cache refreshed (dnf)" - else - log_warning "dnf makecache failed" - fi - elif command_exists yum; then - log_info "Running yum makecache..." - if yum makecache fast 2>&1; then - log_success "CentOS/RHEL metadata cache refreshed (yum)" - else - log_warning "yum makecache failed" - fi - else - log_warning "No DNF or YUM found on RHEL-type system" - fi -} - -update_suse() { - if ! command_exists zypper; then - log_warning "zypper not found on openSUSE-type system" - return 0 - fi - - log_info "Running zypper refresh..." - if zypper --non-interactive refresh 2>&1; then - log_success "openSUSE repositories refreshed" - else - log_warning "zypper refresh failed" - fi -} - -update_void() { - if ! command_exists xbps-install; then - log_warning "xbps-install not found on Void system" - return 0 - fi - - log_info "Running xbps-install -S (sync repository index)..." - if xbps-install -S 2>&1; then - log_success "Void package index synced" - else - log_warning "xbps-install -S failed" - fi -} - -update_gentoo() { - if ! command_exists emerge; then - log_warning "emerge not found on Gentoo system" - return 0 - fi - - log_info "Running emaint sync -a..." - if emaint sync -a 2>&1; then - log_success "Gentoo Portage tree synced" - else - log_warning "emaint sync failed" - fi -} - -#============================================================================== -# MAIN EXECUTION -#============================================================================== - -main() { - log_step "Starting package list update (${SCRIPT_NAME})" - echo - - # Detect the OS - local distro_info distro_id family_id - distro_info="$(detect_os)" - distro_id="${distro_info%% *}" - family_id="${distro_info##* }" - - log_info "Detected OS: ${distro_id}" - if [[ -n "${family_id}" && "${family_id}" != "${distro_id}" ]]; then - log_info "OS family: ${family_id}" - fi - echo - - # Dispatch to the correct handler based on distro family - # Check distro_id first, then fall back to family_id for aliases - case "${distro_id}" in - alpine) - update_alpine - ;; - arch|artix|manjaro|garuda) - update_arch - ;; - debian|ubuntu|linuxmint|kali|raspbian|pop) - update_debian - ;; - fedora|rhel|centos|rocky|almalinux|amazon|oracle|clear-linux-os) - update_rhel - ;; - opensuse*|suse|sles) - update_suse - ;; - void) - update_void - ;; - gentoo|funtoo) - update_gentoo - ;; - *) - # Try matching on family ID as fallback - case "${family_id}" in - *alpine*) - update_alpine - ;; - *arch*) - update_arch - ;; - *debian*|*ubuntu*) - update_debian - ;; - *rhel*|*fedora*|*centos*) - update_rhel - ;; - *suse*|*opensuse*) - update_suse - ;; - *void*) - update_void - ;; - *gentoo*) - update_gentoo - ;; - *) - die "Unsupported OS: ${distro_id} (${family_id}). Cannot update package index." - ;; - esac - ;; - esac - - echo - log_success "Package list update completed!" -} - -main "$@"