You are not logged in.

#1 2024-10-26 10:37:35

coxe87b
Member
From: Canberra
Registered: 2019-12-08
Posts: 86

pacup - PACman automated UPdate script (work in progress)

The purpose of this script is to streamline the process of creating a system (not user data) backup then performing pacman update.
Features;
- Providing automatic shutdown option upon completion
- Keeping a human-readable log including application version changes (shows old version -> new version which is a bit extra from the /var/log/pacman.log)
- Automatic removal of orphaned packages
- Automatic cleaning of pacman package cache

This is a work in progress and I am open to suggestions on features or how to improve my code (as I know the code is not exactly optimised).
I am aware that there are shorter ways to perform logic funtions such as [ test ] && do_this_if_true || do_this_if_false for example, but I have done certain functions in long-hand to improve readability.

Anyway, here is the script;

#!/usr/bin/env bash

#
# Possibly integrate curl -o /etc/pacman.d/mirrorlist https://archlinux.org/mirrorlist/all/
# for auto updating mirrorlists
# https://archlinux.org/mirrorlist/?country=AU&protocol=https&ip_version=4
#
# Detect Linux kernel update and suggest reboot
#
# pacup - System update script by Ewan Cox.
# Version:
pacup_ver="* pacup version 1.10 *"
# 
# This script will help to automate updating in your Arch based system. It supports automatic shutdown on completion, logging of package changes, error logging,
# automatic timeshift backups and support for AUR updating (using -a flag)
#
# Copy this script to your /usr/local/bin directory and use script with "sudo pacup" from your terminal CLI
#


# User definable:
logfile=/home/$SUDO_USER/logs/pacup.log
timeshiftgit="https://aur.archlinux.org/timeshift-bin.git"

# Declare variables:
aurpkgnum=$(pacman -Qm | wc -l)
orpkgnum=$(pacman -Qtdq | wc -w)
helper="unknown"
pkglist=""
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
nc='\033[0m' # No colour
unknownflag=0
helpflag=0
shutcomp=0
pkgup=0
aurupdate=0
rmorpflag=0
aurflag=0
shutflag=0
rmcache=0

