For the past three years, I’ve been developing and refining a Bash shell script—enhanced with AI—to automate system updates on Manjaro.
The script assumes you’re using the following:
Stable branch
Yay as your AUR helper
Flatpaks
Meld for diffing config files
GNOME Terminal
feel free to comment harshly ![]()
Disclaimer: This script is intended for advanced users who fully understand what it does. You should only use it if you’re confident in your ability to modify it to fit your own setup and preferences. New users: consider yourselves warned. I take no responsibility for any unintended consequences from running this script without understanding it first.
Latest Version Updates
Minor cosmetic improvements
Enhanced handling of .pacnew / .pacsave / modified files using Meld, with an option to delete them after review
Added initial checks for PGP signature errors, with progressive troubleshooting options:
Attempts to fix incrementally
Prompts for user action
Falls back to a full reset if necessary
Or exits gracefully if all else fails
#!/bin/bash
# Define color codes
cyan='\033[0;36m'
light_red='\033[1;31m'
reset='\033[0m'
# Keep sudo alive during the script
echo
sudo -v
(sudo -v; while true; do sleep 60; sudo -v; done) &
SUDO_REFRESH_PID=$!
trap 'kill $SUDO_REFRESH_PID' EXIT INT TERM
# Function to execute commands and check for errors
run_command() {
echo -e "${cyan}Running: $@${reset}"
# Execute the command with yes feeding inputs, then capture its status
"$@" < <(yes)
local status=$?
if [ $status -ne 0 ]; then
echo -e "${light_red}Error: Command '$@' failed with exit code $status${reset}"
return 1
else
echo -e "${cyan}Command '$@' completed successfully.${reset}"
return 0
fi
}
# Lock-handling prompt function
prompt_for_db_lock_resolution() {
while [ -f /var/lib/pacman/db.lck ]; do
echo -e "${light_red}Pacman database is locked!${reset}"
echo "Options:"
echo " r - Retry after a short wait"
echo " d - Delete the lock file"
echo " e - Exit the script"
read -rp "Your choice (r/d/e): " choice
case "$choice" in
r|R)
echo -e "${cyan}Retrying in 3 seconds...${reset}"
sleep 3
;;
d|D)
echo -e "${cyan}Deleting pacman lock file...${reset}"
sudo fuser -v /var/lib/pacman/db.lck || sudo rm -f /var/lib/pacman/db.lck*
;;
e|E)
echo -e "${light_red}Exiting due to pacman lock.${reset}"
exit 1
;;
*)
echo -e "${light_red}Invalid choice. Try again.${reset}"
;;
esac
echo -e "${cyan}\n\n\n${reset}"
done
}
# Open Manjaro pages
xdg-open https://forum.manjaro.org/c/announcements/stable-updates/12/ >/dev/null 2>&1 &
sleep 1
xdg-open https://repo.manjaro.org/ >/dev/null 2>&1 &
# Clear the terminal
clear
echo
# Proceed with update prompt
read -p "Proceed with the Update? (n or any other key) " update
if [[ "$update" = "n" ]]; then
exit
fi
echo
# Check for PGP signature issues in a safe dry-run update
echo -e "${cyan}Checking for keyring issues...${reset}"
if ! sudo pacman -Syuw --noconfirm >/dev/null 2>&1; then
echo
echo -e "${light_red}Failed to simulate full system upgrade. Package databases may be broken.${reset}"
echo
fi
echo
# Try to detect signature issues from pacman logs
signature_errors=$(journalctl -p err -b | grep -iE 'invalid.*signature|corrupted package')
key_fix_failed=0
if [[ -n "$signature_errors" ]]; then
echo -e "${light_red}PGP signature issues detected in recent logs:${reset}"
echo "$signature_errors"
echo
read -p "Attempt to refresh keys? (y or any other key to skip) " keys
echo
if [[ "$keys" = "y" ]]; then
if ! run_command sudo pacman-key --refresh-keys; then
echo
echo -e "${light_red}Key refresh failed.${reset}"
echo
read -p "Try full keyring reset and re-init? (y or any other key to skip) " reset_keys
echo
if [[ "$reset_keys" = "y" ]]; then
echo -e "${cyan}Resetting pacman keyring...${reset}"
echo
if ! run_command sudo rm -rf /etc/pacman.d/gnupg; then
echo -e "${light_red}Failed to force-remove old keyring.${reset}"
echo
fi
if ! run_command sudo pacman-key --init; then
echo -e "${light_red}Failed to initialize keyring.${reset}"
echo
fi
if ! run_command sudo pacman-key --refresh-keys; then
echo -e "${light_red}Key refresh still failed after reset.${reset}"
echo
key_fix_failed=1
fi
else
key_fix_failed=1
fi
fi
echo
echo -e "${cyan}Populating keyring with default keys...${reset}"
if ! run_command sudo pacman-key --populate archlinux manjaro; then
echo -e "${light_red}Failed to populate keys. Continuing anyway...${reset}"
echo
fi
else
key_fix_failed=1
fi
else
echo -e "${cyan}No signature problems detected in recent logs.${reset}"
fi
echo
# If key fixes failed, prompt to retry or exit
if [[ "$key_fix_failed" -eq 1 ]]; then
echo -e "${light_red}PGP keyring problems may still exist.${reset}"
echo
read -p "Would you like to rerun this step? (r to retry, any other key to exit) " retry
echo
if [[ "$retry" = "r" ]]; then
exec "$0" "$@" # Rerun the same script with original args
else
echo -e "${light_red}Exiting due to unresolved key issues.${reset}"
echo
exit 1
fi
fi
# Refresh mirrors prompt
read -p "Refresh Mirrors? (n or any other key) " mirrors
if [[ "$mirrors" != "n" ]]; then
echo
if ! run_command sudo pacman-mirrors --continent --api --protocols all --set-branch stable; then
echo -e "${light_red} Failed to refresh mirrors.${reset}"
read -rp "Press Enter to exit..."
exit 1
fi
fi
echo -e "${cyan}\n\n\n${reset}"
# Function to update system with retry logic
perform_updates() {
prompt_for_db_lock_resolution
if ! run_command sudo pacman -Syyu; then
echo -e "${light_red}Failed to update system.${reset}"
pacman_failed=true
else
pacman_failed=false
fi
echo -e "${cyan}\n\n\n${reset}"
prompt_for_db_lock_resolution
if ! run_command yay -Syu --devel --timeupdate; then
echo -e "${light_red}Yay update failed.${reset}"
yay_failed=true
else
yay_failed=false
fi
}
# Run updates first time
perform_updates
# Retry loop if either failed
while [[ "$pacman_failed" == true || "$yay_failed" == true ]]; do
echo -e "${light_red}One or both of pacman and yay failed.${reset}"
read -p "Would you like to retry (r) or exit (e)? " choice
case "$choice" in
[Rr]* )
echo -e "${cyan}Retrying updates...${reset}"
perform_updates
;;
[Ee]* )
echo -e "${light_red}Exiting script...${reset}"
exit 1
;;
* )
echo -e "${light_red}Please answer with 'r' to retry or 'e' to exit.${reset}"
;;
esac
done
echo -e "${cyan}\n\n\n${reset}"
# Remove orphaned packages (auto-confirm)
mapfile -t orphaned_packages < <(sudo pacman -Qtdq)
if [ ${#orphaned_packages[@]} -ne 0 ]; then
if ! run_command sudo pacman -Rns --noconfirm "${orphaned_packages[@]}"; then
echo -e "${light_red}Failed to remove orphaned packages, continuing...${reset}"
fi
else
echo -e "${cyan}No orphaned packages to remove.${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
# Clean package cache (auto-confirm)
if ! run_command sudo pacman -Scc; then
echo -e "${light_red}Failed to clean package cache, continuing...${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
# Flatpak updates
if ! run_command sudo flatpak update; then
echo -e "${light_red}Flatpak update (sudo) failed, continuing...${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
if ! run_command flatpak update; then
echo -e "${light_red}Flatpak update (user) failed, continuing...${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
# Flatpak repair (system)
if ! run_command sudo flatpak repair; then
echo -e "${light_red}Flatpak system repair failed, continuing...${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
# Flatpak repair (user)
if ! run_command flatpak repair --user; then
echo -e "${light_red}Flatpak user repair failed, continuing...${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
# Find .pacnew and .pacsave files
found_files=$(sudo find / -regextype posix-extended -regex ".+\.pac(new|save)" 2> /dev/null)
skipped_files=()
if [ -n "$found_files" ]; then
echo -e "${light_red}Found .pacnew .pacsave files:${reset}"
echo "$found_files"
echo
read -p "Press Enter to continue"
while IFS= read -r pacfile; do
base="${pacfile%.pacnew}"
base="${base%.pacsave}"
original=$(ls "$base"* 2>/dev/null | grep -vE '\.pacnew$|\.pacsave$' | head -n 1)
if [ -f "$original" ]; then
# Create a lock file
lockfile=$(mktemp)
gnome-terminal -- bash -c "
echo -e '${cyan}Original file:${reset} $original';
echo -e '${cyan}Compared to:${reset} $pacfile';
echo;
echo -e '${light_red}Launching meld...${reset}';
meld \"$original\" \"$pacfile\";
echo;
read -p 'Delete $pacfile? (y/N) ' answer;
if [[ \$answer =~ ^[Yy]$ ]]; then
sudo rm \"$pacfile\" && echo && echo -e '${light_red}$pacfile deleted.${reset}';
else
echo
echo -e '${cyan}Skipped.${reset}';
echo \"$pacfile\" >> \"$lockfile.skipped\"
fi;
echo;
echo 'Press Enter to close this window...'; read;
rm \"$lockfile\""
# Wait until the lock file is removed
while [ -e "$lockfile" ]; do
sleep 1
done
# Check if the terminal wrote a skipped file marker
if [ -e "$lockfile.skipped" ]; then
skipped_files+=("$pacfile")
rm "$lockfile.skipped"
fi
else
echo
echo -e "${light_red}Original file not found for:${reset} $pacfile"
fi
done <<< "$found_files"
# Show skipped files
if [ "${#skipped_files[@]}" -gt 0 ]; then
echo
echo -e "${light_red}Skipped .pacnew/.pacsave files:${reset}"
for f in "${skipped_files[@]}"; do
echo "$f"
done
fi
else
echo -e "${cyan}No .pacnew .pacsave files, continuing...${reset}"
fi
echo -e "${cyan}\n\n\n${reset}"
# Prevent console from closing
echo "Sudo keepalive PID: $SUDO_REFRESH_PID"
echo
read -p "Press Enter to exit..."