Compare commits

...
4 Commits
Author SHA1 Message Date
Felitendo 8834abe648 cachy-auto-update 1.2.2
The progress bar counts the download as well as the transaction, and every
step says outright that an update is running.
2026-08-14 14:25:11 +02:00
Felitendo b796b2711a Move the bar while packages are downloading
The progress entry sat at "0 von 123" for the whole download and only
started counting once pacman began unpacking - which on a domestic line is
most of the run spent looking like nothing was happening. What was being
watched for was the transaction, and the transaction had not started yet.

Downloading is now a step of its own, worth 30 of the bar against the
transaction's 40, counted the same way: one line per package out of
pacman's "foo-1.2-1-x86_64 downloading...". The database sync just before
it prints the identical shape with the suffix that would give it away
already stripped, so the tally starts only after the ":: Retrieving
packages..." header. Packages already in the cache never announce
themselves, so the step regularly ends short of its total and hands the
rest of its share over when unpacking begins.

The headline follows: "Updates werden heruntergeladen", then
"Systempakete werden aktualisiert".
2026-08-14 14:22:05 +02:00
Felitendo 6538c1510d Say on the progress bar that this is an update running
The headline read "Paketquellen" over KDE's own "0 von 123 Elementen" -
two nouns and a count, with nothing anywhere saying that the machine was
updating itself. Nobody goes looking for this entry; it turns up beside
whatever they were doing, so it has to explain itself in one line.

Each step now says so outright: "Systempakete werden aktualisiert",
"Flatpak-Programme werden aktualisiert", and so on.

The count line underneath belongs to Plasma, which only counts in bytes,
files, dirs or items - "Paketen" is not on offer, so "Elementen" stays,
now under a headline that says what is being counted.
2026-08-14 14:13:54 +02:00
Felitendo ea3f0d21fa Make the progress bar follow the transaction it was supposed to be watching
It sat at "0 of 218 items" for an entire run. The watcher looked for
"(120/218) upgrading foo", which pacman only prints when it is drawing its
progress bar - and the unattended runs this exists for are precisely the ones
started with --noprogressbar, where it instead prints "upgrading glibc..." with
no counter at all. So nothing ever matched and the position never moved.

Both shapes are handled now. Where there is no counter the packages are tallied
here, one line each, and the total is read from the "Package (218)" header
pacman prints before it starts - a better number than checkupdates gives, since
that counts packages with an update available and knows nothing about new
dependencies pulled in alongside them. Replayed against a real 218-package run
from the log: 63, 137, 215, 218 of 218, with the package name in each step.

Offer to switch off CachyOS's reboot notification while enabling. cachyos-hooks
ships a PostTransaction hook that pops up "Reboot recommended!" the moment a
kernel, driver or systemd package is unpacked. That is fine for a manual
upgrade, where the transaction is the last thing happening. In an unattended
one it lands mid-run, with AUR packages still to build and Flatpaks still to
pull, and asking for a restart there is an invitation to cut the update in
half. Overriding the hook by name from /etc/pacman.d/hooks is the standard way
to switch a distribution hook off and is undone by deleting the link.