# Declare Functions:
teelog(){
    tee -a $logfile
}
blank() {
    echo | teelog
}
ins_sep(){
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | teelog
}
timestamp() {
    echo $(date) | teelog
}
completion_stamp() {
    echo -ne ${yellow}
    echo -n "${pacup_ver} script completed at: " | teelog
    echo -ne ${nc}
    timestamp
    ins_sep
}
help_message() {
    echo
    echo -e "${yellow}Usage: pacup [options]${nc}"
    echo "If multiple options are specified, the first option will take precedence and may exit the script before other options take effect."
    echo "Eg. If options -h was combined with another option, any other options will have no effect as this help message will be printed and then exit."
    echo
    echo "Available options:"
    echo
    echo "-h   =   Display this help"
    echo "-s   =   Request shutdown on completion of script"
    echo "-a   =   Run AUR helper after completion (autodetect - yay/paru) - USE WITH CAUTION"
    echo "-o   =   Check for and remove orphaned packages only and then quit (this is automatically done at the end of the script)"
    echo "-r   =   Run paccache clean after completion"
    echo
    exit 0
}
check_upgradable() {
    pacman -Sy 
    pkgup=$(pacman -Qu | wc -l)
    
    if [ $pkgup -eq 0 ]; then
    echo -e "${yellow}There are no updates available at this time. Nothing to do.${nc}" | teelog
    completion_stamp
    blank
    exit 0
    fi

    echo
    echo -n "There are " | teelog
    echo -ne ${green}
    echo -n $pkgup | teelog
    echo -ne ${nc}
    echo " packages that can be upgraded using pacman." | teelog
    blank
    sleep 1
    pkglist=$(pacman -Qu)
    echo "Packages with update available:" | teelog
    echo -e ${yellow}
    echo -e "$pkglist"
    echo -ne ${nc}
    sleep 1
    echo "$pkglist" >> $logfile
    blank
}
check_root(){
    echo
    if [ $EUID -ne 0 ]; then
        echo -e "${red}Root access required, please re-run this script with sudo.${nc}"
        echo
        exit 101
    else
        echo -e "Root access detected... [${green}ok${nc}]"
        echo
        sleep 1
    fi
}
check_timeshift(){
    if ! command -v timeshift &> /dev/null; then
        echo -e "${red}[ timeshift ] has not been detected!${nc}"
        echo "This script relies on the package timeshift being installed on the system."
        echo
        echo -e "${yellow}Would you like to install timeshift from the AUR now? [y/N]${nc} "; read inst
        case $inst in
            "y")
                echo
                echo "Attempting to install timeshift from the AUR now..."
                echo
                install_ts;;
            *)
                echo "Please manually install timeshift and re-run this script."
                blank
                echo -ne ${red}
                echo -n "Script cannot continue and will now exit." | teelog
                echo -e ${nc}
                exit 1
        esac
    else
        echo -e "Package [ timeshift ] detected... [${green}ok${nc}]"
        sleep 1
    fi
}
install_ts() {
    if ! command -v git &> /dev/null; then
        echo -e "${red}Command [ git ] not found, attempting to install git using pacman...${nc}"
        pacman -Sy git || exit 102
    else
        echo -e "${green}Command [ git ] found.${nc}"
    fi

    echo
    
    if [ -e /home/$SUDO_USER/git/timeshift ] || [ -e /home/$SUDO_USER/git/timeshift-bin ]; then
        echo "Found old [ timeshift ] git directory, removing..."
        rm -rf /home/$SUDO_USER/git/{timeshift,timeshift-bin} &> /dev/null
    else
        echo "No previous [ timeshift ] git directory found."
    fi

    echo
    echo "Creating directory for [ git ] if it doesn't exist..."

    su $SUDO_USER <<EOF1
    mkdir -p ~/git
EOF1

    echo
    echo "Cloning [ timeshift ] from git to local machine..."
    echo

    su $SUDO_USER <<EOF2
    cd ~/git
    git clone $timeshiftgit
EOF2
    
    echo
    echo "Installing git package [ timeshift ]..."

    echo
    echo "Switching out of sudo to run [ makepkg ] as user: $SUDO_USER"

    su $SUDO_USER <<EOF3
    cd ~/git/timeshift-bin
    makepkg -si
EOF3
}
pac_orphans(){
    echo "Checking for orphaned packages... " | teelog
    echo -ne ${yellow}
    echo -n "$orpkgnum orphaned packages have been found." | teelog
    echo -e ${nc}
    sleep 1
    blank
    if [ $orpkgnum -gt 0 ]; then
        echo "   Removing orphaned packages... " | teelog
        if PAC_OR="$(pacman -Qtdq | pacman -Rns --noconfirm - )"; then
            echo $PAC_OR | teelog
            echo -e "${green}Done.${nc}"
            echo
        else
            echo -ne ${red}
            echo -n "   Error removing orphans." | teelog
            echo -ne ${nc}
            echo " Check log: $logfile" | teelog
            echo
            exit 1
        fi
    else
        echo -e "${green}   No orphans found, nothing to do.${nc}"
        blank
    fi
}
shut_req(){
    echo " ---> Shutdown requested on completion." | teelog
    sleep 2
    blank
    shutcomp=1
}
do_shut(){
    blank
    echo -ne ${yellow}
    echo -n "Shutdown requested. Sending shutdown request to system now." | teelog
    echo -e ${nc}
    shutdown +1 | teelog
}
aur_req(){
    echo -en ${red}
    echo -n "AUR helper auto update requested - Use with caution!" | teelog
    echo -e ${nc}
    blank
    echo -ne "${yellow}Are you sure you want to update ALL of your AUR packages? [y/N] ${nc}"; read aurcheck

    if [[ $aurcheck = "y" ]] || [[ $aurcheck = "Y" ]]; then
        echo "    Auto updating of AUR packages with AUR helper confirmed by user." | teelog
        blank
        echo "    Checking which helper is in installed..."

        if command -v paru &> /dev/null; then
            echo -ne ${yellow}
            echo -n "    Helper [ paru ] detected, using that." | teelog
            echo -e ${nc}
            helper="paru"
            aurupdate=1
            blank
        elif command -v yay &> /dev/null; then
            echo -ne ${yellow}
            echo -n "    Helper [ yay ] detected, using that." | teelog
            echo -e ${nc}
            helper="yay"
            aurupdate=1
            blank
        else
            echo -ne ${yellow}
            echo -n "    AUR Helper could not be detected. Please re-run script without -a option or install [ paru ] or [ yay ]." | teelog
            echo -n "    Alternatively, you can simply update your AUR packages manually with [ makepkg -si ] after downloading the updated files."
            echo -e ${nc}
            echo
            exit 2
        fi
    elif [[ $aurcheck = "n" ]] || [[ $aurcheck = "N" ]] || [[ -z $aurcheck ]]; then
        echo "    AUR option selected but cancelled by user." | teelog
        blank
    else
        echo "    AUR option selected but invalid answer given! Skipping AUR updates." | teelog
        blank
    fi
}
rmorp_req(){
            ins_sep
            echo
            echo -en ${yellow}
            echo -n "Flag [ -o ] used for removing of orphaned packages only" | teelog
            echo -e ${nc}
            pac_orphans
            if [ $shutcomp = 1 ]; then
                do_shut
            fi
            exit 0
}
cli_opts(){
        case $option in
            h) helpflag=1 ;;
            o) rmorpflag=1 ;;
            a) aurflag=1 ;;
            s) shutflag=1 ;;
            r) rmcache=1 ;;
            ?) unknownflag=1 ;;
        esac
}
main_script(){
    blank
    ins_sep
    echo -ne ${yellow}
    echo -n "${pacup_ver} System update script initiated at: " | teelog
    echo -ne ${nc}
    timestamp
    sleep 1
    blank
    
    # Call function to check that there is actually work to do
    check_upgradable
    
    echo -n "Creating new snapshot with [ " | teelog
    echo -ne ${yellow}
    echo -n "timeshift" | teelog
    echo -ne ${nc}
    echo " ]..." | teelog
    blank 
    
    # Proceed if timeshift succeeds, otherwise exit with error code
    if TIM_A="$(timeshift --create --quiet --scripted --comments pacup-script-update 2>&1)"; then
        echo "$TIM_A" | teelog
        blank
        echo -n "Timeshift backup... [" | teelog
        echo -ne ${green}
        echo -n "ok" | teelog
        echo -ne ${nc}
        echo "]" | teelog
        sleep 1
        blank
    else
        echo -e "Timeshift backup...[${red}failed${nc}]"
        echo $TIM_A | teelog
        echo "An error occurred when running [ timeshift ], consult log file for details." | teelog
        echo "Logfile located at: $logfile"
        blank
        completion_stamp
        if [ $shutcomp = 1 ]; then
            do_shut
        fi
        exit 3
    fi
    
    # Perform pacman system update, or exit with error code if pacman fails
    echo -n "Updating system with [ " | teelog
    echo -ne ${yellow}
    echo -n "pacman" | teelog
    echo -ne ${nc}
    echo " ]..." | teelog
    blank
    if PAC_A="$(pacman -Sqyu --noconfirm --noprogressbar 2>&1)"; then
        echo "$PAC_A" | teelog
        blank
        echo -n "Pacman update... [" | teelog
        echo -ne ${green}
        echo -n "ok" | teelog
        echo -ne ${nc}
        echo "]" | teelog
        sleep 1
        blank
    else
        blank
        echo -e "Pacman update... [${red}failed${nc}]"
        sleep 1
        echo $PAC_A | teelog
        echo
        echo -e "${red}An error occurred when running [ ${yellow}pacman${red} ], consult log file for details." | teelog
        echo -ne ${nc}
        echo "Logfile located at: $logfile"
        blank
        echo "Do you want to try applying some fixes? [y/N]: "; read fix_pac
        if [ $fix_pac = "Y" ] || [ $fix_pac = "y" ]; then
            echo "Attempting fix 1:"
            if PAC_B="$(pacman -Sy --needed archlinux-keyring && pacman -Su --noconfirm --noprogressbar 2>&1)"; then
                echo "$PAC_B" | teelog
                blank
                echo -n "Fix 1 worked. Pacman update [" | teelog
                echo -ne ${green}
                echo -n "ok" | teelog
                echo -ne ${nc}
                echo "]" | teelog
                exit 0
            else
                echo "Fix 1 " | teelog
                echo -ne ${red}
                echo -n "failed" | teelog
                echo -ne ${nc}
            fi
        else
            echo -n "Skipping fixes..." | teelog
        fi
        completion_stamp
        if [ $shutcomp = 1 ]; then
            do_shut
        fi
        exit 4
    fi
    
    # Check for orphaned packages and remove if any
    pac_orphans

    if [[ $rmcache = 1 ]]; then
        echo "Pacman cache clean option selected."
        echo "Removing all but last 3 versions of archived packages in cache..."
        paccache -r | teelog
        echo
    fi
    
    if [[ $aurupdate = 1 ]]; then
    aurpkg_install
    elif [ $aurpkgnum -gt 0 ]; then
        echo -ne ${red}
        echo -n $aurpkgnum | teelog
        echo -ne ${yellow}
        echo " foreign packages (likely installed from the AUR) have been detected, it might be a good idea to manually update these packages or re-run this script with -a flag." | teelog
        echo -ne ${nc}
    fi
    
    blank
    completion_stamp
    
    echo
    echo "Update script has completed successfully. Reboot recommended as soon as practical for changes to take effect and to check for stability of new kernel and packages."
    echo
    
    if [ $shutcomp = 1 ]; then
        do_shut
    fi
}

