Files
dotfiles/dotfiles/.config/quickshell/scripts/update-system.sh
thomas 5c6dad3e91 Feature: Run system updater in Ghostty with floating window rule
Open the update terminal in Ghostty, offering to install it via pkexec
when missing and falling back to foot if declined. Add the
system-update-terminal window rule the script already referred to, so
the window floats centred at 1000x650 instead of tiling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 07:23:24 +01:00

106 lines
4.9 KiB
Bash
Executable File

#!/bin/bash
# Launched by SystemUpdate.qml (top bar, next to the clock) — opens a
# terminal running whatever update command fits this distro, then notifies
# once it's done. Ported from the old waybar custom/updater module
# (.config/waybar/scripts/update-distro.sh); kept multi-distro even though
# this machine is Arch, since these dotfiles aren't.
#
# The window it opens is floated + centered by a matching Hyprland rule
# (system-update-terminal, in .config/hypr/layout/windowrules.lua),
# same treatment as Bitwarden's window, rather than tiling into whatever
# workspace you happened to be on.
iDIR="$HOME/.config/swaync/images"
# Printed inside the terminal before the actual update command runs, so it
# reads as "updating" from the first frame rather than looking like an
# empty/stuck window while pacman/apt/etc. take a moment to start printing.
#
# The terminal runs this in a fresh `bash -c`, which doesn't inherit shell
# functions from this script's own environment (export -f only reaches bash
# processes forked from *this* one, not whatever the terminal execs) — so
# instead, the already-colored text is built once here with real escape
# bytes ($'...'), then %q-quoted into a self-contained `echo '...'` that
# reproduces those same bytes verbatim when the child bash parses it.
msg=" Updating System — please wait "
line=$(printf '=%.0s' $(seq 1 ${#msg}))
banner_text=$'\033[1;36m'"$line"$'\033[0m\n\033[1;36m'"$msg"$'\033[0m\n\033[1;36m'"$line"$'\033[0m\n\n'
BANNER="echo $(printf '%q' "$banner_text")"
# GHOSTTY_INSTALL is how to install the terminal on this distro, if it's
# packaged there at all — only Arch ships it in the official repos.
if command -v paru &> /dev/null; then
CMD="paru -Syu"
LABEL="Arch-based system"
GHOSTTY_INSTALL="pacman -S --needed --noconfirm ghostty"
elif command -v yay &> /dev/null; then
CMD="yay -Syu"
LABEL="Arch-based system"
GHOSTTY_INSTALL="pacman -S --needed --noconfirm ghostty"
elif command -v dnf &> /dev/null; then
CMD="sudo dnf update --refresh -y"
LABEL="Fedora system"
elif command -v apt &> /dev/null; then
CMD="sudo apt update && sudo apt upgrade -y"
LABEL="Debian/Ubuntu system"
elif command -v zypper &> /dev/null; then
CMD="sudo zypper dup -y"
LABEL="openSUSE system"
else
notify-send -i "$iDIR/error.png" -u critical "Unsupported system" "This script does not support your distribution."
exit 1
fi
# --- Terminal: Ghostty, offering to install it if missing ---
# This runs from the bar with no terminal of its own, so there's nowhere to
# type a sudo password — the install goes through pkexec instead, which
# pops up the polkit agent's graphical password prompt. Declining (or a
# failed install) falls back to foot for this run, so updating never gets
# blocked on the terminal choice.
if ! command -v ghostty &> /dev/null; then
if [ -n "$GHOSTTY_INSTALL" ]; then
# -A waits for a click and prints the chosen action's name (nothing if
# the notification is dismissed instead).
choice=$(notify-send -i "$iDIR/error.png" -u critical \
-A install="Install Ghostty" -A foot="Use foot this time" \
"Ghostty not installed" "The system updater runs in Ghostty. Install it now?")
if [ "$choice" = "install" ]; then
if pkexec $GHOSTTY_INSTALL; then
notify-send -i "$iDIR/profile-pic.jpg" -u low "Ghostty installed" "Starting the update..."
else
notify-send -i "$iDIR/error.png" "Ghostty install failed" "Using foot for this update."
fi
fi
else
notify-send -i "$iDIR/error.png" "Ghostty not installed" \
"Install Ghostty for the system updater — using foot for now."
fi
fi
if command -v ghostty &> /dev/null; then
# --gtk-single-instance=false: otherwise, with a Ghostty window already
# open, this would just hand the window off to that running instance and
# return immediately — SystemUpdates.qml relies on this script blocking
# until the update window is closed (that's when it re-checks the count),
# and the "has been updated" notification below would fire up front.
TERMINAL=(ghostty --gtk-single-instance=false --title=update -e)
elif command -v foot &> /dev/null; then
TERMINAL=(foot -T update)
else
notify-send -i "$iDIR/error.png" "Need a terminal:" "Neither Ghostty nor foot is installed."
exit 1
fi
# Keeps the terminal open (whether the update succeeded or failed — read
# always runs regardless, since it's ; not &&) until you press Enter,
# instead of the terminal closing the instant the update command exits and
# taking any output/errors with it.
WAIT_PROMPT="echo; read -p 'Update finished — press Enter to close...'"
# Pull + re-stow the dotfiles first, so config changes from the other machine
# land alongside the package update.
SYNC="$(printf '%q' "$(dirname "$0")/sync-dotfiles.sh")"
"${TERMINAL[@]}" bash -c "$BANNER; $SYNC; $CMD; $WAIT_PROMPT"
notify-send -i "$iDIR/profile-pic.jpg" -u low "$LABEL" 'has been updated.'