Offered rather than done, like the existing cachy-update prompt: it is another
package's behaviour and it applies to manual upgrades too.
2026-08-14 11:34:03 +02:00
12 changed files with 229 additions and 53 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@
# Overridable so a packager can pass the version it is actually building
# (`make VERSION=$pkgver`). The literal below is the fallback for builds
# straight from a checkout, and is what a release tag has to carry.
VERSION ?= 1.2.0
VERSION ?= 1.2.2
PREFIX ?= /usr
DESTDIR ?=
+13 -5
View File
@@ -214,11 +214,19 @@ Two things about how it is put together:
the length of the update, holding the connection open and taking instructions
on stdin. It needs **python-gobject**; without it there is simply no bar and
nothing else changes.
- The position inside the repository step comes from pacman's own
`(120/260) upgrading foo` lines. pacman's other `(n/m)` sequences — checking
keys, package integrity, loading files — each count to the same total, so
only the transaction verbs are followed; otherwise the bar would reach the
end three times before the first package was unpacked.
- Downloading and unpacking are two separate steps on the bar. On a domestic
line the download is the longer of the two, and calling the whole thing
"installing" leaves the bar sitting at 4% for six minutes, which reads as a
hang rather than as progress.
- Neither phase carries a counter on an unattended run, so both are counted a
line at a time — `foo-1.2-1-x86_64 downloading...` and `upgrading foo...`.
The database sync just before prints the same shape (` core downloading...`)
with the suffix that would give it away already stripped, so counting starts
only after pacman's `:: Retrieving packages...` header. pacman's other
`(n/m)` sequences — checking keys, package integrity, loading files — each
count to the same total, so only the transaction verbs are followed;
otherwise the bar would reach the end three times before the first package
was unpacked.
This is Plasma's job interface. On a desktop that does not implement it the
helper exits quietly and the ordinary notifications carry on as before.
+12 -4
View File
@@ -122,10 +122,18 @@ of the update and holds that connection open. It requires _python-gobject_.
Where that is missing, or on a desktop with no job interface, there is no
progress entry and nothing else is affected.
The position within the repository step is read from pacman's own
"(120/260) upgrading foo" output. Its other (n/m) sequences - checking keys,
package integrity, loading package files - each count up to the same total and
are deliberately ignored.
Fetching the packages and unpacking them are two steps rather than one. On a
domestic line the download is the longer of the two, and a bar that called the
whole thing "installing" would sit near its beginning for minutes at a time
looking stuck.
Neither phase gets a counter from pacman on an unattended run, so both are
counted here, a line at a time: "foo-1.2-1-x86_64 downloading..." for the
first, "upgrading foo..." for the second. Packages already in the cache are
never announced, so the download step regularly ends short of its total and
gives up the rest of its share when unpacking begins. pacman's other (n/m)
sequences - checking keys, package integrity, loading package files - each
count up to the same total and are deliberately ignored.
# HOW LONG NOTIFICATIONS STAY
+23 -5
View File
@@ -174,6 +174,17 @@ msgstr ""
msgid "cachy-update's update check has been disabled."
msgstr ""
msgid "CachyOS shows a \"Reboot recommended\" notification while an update is still running. Turn it off? [Y/n]"
msgstr ""
#, c-format
msgid "The reboot notification has been turned off. Undo it by deleting %s."
msgstr ""
#, c-format
msgid "Could not write %s."
msgstr ""
msgid "No log yet."
msgstr ""
@@ -219,19 +230,26 @@ msgid "Without a command an interactive menu is shown."
msgstr ""
#. Progress bar
msgid "Repository packages"
#. The headline of the desktop's progress entry. Whoever reads it has not gone
#. looking for it - it appears next to whatever they were doing - so each one
#. says outright that this is an update running, rather than naming the kind of
#. package on its own.
msgid "Downloading updates"
msgstr ""
msgid "AUR packages"
msgid "Updating system packages"
msgstr ""
msgid "Flatpaks"
msgid "Updating AUR packages"
msgstr ""
msgid "AppImages"
msgid "Updating Flatpak apps"
msgstr ""
msgid "Cleaning up"
msgid "Updating AppImages"
msgstr ""
msgid "Cleaning up after the update"
msgstr ""
msgid "Package"
+28 -10
View File
@@ -175,6 +175,17 @@ msgstr "cachy-update benachrichtigt ebenfalls über verfügbare Updates. Dessen
msgid "cachy-update's update check has been disabled."
msgstr "Die Update-Prüfung von cachy-update wurde abgeschaltet."
msgid "CachyOS shows a \"Reboot recommended\" notification while an update is still running. Turn it off? [Y/n]"
msgstr "CachyOS zeigt eine Benachrichtigung \"Neustart empfohlen\", während ein Update noch läuft. Abschalten? [J/n]"
#, c-format
msgid "The reboot notification has been turned off. Undo it by deleting %s."
msgstr "Die Neustart-Benachrichtigung wurde abgeschaltet. Rückgängig durch Löschen von %s."
#, c-format
msgid "Could not write %s."
msgstr "%s konnte nicht geschrieben werden."
msgid "No log yet."
msgstr "Noch kein Protokoll vorhanden."
@@ -220,20 +231,27 @@ msgid "Without a command an interactive menu is shown."
msgstr "Ohne Befehl wird ein interaktives Menü angezeigt."
#. Progress bar
msgid "Repository packages"
msgstr "Paketquellen"
#. The headline of the desktop's progress entry. Whoever reads it has not gone
#. looking for it - it appears next to whatever they were doing - so each one
#. says outright that this is an update running, rather than naming the kind of
#. package on its own.
msgid "Downloading updates"
msgstr "Updates werden heruntergeladen"
msgid "AUR packages"
msgstr "AUR-Pakete"
msgid "Updating system packages"
msgstr "Systempakete werden aktualisiert"
msgid "Flatpaks"
msgstr "Flatpaks"
msgid "Updating AUR packages"
msgstr "AUR-Pakete werden aktualisiert"
msgid "AppImages"
msgstr "AppImages"
msgid "Updating Flatpak apps"
msgstr "Flatpak-Programme werden aktualisiert"
msgid "Cleaning up"
msgstr "Wird aufgeräumt"
msgid "Updating AppImages"
msgstr "AppImages werden aktualisiert"
msgid "Cleaning up after the update"
msgstr "Aufräumen nach dem Update"
msgid "Package"
msgstr "Paket"
+42
View File
@@ -84,6 +84,48 @@ cau_do_enable() {
fi
cau_offer_disable_cachy_update
cau_offer_disable_reboot_hook
}
# CachyOS ships a pacman hook that pops up "Reboot recommended!" the moment a
# kernel, driver or systemd package is unpacked. During a manual upgrade that
# is fine - the transaction is the last thing happening. During an unattended
# one it lands in the middle: the run still has AUR packages to build and
# Flatpaks to pull, and a notification asking for a restart right then is an
# invitation to cut the update in half.
#
# pacman lets a hook be overridden by name from /etc/pacman.d/hooks, which takes
# precedence over /usr/share/libalpm/hooks, and a symlink to /dev/null there
# disables it. That is the standard way to switch off a distribution hook, and
# it is undone by deleting the link.
#
# This one is offered, never decided: it is another package's behaviour and it
# applies to manual upgrades too.
CAU_REBOOT_HOOK="cachyos-reboot-required.hook"
cau_offer_disable_reboot_hook() {
local system="/usr/share/libalpm/hooks/$CAU_REBOOT_HOOK"
local override="/etc/pacman.d/hooks/$CAU_REBOOT_HOOK"
local answer
[[ -t 0 && -t 1 ]] || return 0
[[ -f $system ]] || return 0
[[ -e $override || -L $override ]] && return 0
printf '\n %s\n ' \
"$(cau_msg "CachyOS shows a \"Reboot recommended\" notification while an update is still running. Turn it off? [Y/n]")"
read -r answer || return 0
case "${answer,,}" in
''|y|yes|j|ja) ;;
*) return 0 ;;
esac
if mkdir -p /etc/pacman.d/hooks 2>/dev/null && ln -sfn /dev/null "$override" 2>/dev/null; then
cau_ok "$(cau_msg "The reboot notification has been turned off. Undo it by deleting %s." "$override")"
else
cau_bad "$(cau_msg "Could not write %s." "$override")"
fi
}
# Whether an AUR helper exists at all, without the logging cau_aur_detect does.
+1 -1
View File
@@ -185,7 +185,7 @@ failed=0
# Open the desktop's progress bar, told up front which steps this run will
# perform. Only those count towards the bar, so a machine with no Flatpaks
# does not sit at 85% for the last second of the run.
progress_steps=(repo)
progress_steps=(download repo)
[[ $CFG_AUR == yes ]] && progress_steps+=(aur)
[[ $CFG_FLATPAK == yes ]] && progress_steps+=(flatpak)
[[ $CFG_APPIMAGE == yes ]] && progress_steps+=(appimage)
+1 -1
View File
@@ -43,7 +43,7 @@ cau_appimage_update() {
local user uid count rc=0
local -a cmd
cau_progress_step appimage "AppImages"
cau_progress_step appimage "Updating AppImages"
while read -r user uid; do
[[ -n $user ]] || continue
+1 -1
View File
@@ -126,7 +126,7 @@ cau_aur_update() {
cau_aur_ready || return 0
cau_progress_step aur "AUR packages"
cau_progress_step aur "Updating AUR packages"
pending="$(cau_aur_pending)"
if (( pending == 0 )); then
+1 -1
View File
@@ -28,7 +28,7 @@ cau_flatpak_update() {
cau_have flatpak || return 0
cau_progress_step flatpak "Flatpaks"
cau_progress_step flatpak "Updating Flatpak apps"
# refresh appstream metadata first so remote-ls sees current versions
cau_run_logged flatpak update --appstream --system --noninteractive || true
+103 -22
View File
@@ -27,37 +27,116 @@ cau_pacman_flags() {
done
}
# The verbs pacman puts in front of a package as it works through a
# transaction. Matched against English on purpose: the runner forces LC_ALL=C
# precisely so pacman's output stays parseable.
CAU_PACMAN_OP_RE='^(\([[:space:]]*[0-9]+/[0-9]+\) )?(upgrading|installing|reinstalling|downgrading|removing) [^[:space:]]+'
# Before any of that, everything has to be fetched, and on a domestic line
# that is the longer half of the run: two hundred packages take minutes to
# arrive and seconds to unpack. pacman prints one line per package while it
# does it,
#
# glibc-2.44+r24+g16be1518495f-1-x86_64_v3 downloading...
#
# and nothing else - no counter, no total - so the position here is counted the
# same way the transaction is.
#
# The database sync a few lines earlier prints the very same shape (" core
# downloading..."), and pacman strips the suffix that would tell a database
# from a package, so the count begins only after the header that separates the
# two phases.
CAU_PACMAN_DL_AWK='
/^:: Retrieving packages/ { retrieving = 1; next }
retrieving && / downloading\.\.\.$/ { n++; name = $1 }
END { print n + 0, name }'
# _cau_pacman_progress_watch <logfile>
# Feeds the desktop's progress bar from pacman's own transaction counter.
# --noprogressbar makes pacman print one "(120/260) upgrading foo" line per
# package, and that is the only live measure of how far a transaction has got:
# checkupdates knows the total beforehand, but nothing else knows the position.
# Feeds the desktop's progress bar by watching pacman work, through both of the
# phases a pacman run has: first everything is fetched, then everything is
# unpacked. They are two steps on the bar rather than one, because they are two
# steps to sit through - a run that has been "installing updates" at 4% for six
# minutes has not hung, it is still downloading, and the bar should say so.
#
# The other (n/m) sequences pacman prints - checking keys in keyring, checking
# package integrity, loading package files - are deliberately not matched. Each
# counts up to the same total, so following them would run the bar to the end
# three times over before the first package was unpacked.
# In the transaction, pacman announces each package twice over, in one of two
# shapes, and which one depends on a flag this program sets itself:
#
# Read from the log by polling rather than from a pipe: the log is written
# either by pacman directly or through tee, depending on whether a person is
# watching, and one reader that works for both is worth more here than the
# second or so of latency it costs.
# upgrading glibc... with --noprogressbar, i.e. every timer run
# ( 12/218) upgrading glibc [##] with the bar, i.e. an interactive `run`
#
# Only the second carries a counter, and the unattended runs that this bar
# exists for are exactly the ones that do not get it. So the position is
# counted here instead - one line per package - and the total taken from the
# "Package (218)" header pacman prints before it starts. That header is the
# better number anyway: checkupdates counts packages with an update available
# and knows nothing about the new dependencies pulled in alongside them.
#
# What must not be counted is the other (n/m) sequence pacman prints, for
# hooks and for checking keys, integrity and file conflicts. Each of those runs
# up to its own total, so following them would drive the bar to the end several
# times before the first package was unpacked. Requiring one of the verbs above
# is what excludes them.
#
# Read by polling the log rather than from a pipe: the log is written either by
# pacman directly or through tee depending on whether a person is watching, and
# one reader that works for both is worth the second of latency it costs.
_cau_pacman_progress_watch() {
local log="$1" line last='' pkg
local log="$1"
local total="${CAU_PACMAN_COUNT:-0}" announced processed line pkg last=''
local phase=download fetched shown=''
while :; do
sleep 1
line="$(grep -aoE '^\([[:space:]]*[0-9]+/[0-9]+\) (upgrading|installing|reinstalling|downgrading|removing) [^[:space:]]+' \
"$log" 2>/dev/null | tail -n1)"
[[ -n $line && $line != "$last" ]] || continue
announced="$(grep -aoE '^Packages? \([0-9]+\)' "$log" 2>/dev/null \
| head -n1 | grep -oE '[0-9]+')"
[[ $announced =~ ^[0-9]+$ ]] && (( announced > 0 )) && total="$announced"
line="$(grep -aoE "$CAU_PACMAN_OP_RE" "$log" 2>/dev/null | tail -n1)"
# Nothing unpacked yet, so this is still the download - or the database
# sync ahead of it, which the awk above declines to count.
if [[ -z $line ]]; then
read -r fetched pkg < <(awk "$CAU_PACMAN_DL_AWK" "$log" 2>/dev/null)
[[ $fetched =~ ^[0-9]+$ ]] && (( fetched > 0 )) || continue
[[ $fetched != "$shown" ]] || continue
shown="$fetched"
cau_progress_item "$fetched" "$total"
# Down to the bare name, as the transaction reports it: the file
# pacman names here carries version, release and architecture.
cau_progress_detail "Package" "${pkg%-*-*-*}"
continue
fi
# The first package being unpacked ends the download step. Its share of
# the bar is given up wherever it had got to - packages already in the
# cache are fetched in no time at all and never print a line, so the
# tally regularly stops short of the total it was promised.
if [[ $phase == download ]]; then
phase=install
cau_progress_step repo "Updating system packages" "$total"
fi
# Nothing new since the last look. Checked before the counting grep
# because on a large upgrade this loop spends most of its life here.
[[ $line != "$last" ]] || continue
last="$line"
[[ $line =~ ^\([[:space:]]*([0-9]+)/([0-9]+)\)[[:space:]]+[a-z]+[[:space:]]+(.+)$ ]] || continue
processed="$(grep -acE "$CAU_PACMAN_OP_RE" "$log" 2>/dev/null)"
[[ $processed =~ ^[0-9]+$ ]] || continue
cau_progress_item "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
pkg="${BASH_REMATCH[3]%...}"
cau_progress_detail "Package" "$pkg"
# Where pacman does carry a counter, believe it over the tally: it is
# the same number, but it also knows the true total.
if [[ $line =~ ^\([[:space:]]*([0-9]+)/([0-9]+)\) ]]; then
processed="${BASH_REMATCH[1]}"
total="${BASH_REMATCH[2]}"
fi
cau_progress_item "$processed" "$total"
pkg="${line##* }"
cau_progress_detail "Package" "${pkg%...}"
done
}
@@ -166,7 +245,9 @@ cau_pacman_update() {
local log kind
local -a flags
cau_progress_step repo "Repository packages"
# The download comes first and the watcher moves on to the repo step once
# pacman starts unpacking.
cau_progress_step download "Downloading updates"
if ! cau_pacman_pending; then
cau_info "No repository updates pending"
@@ -310,7 +391,7 @@ cau_pacman_pacnew_count() {
cau_pacman_cleanup() {
local -a orphans
cau_progress_step cleanup "Cleaning up"
cau_progress_step cleanup "Cleaning up after the update"
if [[ $CFG_REMOVE_ORPHANS == yes ]]; then
mapfile -t orphans < <(pacman -Qtdq 2>/dev/null)
+3 -2
View File
@@ -26,11 +26,12 @@ CAU_PROGRESS_FIFOS=()
CAU_PROGRESS_LOCALES=()
# What each step is worth on the bar. Rough shares of a typical run rather than
# anything measured: repositories dominate, the cleanup is a rounding error.
# anything measured: the repositories dominate - fetching them and unpacking
# them about equally, on a domestic line - and the cleanup is a rounding error.
# They do not have to add up to 100 - only the steps a given run will actually
# perform are counted, and the total is normalised against those.
declare -A CAU_PROGRESS_WEIGHTS=(
[repo]=70 [aur]=15 [flatpak]=10 [appimage]=3 [cleanup]=2
[download]=30 [repo]=40 [aur]=15 [flatpak]=10 [appimage]=3 [cleanup]=2
)
CAU_PROGRESS_PLAN=()