aurpkg_install(){
    echo "Attempting AUR updates with ${helper}..." | teelog
    echo "Switching out of sudo to run AUR updates..." | teelog
    echo -e "${yellow}NOTE: User interaction will be required!${nc}"

    if [ $helper = "paru" ]; then
        su $SUDO_USER <<EOFSUDO
        paru -Syqua --noconfirm
EOFSUDO
    fi
        
    if [ $helper = "yay" ]; then
        su $SUDO_USER <<EOFSUDO1
        yay -Syqua --noconfirm
EOFSUDO1
    fi
    echo "AUR package updates completed. Logging has not been recorded." | teelog
}

## Start of execution ##
########################

clear

# Command-line argument(s)
while getopts hsoar option; do
    cli_opts
done

if [[ $unknownflag = 1 ]]; then
    echo
    echo -e "${red}Unknown flag(s) used!${nc}"
    echo "Please use -h flag to view help which will describe possible usage of this script."
    echo
    exit 1
fi

if [[ $helpflag = 1 ]]; then
    help_message
fi

# Check for root before processing other arguments
check_root

if [[ $shutflag = 1 ]]; then
    shut_req
fi
if [[ $rmorpflag = 1 ]]; then
    rmorp_req
fi
if [[ $aurflag = 1 ]]; then
    aur_req
