#!/bin/bash # blob:summary=Update system and AUR packages # blob:args=[--no-confirm] # blob:requires-sudo=true set -eE confirm=true [[ ${1-} == --no-confirm ]] && confirm=false accent_color=4 muted_color=8 success_color=2 failure_color=1 total_steps=4 current_step=0 current_label="Checking for updates" started_at=$SECONDS repo_updates="" aur_updates="" counts_known=false aur_reachable=false reboot_needed=false orphan_count=0 has_gum() { command -v gum &>/dev/null } styled() { local color="$1" shift if has_gum; then gum style --foreground "$color" "$@" else local argument for argument in "$@"; do [[ $argument == --* ]] || printf '%s\n' "$argument" done fi } line_count() { [[ -n $1 ]] || { echo 0; return; } grep -c . <<<"$1" } show_header() { if has_gum; then gum style --border rounded --border-foreground "$accent_color" --padding "0 2" --margin "1 0" \ --bold "Blob Update" "$(date '+%A %-d %B, %H:%M')" else printf '\nBlob Update - %s\n\n' "$(date '+%A %-d %B, %H:%M')" fi } begin_step() { current_step=$((current_step + 1)) current_label="$1" echo styled "$accent_color" --bold "[$current_step/$total_steps] $1" } skip_step() { begin_step "$1" styled "$muted_color" " $2" } report_failure() { trap - ERR echo styled "$failure_color" --bold "Update failed while: $current_label" blob-notify-send -u critical "Update failed" "Stopped while: $current_label" || true exit 1 } trap report_failure ERR print_update_list() { local title="$1" updates="$2" styled "$accent_color" --bold "$title ($(line_count "$updates"))" if [[ -z $updates ]]; then styled "$muted_color" " none" return fi awk '{ printf " %-34s %s -> %s\n", $1, $2, $4 }' <<<"$updates" } gather_pending() { if has_gum; then gum spin --spinner dot --title "Checking the AUR..." -- blob-pkg-aur && aur_reachable=true || true else echo "Checking the AUR..." blob-pkg-aur && aur_reachable=true || true fi blob-cmd-present checkupdates || return 0 counts_known=true local check_status=0 if has_gum; then repo_updates=$(gum spin --spinner dot --title "Checking for system updates..." --show-output -- checkupdates 2>/dev/null) || check_status=$? else echo "Checking for system updates..." repo_updates=$(checkupdates 2>/dev/null) || check_status=$? fi if (( check_status == 1 )); then counts_known=false repo_updates="" styled "$muted_color" "Could not check for system updates; updating anyway." return 0 fi if blob-cmd-present yay && [[ $aur_reachable == true ]]; then aur_updates=$(yay -Qua 2>/dev/null || true) fi if grep -qE '^linux(-lts|-zen|-hardened)? ' <<<"$repo_updates"; then reboot_needed=true fi } show_pending() { [[ $counts_known == true ]] || return 0 echo print_update_list "System packages" "$repo_updates" echo if blob-cmd-present yay && [[ $aur_reachable == true ]]; then print_update_list "AUR packages" "$aur_updates" else styled "$muted_color" "AUR packages: not checked" fi } confirm_update() { [[ $confirm == true ]] || return 0 echo local question="Update system and AUR packages?" if [[ $counts_known == true ]]; then question="Install $(( $(line_count "$repo_updates") + $(line_count "$aur_updates") )) update(s)?" fi if has_gum; then gum confirm "$question" || exit 0 else local answer read -rp "$question [y/N] " answer [[ $answer == [yY] ]] || exit 0 fi } format_duration() { local seconds=$1 if (( seconds >= 60 )); then printf '%dm %ds' $((seconds / 60)) $((seconds % 60)) else printf '%ds' "$seconds" fi } show_summary() { local elapsed elapsed=$(format_duration $((SECONDS - started_at))) echo if has_gum; then local summary_lines=() if [[ $counts_known == true ]]; then summary_lines+=("System packages $(line_count "$repo_updates")") summary_lines+=("AUR packages $(line_count "$aur_updates")") fi summary_lines+=("Orphans removed $orphan_count") summary_lines+=("Time $elapsed") gum style --border rounded --border-foreground "$success_color" --padding "0 2" \ "$(gum style --foreground "$success_color" --bold "Update complete")" "" "${summary_lines[@]}" else echo "Update complete in $elapsed." [[ $counts_known == true ]] && echo "System: $(line_count "$repo_updates"), AUR: $(line_count "$aur_updates"), orphans removed: $orphan_count" fi if [[ $reboot_needed == true ]]; then echo styled "$accent_color" --bold "A new kernel was installed. Reboot to start using it." fi } show_header gather_pending if [[ $counts_known == true && -z $repo_updates && -z $aur_updates ]]; then echo styled "$success_color" --bold "Everything is up to date." if blob-cmd-present yay && [[ $aur_reachable != true ]]; then styled "$muted_color" "The AUR was not reachable, so AUR packages were not checked." fi exit 0 fi show_pending confirm_update blob-sudo-keepalive begin_step "Refreshing the keyring" sudo pacman -Sy --needed --noconfirm archlinux-keyring begin_step "Updating system packages" sudo pacman -Syu --noconfirm if ! blob-cmd-present yay; then skip_step "Updating AUR packages" "Skipped: yay is not installed" elif [[ $aur_reachable != true ]]; then skip_step "Updating AUR packages" "Skipped: the AUR is not reachable" else begin_step "Updating AUR packages" yay -Sua --noconfirm fi begin_step "Removing orphaned packages" orphans=$(pacman -Qtdq 2>/dev/null || true) if [[ -n $orphans ]]; then orphan_count=$(line_count "$orphans") printf '%s\n' "$orphans" | sudo pacman -Rns --noconfirm - else styled "$muted_color" " None to remove" fi show_summary notify_body="System and AUR packages are up to date" [[ $reboot_needed == true ]] && notify_body="$notify_body. Reboot to use the new kernel." blob-notify-send "Update complete" "$notify_body"