Fully automated system update – beginner-friendly

After constructive community feedback to my post, I further tweaked, fine-tuned and improved my system update script that I want to share here - see end of this comment. First I want to expound on what the script does and does not do.

It is a Bash script I use on CachyOS for weekly system updates with deliberately conservative .pacnew handling.

This is not an attempt to fully automate system administration, and it does not merge or overwrite configuration files. The goal is to reduce noise while ensuring anything risky remains manual.


What It Does

  • Runs a standard pacman -Syu
  • Cleans package cache (paccache)
  • Creates timestamped logs (with rotation)
  • Skips execution entirely if the system is offline (nm-online, suspend/resume safe)

.pacnew Handling (Key Point)

The script never applies .pacnew files automatically. Instead:

1. Identical / Comment-Only .pacnew

  • Compared against the existing file (ignoring comments/blank lines)
  • Deleted automatically
  • Logged as safe cleanup

2. Critical Configuration Files

Paths related to boot, initramfs, networking, authentication, and system security are treated as critical, e.g.:

/etc/mkinitcpio.conf*
/etc/limine.conf
/etc/NetworkManager/
/etc/systemd/network/
/etc/pam.d/
/etc/sudoers
/etc/security/

For these:

  • The .pacnew is copied to a timestamped backup directory
  • The original config is left untouched
  • A desktop notification is sent only if at least one critical pacnew exists

No merging, no overwriting.

3. Non-Critical .pacnew

  • Removed automatically
  • Logged for reference

Conditional Rebuilds Only

  • mkinitcpio -P is run only if a critical mkinitcpio-related pacnew appears
  • Limine is updated only if Limine configs are involved

No unconditional rebuilds.


What This Script Does Not Do

  • :cross_mark: No automatic .pacnew merges
  • :cross_mark: No config overwrites
  • :cross_mark: No unattended system changes beyond pacman -Syu
  • :cross_mark: No replacement for reading Arch/Cachy news

Manual review of critical files remains mandatory.


Summary

This is a noise-reduction helper, not a “set and forget” updater:

  • Trivial .pacnew files are cleaned up
  • Potentially dangerous ones are preserved and flagged
  • Control stays with the user

If you already update manually and review configs, this just removes busywork.


Short FAQ / Anticipated Objections

Q: Why automate updates at all?
A: This script just ensures consistency and avoids forgetting, while keeping config changes manual.

Q: Why delete non-critical .pacnew files automatically?
A: They are defaults I have not customized. Critical paths are explicitly excluded.

Q: Why not use pacdiff / pacman-contrib?
A: The script does not replace them. It filters obvious no-ops and highlights when manual review is actually needed.

Q: Isn’t this against Arch philosophy?
A: The script does not automate decisions. It only automates detection and cleanup. All risky changes remain manual.

Q: What if the system is suspended or offline?
A: The script checks network availability first and exits cleanly if offline.


Note:
This script does not remove the need to understand or decide on .pacnew changes. It can be arguably safer for beginners than ad-hoc manual updates because

  1. critical configuration files are never overwritten automatically,
  2. identical or comment-only .pacnew files are filtered out to reduce noise,
  3. updates are skipped entirely when the system is offline, avoiding partial upgrades, and
  4. decisions about meaningful .pacnew changes remain manual — which beginners must make anyway, regardless of tooling.

It can be run manually with the noninteractive flag; I have a systemd timer to run it automatically in the background.

Disclaimer:
This script is provided as-is; review it before use and run it at your own risk, as with any custom update tooling on Arch-based systems.

Script
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# CachyOS update + pacnew manager
# - Notify ONLY when >=1 critical pacnew was backed up
# - Interactive & noninteractive modes
# - Auto-handling identical/comment-only pacnews
# - Auto backup of critical pacnews
# - mkinitcpio + Limine rebuild when needed
# - Log rotation, color output, emojis, clean summary
# - Network-aware (safe on suspend/resume)
# -----------------------------------------------------------------------------

# ------------------ Restore safe .bashrc first ------------------
if [ -f "$HOME/.bashrc_saved" ]; then
    cp "$HOME/.bashrc_saved" "$HOME/.bashrc"
fi

# ------------- Configuration -------------
LOG_DIR="$HOME/logs/system-updates"
mkdir -p "$LOG_DIR"