fi

# Check for log file directory
if [ ! -e /home/$SUDO_USER/logs ]
then
    echo "Log directory not found, creating it..."
    echo
    mkdir -p /home/$SUDO_USER/logs
else
    echo -e "Log directory found... [${green}ok${nc}]"
    echo
fi
touch $logfile

# Check for timeshift
check_timeshift

#Main script runs here
    main_script

Last edited by coxe87b (2024-10-26 10:39:42)


Desktop: Arch Linux  |  i3-gaps WM  |  AMD Ryzen 5700X  |  32GB RAM  |  AMD Radeon RX 6700XT  |  Dual monitors @ 2560x1440
Laptop: Debian Linux  |  i3WM  |  Dell Latitude E7270  |  Intel Core i5-6300U  |  16GB RAM
~ Do or do not, there is no try ~

Offline

#2 2024-10-26 11:12:27

WorMzy
Administrator
From: Scotland
Registered: 2010-06-16
Posts: 12,374
Website

Re: pacup - PACman automated UPdate script (work in progress)

A couple of notes from a quick look over:

- Providing automatic shutdown option upon completion

What if there are pacnew files to merge? Or manual intervention communicated via front page news or post_install scriptlets? This is the main thing that people forget about when writing automatic update scripts.

pacman -Sy 
pkgup=$(pacman -Qu | wc -l)

This is dangerous and leaves the user at risk of partial upgrades if the script doesn't complete the update. Use checkupdates instead.

pacman -Sy git

Another partial upgrade. It'd be better to communicate to the user what prerequisites are needed for the script and abort when they are not present.

You're also making assumptions about sudo being present/used, where the calling users home directory is, where they store AUR/ABS build files, what flags they want to remove orphan packages with, etc.


Sakura:-
Mobo: MSI MAG X570S TORPEDO MAX // Processor: AMD Ryzen 9 5950X @4.9GHz // GFX: AMD Radeon RX 5700 XT // RAM: 32GB (4x 8GB) Corsair DDR4 (@ 3000MHz) // Storage: 1x 3TB HDD, 6x 1TB SSD, 2x 120GB SSD, 1x 275GB M2 SSD

