#!/usr/bin/env bash set -Eeuo pipefail usage() { cat <<'USAGE' Usage: fedora-update [options] Update Fedora RPM packages, Flatpaks, and firmware. Options: --offline Prepare the DNF transaction as an offline update, then ask before rebooting. --no-flatpak Skip Flatpak updates. --no-firmware Skip fwupd firmware checks/updates. -y, --yes Pass -y to DNF/Flatpak where possible and accept prompts from this script. -h, --help Show this help. USAGE } log() { printf '\n\033[1m==> %s\033[0m\n' "$*"; } warn() { printf '\033[33mWARN:\033[0m %s\n' "$*" >&2; } die() { printf '\033[31mERROR:\033[0m %s\n' "$*" >&2; exit 1; } have() { command -v "$1" >/dev/null 2>&1; } confirm() { local prompt="$1" answer if [[ "${ASSUME_YES}" == "true" ]]; then return 0; fi read -r -p "$prompt [y/N] " answer [[ "$answer" =~ ^[Yy]$ ]] } DNF="$(command -v dnf5 || command -v dnf || true)" [[ -n "$DNF" ]] || die "Could not find dnf5 or dnf." OFFLINE=false DO_FLATPAK=true DO_FIRMWARE=true ASSUME_YES=false while [[ $# -gt 0 ]]; do case "$1" in --offline) OFFLINE=true ;; --no-flatpak) DO_FLATPAK=false ;; --no-firmware) DO_FIRMWARE=false ;; -y|--yes) ASSUME_YES=true ;; -h|--help) usage; exit 0 ;; *) die "Unknown option: $1" ;; esac shift done YUM_YES=() [[ "$ASSUME_YES" == "true" ]] && YUM_YES=(-y) log "Refreshing and updating RPM packages with $(basename "$DNF")" if [[ "$OFFLINE" == "true" ]]; then sudo "$DNF" upgrade --refresh --offline "${YUM_YES[@]}" log "DNF offline transaction prepared" else sudo "$DNF" upgrade --refresh "${YUM_YES[@]}" fi if [[ "$DO_FLATPAK" == "true" ]]; then if have flatpak; then log "Updating Flatpaks" flatpak update "${YUM_YES[@]}" log "Removing unused Flatpak runtimes" flatpak uninstall --unused "${YUM_YES[@]}" || true else warn "flatpak not found; skipping Flatpak updates." fi fi if [[ "$DO_FIRMWARE" == "true" ]]; then if have fwupdmgr; then log "Checking firmware updates with fwupd" sudo fwupdmgr refresh --force || warn "fwupdmgr refresh failed; continuing." sudo fwupdmgr get-updates || true if confirm "Run firmware updates now?"; then sudo fwupdmgr update || warn "Firmware update command returned a non-zero status." fi else warn "fwupdmgr not found; install fwupd to manage firmware updates." fi fi log "Checking whether a reboot is recommended" NEEDS_RESTARTING_OUTPUT="$(mktemp)" set +e sudo "$DNF" needs-restarting >"$NEEDS_RESTARTING_OUTPUT" 2>&1 status=$? set -e if [[ $status -eq 0 ]]; then printf 'No reboot required according to dnf needs-restarting.\n' elif grep -qiE 'No such command|unknown command|unrecognized|Unable to resolve|not found' "$NEEDS_RESTARTING_OUTPUT"; then warn "Could not run '$(basename "$DNF") needs-restarting'. Try: sudo $DNF install dnf5-plugins" else cat "$NEEDS_RESTARTING_OUTPUT" warn "A reboot is recommended." fi rm -f "$NEEDS_RESTARTING_OUTPUT" if [[ "$OFFLINE" == "true" ]]; then if confirm "Reboot now to run the offline DNF transaction?"; then sudo "$DNF" offline reboot else printf 'Run this later to apply it: sudo %s offline reboot\n' "$DNF" fi fi