LOG_FILE="$LOG_DIR/update-$(date +%Y%m%d-%H%M%S).log"

# Keep last 5 update logs
ls -1t "$LOG_DIR"/update-*.log 2>/dev/null | tail -n +6 | xargs -r rm -f

# Tee output to terminal + log
exec > >(tee -a "$LOG_FILE") 2>&1

# -------- LOG HEADER --------
echo "============================================"
echo "System update run started at: $(date)"
echo "Host: $(hostname)"
echo "============================================"
echo

# -------- NETWORK GUARD (suspend/resume safe) --------
if command -v nm-online >/dev/null 2>&1; then
  echo "Waiting for network (nm-online, timeout 60s)..."
  if ! nm-online -q -t 60; then
    echo
    echo "============================================"
    echo "UPDATE SKIPPED DUE TO NO NETWORK"
    echo "Timestamp: $(date)"
    echo "============================================"
    exit 0
  fi
else
  echo "WARNING: nm-online not found; proceeding without network guard."
fi

BACKUP_DIR="$HOME/pacnew_backups"
ORPHAN_LOG_DIR="$HOME/orphan-removal-logs"
UPGRADE_LOG_DIR="$HOME"
SCRIPT_NAME="$(basename "$0")"

mkdir -p "$BACKUP_DIR" "$ORPHAN_LOG_DIR"

# Critical patterns
critical_patterns=(
  "/etc/mkinitcpio.conf"
  "/etc/mkinitcpio.conf.d/"
  "/etc/systemd/resolved.conf"
  "/etc/limine.conf"
  "/etc/default/grub"
  "/boot/loader/"
  "/etc/NetworkManager/"
  "/etc/systemd/network/"
  "/etc/iwd/"
  "/etc/dhcpcd.conf"
  "/etc/wpa_supplicant/"
  "/etc/nftables.conf"
  "/etc/ufw/"
  "/etc/pam.d/"
  "/etc/sudoers"
  "/etc/security/"
  "/etc/locale.gen"
  "/etc/locale.conf"
  "/etc/vconsole.conf"
)

# Colors
RED="\e[31m"; GREEN="\e[32m"; YELLOW="\e[33m"; CYAN="\e[36m"
BOLD="\e[1m"; RESET="\e[0m"

# Flags & arrays
NONINTERACTIVE=false
REBUILD_MKINITCPIO=false
UPDATE_LIMINE=false
MKINIT_REQUIRED=false
LIMINE_REQUIRED=false
SCRIPT_SUCCESS=true

declare -a CRITICAL_BACKUPS
declare -a SAFE_DELETED
declare -a SKIPPED_PACNEW

# ------------- Helpers -------------
usage() {
  cat <<EOF
Usage: $SCRIPT_NAME [--noninteractive] [--help]
EOF
}

# Parse flags
while [[ $# -gt 0 ]]; do
  case "$1" in
    --noninteractive) NONINTERACTIVE=true; shift ;;
    --help) usage; exit 0 ;;
    *) echo "Unknown option: $1"; exit 1 ;;
  esac
done

print_heading() {
  echo -e "${BOLD}${CYAN}=============================================="
  echo -e "   CachyOS Update & pacnew manager "
  echo -e "==============================================${RESET}"
}

is_critical() {
  local p
  for p in "${critical_patterns[@]}"; do
    if [[ "$p" == */ ]]; then [[ "$1" == $p* ]] && return 0
    else [[ "$1" == "$p" || "$1" == "$p"* ]] && return 0
    fi
  done
  return 1
}

effectively_identical() {
  local a="$1" b="$2"
  awk '!/^[[:space:]]*#/ && NF' "$a" > /tmp/.pa &&
  awk '!/^[[:space:]]*#/ && NF' "$b" > /tmp/.pb
  cmp -s /tmp/.pa /tmp/.pb
  local r=$?
  rm -f /tmp/.pa /tmp/.pb
  return $r
}

color_diff() { diff --color=always -u "$1" "$2" || true; }

open_kwrite() {
  command -v kwrite >/dev/null && kwrite "$@" >/dev/null 2>&1 &
}

