#!/bin/bash ############################################################################### # Script Name: system-update.sh # # Description: # Performs automated system maintenance and package updates. # # Tasks: # - apt update # - apt upgrade # - apt dist-upgrade # - apt autoclean # - apt autoremove # - flatpak update # - cleanup old log files # - update Docker images # - restart Docker service # - maintain SpotDL Python virtual environment # - install/update spotdl, yt-dlp and youtube-dl # - optional email reporting # # Requirements: # - apt # - flatpak (optional) # - python3-venv # - docker (optional) # - mail (optional, for -mail mode) # # Usage: # # Normal execution: # ./system-update.sh # # Email report mode: # ./system-update.sh -mail # # Logs: # ~/.LOG/log_update_.log # ############################################################################### set -o pipefail usage() { cat <&2 echo usage >&2 exit 2 } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in -h|--help) usage exit 0 ;; --version) echo "${0##*/} 1.0" exit 0 ;; --build) BUILD=true shift 1; ;; --mail) MAIL=true shift 1; ;; -*) die "Unknown option: $1" ;; *) die "Unexpected argument: $1" ;; esac shift done } write_out() { if [[ "${MAIL}" == "true" ]]; then printf "$@\n" >> /tmp/update.txt else printf "$@\n" fi } echo "Start Update" mkdir -p "${home_dir}/.LOG" log_date="$(date +"%m%d%H")" if [[ "$1" == "-mail" ]]; then > /tmp/update.txt exec 3>&1 4>&2 \ 1>"${home_dir}/.LOG/log_update_${log_date}.log" \ 2>&1 MAIL=true green="" red="" norm="" fi write_out "Start: $(date)" ############################################################################### # System Updates ############################################################################### ############################################################################### # Standard updates ############################################################################### CMD=( "apt -y update" "apt -y upgrade" "apt -y dist-upgrade" "apt -y autoclean" "apt -y autoremove" "find ${home_dir}/.LOG/ -name 'log_update_*' -mtime +6 -type f -delete" ) MSG=( "update" "upgrade" "dist-upgrade" "autoclean" "autoremove" "log-cleanup" ) for ((i=0; i<${#CMD[@]}; i++)); do printf "Running: %s\n" "${CMD[$i]}" if eval "sudo ${CMD[$i]}"; then write_out "${MSG[$i]} [${green}OK${norm}]" else write_out "${MSG[$i]} [${red}FAIL${norm}]" fi done ############################################################################### # Flatpak Update ############################################################################### if command -v flatpak >/dev/null 2>&1; then echo "Starting Flatpak Update..." sudo flatpak update -y --noninteractive ret=$? echo "Flatpak returned: ${ret}" if [[ $ret -eq 0 ]]; then write_out "flatpak-update [${green}OK${norm}]" else write_out "flatpak-update [${red}FAIL${norm}]" fi fi echo "Finished Package Update" ############################################################################### # SpotDL Virtual Environment ############################################################################### PACKAGES=( yt-dlp spotdl ) # Install pipx if missing if ! command -v pipx >/dev/null 2>&1; then echo "Installing pipx..." sudo apt update sudo apt install -y pipx pipx ensurepath fi # Check ffmpeg if ! command -v ffmpeg >/dev/null 2>&1; then echo "Installing ffmpeg..." sudo apt install -y ffmpeg fi # Install or update packages for pkg in "${PACKAGES[@]}"; do if pipx list --short 2>/dev/null | grep -qx "$pkg"; then echo "Updating $pkg..." pipx upgrade "$pkg" else echo "Installing $pkg..." pipx install "$pkg" fi done # Install Deno for spotdl if missing if ! command -v deno >/dev/null 2>&1; then echo "Installing Deno..." curl -fsSL https://deno.land/install.sh | sh # Add to PATH for current shell export PATH="$HOME/.deno/bin:$PATH" fi ############################################################################### # Docker Updates ############################################################################### if command -v docker >/dev/null 2>&1; then echo "Updating Docker images" docker images --format "{{.Repository}}:{{.Tag}}" | while read -r image; do echo "####################################################" echo "### $image" echo "####################################################" case "$image" in qbittorrent-qbittorrent:*) echo "Updating custom qBittorrent image..." ( latest="$(curl -s https://api.github.com/repos/qbittorrent/qBittorrent/releases/latest | grep '"tag_name"' | cut -d '"' -f 4 | cut -d '-' -f2)" current="$(docker exec -it qbittorrent /app/qbittorrent-nox -v | cut -d 'v' -f2)" if [[ "$LATEST" != "$CURRENT" && "$BUILD" == "true" ]]; then echo "New version available: $CURRENT -> $LATEST" cd /docker/qbittorrent && docker compose build --pull && docker compose up -d elif [[ "$LATEST" != "$CURRENT" ]]; then echo "New version available: $CURRENT -> $LATEST" echo "BUILD is turend off" else echo "No new version available: $CURRENT == $LATEST" fi ) ;; nextcloud:*) docker pull "$image" docker exec -it nextcloud php occ app:update --all ;; *""*) ;; *) docker pull "$image" ;; esac done systemctl restart docker if [[ $? -eq 0 ]]; then write_out "docker [${green}OK${norm}]" else write_out "docker [${red}FAIL${norm}]" fi fi if [ -f /docker/qbittorrent/docker-compose.yml ]; then cd /docker/qbittorrent || exit 1   if docker compose config >/dev/null 2>&1; then echo "Updating custom qBittorrent image..." docker compose build --pull docker compose up -d fi fi for d in /docker/*; do if [ -d $d ]; then docker compose -f $d/docker-compose.yaml up -d fi done docker container prune -f docker network prune -f docker image prune -a -f docker builder prune -f ############################################################################### # Mail Report ############################################################################### if [[ "${MAIL}" == "true" ]]; then mail_str="$(cat /tmp/update.txt)" echo "$mail_str" | mail \ -s "Automated Server Message - Upgrade" \ lucas-f@live.at fi write_out "Finished: $(date)"