Making lemonade from lemons since 2015.

Offline

#3 2024-10-28 00:05:05

Omarz2012
Member
From: Oshawa, Canada
Registered: 2024-10-19
Posts: 39
Website

Re: pacup - PACman automated UPdate script (work in progress)

very cool project. and also WorMzy, it gives you the option

Offline

#4 2024-10-28 11:47:12

Lone_Wolf
Administrator
From: Netherlands, Europe
Registered: 2005-10-04
Posts: 12,880

Re: pacup - PACman automated UPdate script (work in progress)

coxe87b wrote:

log including application version changes (shows old version -> new version which is a bit extra from the /var/log/pacman.log)

Already possible and shown in terminal and log.

man pacman.conf wrote:

VerbosePkgLists
Displays name, version and size of target packages formatted as a table for upgrade, sync and remove operations.

Maybe using that option can simplify your code ?


Disliking systemd intensely, but not satisfied with alternatives so focusing on taming systemd.

clean chroot building not flexible enough ?
Try clean chroot manager by graysky

Offline

#5 2024-10-28 11:55:54

coxe87b
Member
From: Canberra
Registered: 2019-12-08
Posts: 86

Re: pacup - PACman automated UPdate script (work in progress)

Lone_Wolf wrote:
coxe87b wrote:

log including application version changes (shows old version -> new version which is a bit extra from the /var/log/pacman.log)

Already possible and shown in terminal and log.

man pacman.conf wrote:

VerbosePkgLists
Displays name, version and size of target packages formatted as a table for upgrade, sync and remove operations.

Maybe using that option can simplify your code ?

Thank you for the suggestion, I did not know about this option.


Desktop: Arch Linux  |  i3-gaps WM  |  AMD Ryzen 5700X  |  32GB RAM  |  AMD Radeon RX 6700XT  |  Dual monitors @ 2560x1440
Laptop: Debian Linux  |  i3WM  |  Dell Latitude E7270  |  Intel Core i5-6300U  |  16GB RAM
~ Do or do not, there is no try ~

Offline

#6 2024-10-28 12:06:41

coxe87b
Member
From: Canberra
Registered: 2019-12-08
Posts: 86

Re: pacup - PACman automated UPdate script (work in progress)

Thanks for the response.

WorMzy wrote:

What if there are pacnew files to merge? Or manual intervention communicated via front page news or post_install scriptlets? This is the main thing that people forget about when writing automatic update scripts.

Can you elaborate on how this could be handled within the script?

WorMzy wrote:
pacman -Sy 
pkgup=$(pacman -Qu | wc -l)

This is dangerous and leaves the user at risk of partial upgrades if the script doesn't complete the update. Use checkupdates instead.

Thanks. I will make this change on your advice. Can you elaborate on how it is dangerous to simply sync the package repo with -Sy by itself?

WorMzy wrote:
pacman -Sy git

Another partial upgrade. It'd be better to communicate to the user what prerequisites are needed for the script and abort when they are not present.

One of the improvements I have already decided upon it to simplify by doing this (including for the auto-install AUR helper section). I will be doing this, appreciate your input though.

WorMzy wrote:

You're also making assumptions about sudo being present/used, where the calling users home directory is, where they store AUR/ABS build files, what flags they want to remove orphan packages with, etc.

In the comments section of the script, I mention that it should be installed to /usr/local/bin and must be run with sudo. I also have a check function to prevent the script being run without sudo privileges. Yes, I am making some assumptions based on a typical Arch install, would you suggest using $HOME instead of /home/$SUDO_USER ? How would you suggest improving these things?


Desktop: Arch Linux  |  i3-gaps WM  |  AMD Ryzen 5700X  |  32GB RAM  |  AMD Radeon RX 6700XT  |  Dual monitors @ 2560x1440
Laptop: Debian Linux  |  i3WM  |  Dell Latitude E7270  |  Intel Core i5-6300U  |  16GB RAM
~ Do or do not, there is no try ~

Offline

#7 2024-10-28 12:27:17

Scimmia
Fellow
Registered: 2012-09-01
Posts: 12,045

Re: pacup - PACman automated UPdate script (work in progress)

https://wiki.archlinux.org/title/System … nsupported

It's one of the most basic concepts in Arch.

Last edited by Scimmia (2024-10-28 12:30:36)

Offline

Board footer

Powered by FluxBB