diff --git a/README.md b/README.md index f52daaf..e62925a 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,9 @@ Kernel 7.2.11-arch1-1 Last update 2026-10-22 13:43 ``` -The kernel comes from Arch, so it's shown separately. If a newer kernel is waiting for a reboot, "version" says so. +The kernel comes from Arch, so it's shown separately. If a newer kernel is waiting for a reboot, "version" says so. It also tells you if a newer version of CoreDesktop is available. + +You don't have to check by hand: a few minutes after you log in, and every few hours after that, CoreDesktop checks for a new version and shows a notification if there is one. Click it to run "update" in a terminal, or dismiss it. Once dismissed, you won't be notified again until there's an even newer version. ### Sibling projects diff --git a/install.sh b/install.sh index 48b1352..7026595 100755 --- a/install.sh +++ b/install.sh @@ -190,6 +190,10 @@ bash ~/.local/share/coredesktop/system/nvidia.sh echo "Configuring the shell..." bash ~/.local/share/coredesktop/shell/setup.sh +# Notify in GNOME when a new version of CoreDesktop is available +echo "Configuring update notifications..." +bash ~/.local/share/coredesktop/system/update-notify.sh + # Apply GNOME dconf settings only if the settings file has changed since last run bash ~/.local/share/coredesktop/gnome/apply-gnome-settings.sh diff --git a/system/systemd/coredesktop-update-check.service b/system/systemd/coredesktop-update-check.service new file mode 100644 index 0000000..b702ca2 --- /dev/null +++ b/system/systemd/coredesktop-update-check.service @@ -0,0 +1,11 @@ +[Unit] +Description=Notify when a new version of CoreDesktop is available +# Notifications need a desktop session to show in +ConditionEnvironment=|WAYLAND_DISPLAY +ConditionEnvironment=|DISPLAY + +[Service] +# Not oneshot: the script waits, without a time limit, for the notification +# to be clicked or dismissed +Type=simple +ExecStart=%h/.local/share/coredesktop/system/update-check diff --git a/system/systemd/coredesktop-update-check.timer b/system/systemd/coredesktop-update-check.timer new file mode 100644 index 0000000..5babc95 --- /dev/null +++ b/system/systemd/coredesktop-update-check.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Check for a new version of CoreDesktop every few hours + +[Timer] +OnStartupSec=5min +# Counted from when the last check finished, so a notification left open for +# hours doesn't have another check queued up behind it +OnUnitInactiveSec=4h + +[Install] +WantedBy=timers.target diff --git a/system/update-check b/system/update-check new file mode 100755 index 0000000..28d1a5f --- /dev/null +++ b/system/update-check @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Purpose : Show a GNOME notification when a newer CoreDesktop is available. +# Clicking it (or its "Update now" button) runs update in a terminal. +# Run by coredesktop-update-check.timer (see system/update-notify.sh). + +# Wrapped in { ...; exit; } so bash reads the whole script up front; update +# replaces this file (git pull) while the notification is waiting for a click. +{ + +# The newest version the user has been told about. Dismissing a notification +# means not hearing about that version again, only about a newer one. +NOTIFIED="$HOME/.local/state/coredesktop/update-notified" + +latest="$("$HOME/.local/bin/version" --check)" || exit 0 +[ "$(cat "$NOTIFIED" 2> /dev/null)" = "$latest" ] && exit 0 + +# Waits until the notification is clicked or dismissed, then prints the action +# ("default" is a click on the notification itself, empty if dismissed) +action="$(notify-send --wait \ + --app-name=CoreDesktop \ + --icon=software-update-available \ + --action=default=Update \ + --action=update="Update now" \ + "CoreDesktop update available" \ + "Version $latest is ready. Click to run update.")" || exit 0 + +mkdir -p "$(dirname "$NOTIFIED")" +echo "$latest" > "$NOTIFIED" + +[ "$action" = "default" ] || [ "$action" = "update" ] || exit 0 + +# Open the first terminal found. bash -i reads ~/.bashrc, as if the user had +# typed update themselves, and the window stays open to show how it went. +run_update=(bash -ic 'update; echo; read -rp "Press Enter to close this window."') +if command -v xdg-terminal-exec &> /dev/null; then + terminal=(xdg-terminal-exec) +elif command -v kgx &> /dev/null; then + terminal=(kgx --) +elif command -v ghostty &> /dev/null; then + terminal=(ghostty -e) +elif command -v kitty &> /dev/null; then + terminal=(kitty) +else + notify-send --app-name=CoreDesktop "No terminal found" "Open a terminal and run update." + exit 1 +fi + +# Run the terminal as its own unit: when this service exits, systemd stops +# everything still running in it, which would close the terminal mid-update +exec systemd-run --user --quiet --collect -- "${terminal[@]}" "${run_update[@]}" +} diff --git a/system/update-notify.sh b/system/update-notify.sh new file mode 100644 index 0000000..87b9603 --- /dev/null +++ b/system/update-notify.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Purpose : Install and enable the timer that checks for a new version of +# CoreDesktop and shows a GNOME notification (system/update-check). +# Run by install.sh and on every update (updates.d/16-update-notify.sh) +# so changes to the units reach existing machines. + +set -e + +COREDESKTOP="$HOME/.local/share/coredesktop" +UNITS="$HOME/.config/systemd/user" + +mkdir -p "$UNITS" +cp "$COREDESKTOP"/system/systemd/coredesktop-update-check.{service,timer} "$UNITS/" +systemctl --user daemon-reload +systemctl --user enable --now coredesktop-update-check.timer diff --git a/updates.d/16-update-notify.sh b/updates.d/16-update-notify.sh new file mode 100644 index 0000000..df83a21 --- /dev/null +++ b/updates.d/16-update-notify.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +COREDESKTOP="$HOME/.local/share/coredesktop" + +run_update() { + bash "$COREDESKTOP/system/update-notify.sh" +} diff --git a/version b/version index d338ad4..05b60fc 100755 --- a/version +++ b/version @@ -6,6 +6,9 @@ # the local checkout is on, plus the short hash to tell same-day commits apart, # e.g. 2026.10.22 (c5f0651). The kernel is shown separately because it comes # from Arch, not CoreDesktop. +# +# version --check prints the newer version and exits 0 if one is available, +# exits 1 if this machine is up to date, or 2 if it couldn't check. COREDESKTOP="$HOME/.local/share/coredesktop" @@ -13,16 +16,40 @@ source "$COREDESKTOP/utils.sh" if ! git -C "$COREDESKTOP" rev-parse --git-dir &> /dev/null; then echo "CoreDesktop isn't installed in $COREDESKTOP." - exit 1 + exit 2 fi -version="$(git -C "$COREDESKTOP" log -1 --format='%cd (%h)' --date=format:%Y.%m.%d)" +# Version string for a commit, e.g. 2026.10.22 (c5f0651) +describe() { + git -C "$COREDESKTOP" log -1 --format='%cd (%h)' --date=format:%Y.%m.%d "$1" +} + +# Print the newer version if the remote has commits this checkout doesn't. +# Returns 1 if up to date, 2 if the remote couldn't be reached. +newer_version() { + GIT_TERMINAL_PROMPT=0 timeout 30 git -C "$COREDESKTOP" fetch --quiet &> /dev/null || return 2 + local behind + behind="$(git -C "$COREDESKTOP" rev-list --count 'HEAD..@{upstream}' 2> /dev/null)" || return 2 + (( behind > 0 )) || return 1 + describe '@{upstream}' +} + +if [ "$1" = "--check" ]; then + newer_version + exit +fi + +version="$(describe HEAD)" # Local edits to the checkout mean this isn't exactly the version above if [ -n "$(git -C "$COREDESKTOP" status --porcelain --untracked-files=no)" ]; then version="$version, modified" fi +if latest="$(newer_version)"; then + version="$version, $latest is available (run update)" +fi + kernel="$(uname -r)" # If the running kernel's modules are gone, a newer kernel was installed if [ ! -d "/usr/lib/modules/$kernel" ]; then