backup_pacnew() {
  local dest="$BACKUP_DIR/$(basename "$1").$(date +%Y%m%d-%H%M%S).bak"
  sudo cp "$1" "$dest"
  CRITICAL_BACKUPS+=("$1 -> $dest")
}

# ---------------- UPDATE -----------------
print_heading

command -v cachyos-rate-mirrors >/dev/null && sudo cachyos-rate-mirrors

echo -e "${CYAN}System upgrade...${RESET}"
sudo pacman -Syu --noconfirm || SCRIPT_SUCCESS=false

upgrade_log="$UPGRADE_LOG_DIR/cachyos-updates-$(date +%Y%m%d-%H%M%S).log"
awk '/upgraded/ {print}' /var/log/pacman.log | tail -n 20 > "$upgrade_log"

sudo paccache -rk3 <<< y
sudo paccache -ruk0 <<< y

echo -e "${CYAN}Checking for pacnews...${RESET}"
mapfile -t PACNEW_LIST < <(sudo find /etc -type f -name "*.pacnew")
for pac in "${PACNEW_LIST[@]}"; do
  orig="${pac%.pacnew}"

  [[ ! -f "$orig" ]] && backup_pacnew "$pac" && continue

  if effectively_identical "$orig" "$pac"; then
    sudo rm -f "$pac"
    SAFE_DELETED+=("$pac (identical)")
    continue
  fi

  if is_critical "$orig"; then
    backup_pacnew "$pac"
    [[ "$orig" == /etc/mkinitcpio.conf* ]] && MKINIT_REQUIRED=true
    [[ "$orig" == *limine* ]] && LIMINE_REQUIRED=true
  else
    sudo rm -f "$pac"
    SAFE_DELETED+=("$pac")
  fi
done

# mkinitcpio
if $MKINIT_REQUIRED; then
  sudo mkinitcpio -P || SCRIPT_SUCCESS=false
  REBUILD_MKINITCPIO=true
fi

# Limine
if $LIMINE_REQUIRED; then
  if command -v limine-update >/dev/null; then
    sudo limine-update || SCRIPT_SUCCESS=false
  elif command -v limine-mkinitcpio >/dev/null; then
    sudo limine-mkinitcpio || SCRIPT_SUCCESS=false
  fi
  UPDATE_LIMINE=true
fi

