My manjaro update bash script

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 :stuck_out_tongue:

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..."
1 Like

It’ll work for you - it probably does
until it doesn’t
and the AI needs to be asked again … :wink:


… why would you need to
--set-branch stable
in
# Refresh mirrors prompt
part of the script? …

when it assumes to be run on and for the stable branch?


… why would you need this kind of
# Lock-handling prompt function
?


Why would it (successfully) continue after having failed when just trying the same thing once again?

# Retry loop if either failed


the
# Remove orphaned packages (auto-confirm)
function

migth be safe - but with “autoconfirm” (without asking)
it might not be



I can’t see the scripts utility.
But:
that’s just me :man_shrugging:

It works for the last 3 years and i continue improving it via ai ,core functionality code is mine but ai contributed a lot with a proper manual intervention of course

i hard-coded --set-branch stable just to be safe

Lock-handling prompt function is needed in case pamac runs in the background without i know it searching for update or in a case of a partial previous update and a pacman db lock

Retry loop if either failed ,in case of some error of pacman or yay updates i wanted to be able to run update routine again or exit the script

Remove orphaned packages (auto-confirm), i never encountered an orphan package i want to keep ,but of course it can be altered to be manually

I can’t see the scripts utility. ok

I don’t have any problem with yay personally (I use it occasionally), but it does seem rather opinionated when there is already pamac as a default to handle AUR.

Scripting AUR builds is risky, I think, unless there’s a failsafe way to manage failed builds, missing checksums and poorly written PKGBUILDs – all of which are common with AUR in the wild.

Well, I’m truly sorry…
I tried to be harsh, really I did, but that’s all I’ve got. :smile_cat:

Regards.

1 Like

originally i used pamac but i had problems maintaining sudo pass for the entire script with pamac so i removed it and used yay instead

yes aur is risky ,my aur packages are kept to necessary only and as last resort

Unless you manually change branches, you shouldn’t have to set it back to stable with every update.

1 Like

i dont change branches

as i said i hardcoded stable just to be safe ,it can be altered of course

Don’t get me wrong, I’m a bigger fan of yay than I am of pamac. :wink:

Indeed. Unfortunately, not everyone is that cautious (they should be). The only real concern I would have are those who would throw caution to the wind, while being blissfully unaware of the risks.

Still, you can’t script against ignorance. :facepalm:

I’ve taken a copy and will play with it on a VM when I have the chance.

Regards.

1 Like

Ok…

# Clean package cache (auto-confirm)
Why do you clean the package cache every time?

# Flatpak repair (system)
# Flatpak repair (user)
And why do you want to repair the Flatpaks if nothing is broken?

i keep no package cache ,we are always online with fast internet connections right? so whats the point?

from time to time flatpaks glitch and appear to need constant update for the same exact versions installed, flatpak repair is used to verify and fix issues with installed Flatpak applications and runtimes. It ensures the data stored in the Flatpak system is intact and matches the expected metadata and checksums from the repository.

Well, it’s not the fear of losing the Internet…
it’s more about having the option of downgrading to the old version if something is broken in the new one :wink:

Why would it be?

That case doesn’t seem to be handled well.

I have more.

But:
the thread has progressed much since my initial response.

It’ll be difficult to keep all the suggestions and opinions apart in a sensible way.

For reliability:
you should use pacman throughout and from the start

and:
make clear that your AUR helper is not pamac, but yay instead


There is no intelligence in “Artificial Intelligence” (AI).

No - there really isn’t.

Just try to reason with it …

true but at the same time keeping an older package can be dangerous ,script can be modified to be interactive regarding deleting cache or any other part

pacman db lock is a possibility so the script gives you all available options ,wait for the lock to be lifted or delete it and proceed or exit and handle it manually

dont i state in the original post that i use yay? and why it is wrong to run yay after pacman to handle normal and aur updates ?

it is
with pamac

not so much with pacman

… if there is a lock - why and how would you expect it “to be lifted”?
… with time …


You do.

It may not be “wrong”, it’s just inconsistent.
You use pamac.
You use pacman.
You use yay.

… It’s just a mess - IMO :man_shrugging:

Use your intelligence - don’t transfer it to AI.

pamac runs in the background in every manjaro system and searches for updates every x hours so pacman db lock is a possibility which can be lifted when pamac finishes updates if someone set it to auto or in case previous pacman updates (in script) haven’t finished somehow

my script uses pacman first for a full system update and then yay for a full system update as a fail-safe and aur upates

i dont use pamac in my script

i asked for harsh critique so your mess evaluation is fine by me

as i said core functionality and decisions in the code are mine ,ai does what i ask it to do and with a lot and i mean a lot manual intervention and editing

not true
there is a regular check for available updates by default, yes
But: it can be disabled or the frequency changed.

I’m sorry - indeed you do not.

good - I mean no disrespect

that is a very ambiguous statement



still:



You wanted to be criticized :wink:
Harshly, even … which I don’t think I have done.

I may just be too stupid to understand the utility and workings of your script …

if in script pacman updates havent finished somehow (nearly impossible) or pamac performs updates in the background if set to auto ,or in case of a failed or interrupted previous update ,there might be a pacman db lock ,so in these occurrences i want to be able to choose what to do with the lock and the script provides every possible option
i must add that db lock routine runs only if a db lock is detected

ambiguous statement why? i provided ai with my core script and asked to make specific changes or add specific functionality and i always strictly instruct it to not change anything else (it does some times) and list me all changes, i review its proposals but all the decisions are mine

no i dont think you are stupid or harsh ,i consider all your comments

the script just asks sudo password ,maintains it during the whole duration of the script ,gives you choices in case of something goes south or not expected and updates the whole complete system and house-keeps at the end ,i wanted a worry free easy update script and i think it accomplishes it to a degree

I was just writing a polite and comprehensive response.

Then the “System” decided to wanting to “re load” - and I lost all of what I wrote.

I should have known better.
Indeed: I did.
And I still did it, this time. …

I’m not going to re-create it.
Not today.

I always do my writing in an external editor - then just copy/paste to here.
This time I didn’t - and was taught to really always do what I nearly always do.

So:
I disagree with no explanation …

1 Like

Me personally…i would not over-automate on a rolling arch based distro. And would also not trust ai to write decent code - generative ai is notoriously bad at doing that, because it just statistically summarizes stackoverflow…and the questions and errors and bad advice is abundant there.

For example, why the -Syyu everytime?

But it has some good ideas. If it works for you, why not. But i wouldn’t advice novice users to use scripts without understanding what those do. Just my 2 cents.

2 Likes