shellcheck / shellcheck (push) Failing after 18s
Checking for newer versions goes to the network and can take several seconds with nothing on screen. Show "Checking for updates..." with a spinner on the terminal until every check is done, then print the results all at once. The spinner goes to stderr and only to a terminal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
212 lines
6.7 KiB
Bash
Executable File
212 lines
6.7 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Purpose : Show which version of CoreDesktop this machine is running
|
|
#
|
|
# CoreDesktop is a rolling release, so its version is the date of the commit
|
|
# the local checkout is on, plus the short hash to tell same-day commits apart,
|
|
# e.g. 2026.10.22 (c5f0651). The kernel and video driver are shown separately
|
|
# because they come from Arch, not CoreDesktop, along with any newer release of
|
|
# either that update would install.
|
|
#
|
|
# Sibling projects (see sibling) are listed with their versions too. If any has
|
|
# a newer version, version asks, for each one, whether to update it now.
|
|
#
|
|
# 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"
|
|
|
|
source "$COREDESKTOP/utils.sh"
|
|
|
|
if ! git -C "$COREDESKTOP" rev-parse --git-dir &> /dev/null; then
|
|
echo "CoreDesktop isn't installed in $COREDESKTOP."
|
|
exit 2
|
|
fi
|
|
|
|
# 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
|
|
|
|
# Checking for newer versions goes to the network and can take several
|
|
# seconds, so show a spinner on the terminal until the results are ready. It
|
|
# goes to stderr and only to a terminal, so output that's piped stays clean.
|
|
spinner_pid=""
|
|
start_spinner() {
|
|
[ -t 2 ] || return 0
|
|
(
|
|
frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
i=0
|
|
printf '\e[?25l' >&2
|
|
while :; do
|
|
printf '\r%s Checking for updates...' "${frames[i++ % ${#frames[@]}]}" >&2
|
|
sleep 0.1
|
|
done
|
|
) &
|
|
spinner_pid=$!
|
|
}
|
|
stop_spinner() {
|
|
[ -n "$spinner_pid" ] || return 0
|
|
kill "$spinner_pid" 2> /dev/null
|
|
wait "$spinner_pid" 2> /dev/null
|
|
spinner_pid=""
|
|
printf '\r\e[K\e[?25h' >&2
|
|
}
|
|
trap stop_spinner EXIT
|
|
trap 'stop_spinner; exit 130' INT TERM
|
|
|
|
start_spinner
|
|
|
|
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
|
|
|
|
# A version without its epoch and pkgrel, e.g. 1:26.2.6-1 is 26.2.6. Arch's
|
|
# kernel puts its suffix in the version too (7.2.14.arch1), so drop that.
|
|
upstream() {
|
|
local v="${1#*:}"
|
|
v="${v%-*}"
|
|
echo "${v%.arch*}"
|
|
}
|
|
|
|
# Packages with a newer version in the repos, as "name old -> new" lines.
|
|
# checkupdates syncs a copy of the package database, so it needs no root and
|
|
# doesn't leave a partial upgrade behind. Empty if it couldn't check.
|
|
pending=""
|
|
if command -v checkupdates &> /dev/null; then
|
|
pending="$(timeout 30 checkupdates 2> /dev/null)"
|
|
fi
|
|
|
|
# Print the newer upstream version of package $1 if the repos have one. A
|
|
# rebuild of the same version (only the pkgrel changed) doesn't count.
|
|
newer_package() {
|
|
local name old _ new
|
|
while read -r name old _ new; do
|
|
[ "$name" = "$1" ] || continue
|
|
[ "$(upstream "$old")" != "$(upstream "$new")" ] && upstream "$new"
|
|
return
|
|
done <<< "$pending"
|
|
return 1
|
|
}
|
|
|
|
kernel="$(uname -r)"
|
|
# The package the running kernel came from (linux, linux-lts, ...)
|
|
kernel_pkg="$(cat "/usr/lib/modules/$kernel/pkgbase" 2> /dev/null)"
|
|
case "$kernel" in
|
|
*-lts) kernel_pkg="${kernel_pkg:-linux-lts}" ;;
|
|
*-zen) kernel_pkg="${kernel_pkg:-linux-zen}" ;;
|
|
*-hardened) kernel_pkg="${kernel_pkg:-linux-hardened}" ;;
|
|
*) kernel_pkg="${kernel_pkg:-linux}" ;;
|
|
esac
|
|
|
|
# If the running kernel's modules are gone, a newer kernel was installed
|
|
if [ ! -d "/usr/lib/modules/$kernel" ]; then
|
|
kernel="$kernel (newer kernel installed, reboot to use it)"
|
|
fi
|
|
if latest="$(newer_package "$kernel_pkg")"; then
|
|
kernel="$kernel, $latest is available (run update)"
|
|
fi
|
|
|
|
# NVIDIA's driver if nvidia.sh installed it, otherwise Mesa, which drives
|
|
# Intel and AMD graphics (vulkan-intel and vulkan-radeon are built with it)
|
|
driver=""
|
|
for pkg in nvidia-utils nvidia-580xx-utils mesa; do
|
|
if installed="$(pacman -Q "$pkg" 2> /dev/null)"; then
|
|
case "$pkg" in
|
|
nvidia*) driver="NVIDIA $(upstream "${installed#* }")" ;;
|
|
*) driver="Mesa $(upstream "${installed#* }")" ;;
|
|
esac
|
|
if latest="$(newer_package "$pkg")"; then
|
|
driver="$driver, $latest is available (run update)"
|
|
fi
|
|
break
|
|
fi
|
|
done
|
|
|
|
# 10-pacman.sh rewrites this log on every update
|
|
if [ -f "$PACMAN_UPDATE_LOG" ]; then
|
|
last_update="$(date -r "$PACMAN_UPDATE_LOG" '+%Y-%m-%d %H:%M')"
|
|
else
|
|
last_update="never"
|
|
fi
|
|
|
|
# Sibling projects, as "name<TAB>installed<TAB>available" lines (see sibling)
|
|
siblings="$(bash "$COREDESKTOP/sibling" check --porcelain 2> /dev/null)"
|
|
|
|
# Hours since the package mirror last synced with Arch, empty if unknown
|
|
age="$(mirror_age_hours)"
|
|
|
|
stop_spinner
|
|
|
|
echo "CoreDesktop $version"
|
|
echo "Kernel $kernel"
|
|
[ -n "$driver" ] && echo "Video driver $driver"
|
|
echo "Last update $last_update"
|
|
|
|
newer_siblings=()
|
|
if [ -n "$siblings" ]; then
|
|
echo
|
|
echo "Sibling projects"
|
|
width=0
|
|
while IFS=$'\t' read -r name _; do
|
|
(( ${#name} > width )) && width=${#name}
|
|
done <<< "$siblings"
|
|
while IFS=$'\t' read -r name installed available; do
|
|
case "$available" in
|
|
"") ;;
|
|
"?") installed="$installed, couldn't check for a newer version" ;;
|
|
*)
|
|
installed="$installed, $available is available"
|
|
newer_siblings+=("$name"$'\t'"$available")
|
|
;;
|
|
esac
|
|
printf ' %-*s %s\n' "$width" "$name" "$installed"
|
|
done <<< "$siblings"
|
|
fi
|
|
|
|
# Packages newer than the mirror's last sync aren't shown above, or installed
|
|
if [ -n "$age" ] && (( age >= MIRROR_STALE_HOURS )); then
|
|
if (( age >= 48 )); then ago="$(( age / 24 )) days"; else ago="$age hours"; fi
|
|
echo
|
|
echo -e "\e[33mYour package mirror ($(first_mirror)) last synced with Arch $ago ago, so"
|
|
echo -e "newer packages may be missing. Run update to switch to up-to-date mirrors.\e[0m"
|
|
fi
|
|
|
|
# Offer each newer sibling, but only to someone at the terminal to answer
|
|
if (( ${#newer_siblings[@]} > 0 )) && [ -t 0 ] && [ -t 1 ]; then
|
|
echo
|
|
chosen=()
|
|
for sibling in "${newer_siblings[@]}"; do
|
|
IFS=$'\t' read -r name available <<< "$sibling"
|
|
read -rp "Update $name to $available? [y/N] " response
|
|
[[ "$response" =~ ^[yY]([eE][sS])?$ ]] && chosen+=("$name")
|
|
done
|
|
if (( ${#chosen[@]} > 0 )); then
|
|
echo
|
|
bash "$COREDESKTOP/sibling" update "${chosen[@]}"
|
|
exit
|
|
fi
|
|
fi
|