# ---------- NOTIFICATION ----------
if [[ ${#CRITICAL_BACKUPS[@]} -gt 0 ]]; then
  notify-send -u critical "Critical .pacnew Found" \
    "⚠️ One or more CRITICAL pacnew files were saved to $BACKUP_DIR"
fi

# ---------- FINAL SUCCESS / FAILURE ----------
if $SCRIPT_SUCCESS; then
  echo "SUMMARY: SUCCESS ✅"
  notify-send -u normal "System Update Complete" "SUCCESS ✅"
else
  echo "SUMMARY: FAILURE ❌"
  notify-send -u critical "System Update FAILED" "FAILURE ❌ (see log)"
fi

exit 0

I can’t find /etc/limine.conf, only /etc/default/limine

Thanks for your feedback.

Limine itself only cares about the config under /boot.
Files like /etc/default/limine or /etc/limine.conf are distro-specific helpers and may not exist at all. On my CachyOS system neither is present; Limine is configured directly under /boot.

My script is doing the conservative thing:

  • It treats Limine-related config changes as critical
  • It does not assume a single canonical path
  • It reacts only if a .pacnew appears in a Limine-related location

That’s why the logic is pattern-based, not hardcoded to one file.

This is still not an acceptable way to handle pacnew files.

You are just .. deleting them under various circumstances?

What criteria besides the supposed ‘identical’ is used there?

The few files I would think that could normally be ignored are not part of your list (ex; shadow, passwd).

And ones that very much do need attention are included and removed automatically.

Whats the idea here?

Thanks for the feedback — that’s a fair concern.

The script does not merge or overwrite any configuration files. It classifies .pacnew files into three categories:

  1. Effectively identical (ignoring comments and blank lines)
    These are removed automatically. The comparison strips comments and empty lines from both files before comparing, so cosmetic upstream changes don’t accumulate unnecessary clutter.
  2. High-impact configuration paths (boot, initramfs, networking, PAM, sudo, security, locale, etc.)
    These are never applied automatically. The .pacnew is copied to a timestamped backup directory, the original file is left untouched, and manual review is still required.
  3. Other paths
    These are removed automatically. This is an opinionated choice: the script assumes that if a file is outside high-impact subsystems and has not been locally customized, upstream default adjustments are typically low-risk. The goal is noise reduction rather than exhaustive manual diffing of every default change.

You’re correct that this is a trade-off. The script does not attempt to decide whether every upstream change is important — it only tries to separate high-impact system components from everything else. Users who prefer reviewing every single .pacnew manually should absolutely continue using pacdiff or similar tools.

Thats not opinionated.

Thats wrong.

First because it simply misunderstands how pacnews work - if there was no local customization then there would be no pacnew.

Regardless of which you are delivering breakages to end user systems.

I again point to the basic introduction on the subject;

So it is not a trade-off.

It is actively delivering breaking non-standard changes incongruent with the guidelines of how to properly admin these systems.

I think it falls within the purview of user safety to remove this from the public until such time that these dangerous functions are removed.

I think we’re talking past each other slightly, so let me clarify the intent and scope once more.

This script does not redefine how pacnew files work, nor does it claim to replace proper administrative judgment. It makes a documented choice to focus on a narrow, high-impact subset of configuration paths, and to reduce noise elsewhere. That choice is explicit, limited in scope, and reversible by design. Users who are uncomfortable with that trade-off should not use the script — which is a perfectly valid outcome.

I intended to add a short policy note before the .pacnew handling section to make this scope and intent unambiguous for readers. Since editing is now blocked, I’ll include the note here for clarity:

Policy note:
This script deliberately limits its scope to a small set of high-impact configuration files. It does not attempt to exhaustively manage all .pacnew files. Configuration changes outside that scope are left untouched so users can review and handle them manually if they wish. The goal is to reduce noise, not to redefine best practices.

Regarding the decision to remove the post “for user safety”: I find that disproportionate while a technical discussion is still ongoing. Reasonable disagreement over scope and philosophy does not, by itself, make a tool dangerous — especially when its behavior is documented and opt-in.

I’m acting in good faith, explaining assumptions, and clarifying limitations. I would appreciate the same courtesy in return, and I ask that you reconsider the removal in light of the clarification above.

Sorry, but it is difficult to communicate in good faith when it’s unknown if the messages are generated by AI and it’s unknown how much input was there from a human.

You would be right, were it not for the FACT that what is in the script is entirely open for inspection. Have you tried to look at it? Have you tried to use it on a test machine? Have you checked all the points I raised?

It is now my impression that you, CSCS, and the others who answered previously cannot accept that the Arch guidelines are not followed strictly, but that that does not necessarily mean you run off the rails if you deviate.

Arch adherents, incl. Arch fork adherents, only accept dogma, the way Arch set it out, and nothing else.

Tell me something: do you drive your car exactly the same way as when you passed your driving test and got your driver’s license?

Nevertheless, contrary to the Arch guidelines, use of my script is not compulsory, so if you are uncomfortable with it, then don’t use it, it really is as simple as that. But don’t judge it only because it was written by Ai, judge it by its contents. I have been using it for months and it has not failed me. Will it never fail me? I hope so. Will CachyOS never fail me if I update according to the guidelines? I would hope so, but in both cases the failure possibility exist. Better not to use Cachy then. I’ll stick with the script, you are entirely free to do what you think is best.

Last but not least, I think it is extremely condescending to have taken the script out of public view, judging that CachyOS users cannot judge for themselves or have the means to help judge for themselves whether to use it or not. Hiding it from the public is telling them you, the public, are too dumb to judge for yourself; it is like censorship of politically “incorrect” articles, magazines and books.

And to hide it to the public when we were in full discussion about certain point is rudeness in the extreme.

P.S. I was honest enough to state upfront it was a script written by Ai, yet you reproach me not to act in good faith. Would you, and the others, even have made such a big deal about it if I had presented it as my own? I doubt it.

Ai is helpful to many but I think if users want to experiment they should do so on there own accord, not by some users scripts here. Teaching users directly how to handle sensitive system tasks such as updates is a better route we should be taking.

This is just my thoughts, not the bible.

I say this only because if a new user relies on this script and it’s inadequate or even later down the line the syntax changes or any other reason, the script is left void, this user does not even understand it or why it’s not working, if they know and understand the proper ways of managing these actions they can do so confidently, if they want to automate the process then they can look into it, but having the bare minimum understanding is beneficial from my perspective.

I’d appreciate if less hostility was here however.. I do suggest not throwing Ai code around the forum though, let people interested in Ai do there own exploring and experiments.

To note: cscs is appointed a leader here, I trust his judgment here, there is no need to battle back and forth.

Take criticisms and work on them.. I also think there are better suited platforms that promote these kinds of scripts for more advanced users :shrug:

I don’t spend too much time here on the forum but from a brief overview cscs has put up valid points regarding the practice of the script.

You must remember there are extremely new users on the forums, never touching linux ever, it’s not about them being ‘dumb’

It’s about following best practices and for the most part keeping these newer users from entering into a non-learning path, a path of the unknown

Allow these newer folk to walk a little before they run.

Right, and then they could use the script if they want to without having to reinvent the wheel, as it were.

Absolutely, I never said anything else. Every user should, however, decide for themselves whether they are up to it or not, and if they are not they should not get involved. Every user is a sovereign person, and in addition, those who already use or switch to Linux are more aware of their own capabilities, so why not let them decide for themselves, rather than have someone else decide for them?
Take criticisms and work on them.

Presumably that is directed at me, which, if so, I take exception to. I have been polite from the beginning, but when someone comes along and tells me I am not acting in good faith when I have been honest to state I got the script from Ai, then my feathers get ruffled a bit. It is not because someone is a moderator that they are right by definition or don’t have to be courteous.

I will not do that anymore, although I do not agree with a policy of treating (new) users like children and insisting on following Arch dogma. Of course, what I think about the policy is irrelevant, I can take it or leave it.

Good point, but it is a 2-way street and goes for everyone, esp. for those lack some basic courtesy. I very much doubt that the leader or his assistants accept that as a principle.

Absolutely, but they are not children, they can decide for themselves whether they engage with scripts or not. Censorship will not do the trick.

Anyway, it is what it is, and I know where I stand.

I don’t presume to know if a user is an adult or a child, what I do consider is if they should proceed with caution, learn about senstive system tasks and use best practices.

I understand you are the type to have never driven a forumula 1 car and neglected the advice of the standard use of operation or safety, taken the keys and gone for a spin because you’re not a child.

Best practices and safety I consider to be a normal thing, wether it’s children or adults, maybe you don’t think so.

I know you’re dead set on your view, your view is not the only one.

Again it’s just my view, you don’t have to agree with it.

We can move on from this, the script has been removed and I agree with the action.

I think Granite wanted the final word, there you are Granite, thanks have been passed.

No, I am not dead set on my view, that is pure projection as you guys are the ones dead set on your dogmatic views.

It has to be the Arch way or the highway: that is ideology, not a fact-based technical approach. CSCS raised a few questions that were fair, which I replied to in detail. No, I did not say “you are the boss so you are right”, I never do. To my surprise he acted in an extremely ill-mannered fashion by removing it from the public in the middle of a discussion.

If I would not have said the script was Ai generated you guys would have looked at it, asked 1 or 2 questions or made remarks, to which I would have provided the answers, and that would have been it. Unfortunately, I was honest and the shit hit the fan.

Forums like Cachy/Arch ones optimize for ideological purity, not necessarily for user outcomes. One-to-one problem solving, iteration and context is where value is derived, dead shttps://www.reverso.net/text-translationet views don’t get you anywhere.

Anyway, thanks for your feedback and high-handed, condescending approach, and please pass my thanks to your colleagues and “leader”.

I’m outta here.

For the record .. none of it was even related to the AI source.

It was entirely because of what it does.

That neither @granite nor the LLM being consulted seemed to fully comprehend how pacnews worked certainly contributed to the situation.

But to declare this as any sort of purity based rejection - as related to “AI” or otherwise - is entirely baseless.

I fully outlined the problems which were that it performs incorrect ‘maintenance’ tasks ala simply deleting all but a few pacnews. A patently incorrect approach and not something suitable for public consumption. “Beginners”, as alluded to by the title, all the less so.