Files
cachy-auto-update/doc/cachy-auto-update.1.scd
T
Felitendo f9cd8a0ace Keep the progress bar moving for the whole run
Three stretches of a run had no way to report anything, and the bar
handled each of them badly.

pacman prints nothing at all between "starting full system upgrade" and
the transaction it eventually prepares. On a 161-package backlog that
silence ran to three minutes and nineteen seconds, and the bar spent all
of it frozen on "0 of 161" - a counter seeded from checkupdates before
there was anything to count. Working out the upgrade is now a step of its
own, with a label and deliberately no item count, because the number was
the part that was lying.

A step that turns out to have no work is dropped from the bar instead of
being handed its share for nothing. Packages already in the cache are
never announced by pacman, so a run that only has to unpack used to jump
thirty points the moment unpacking started; the same went for AUR with
nothing pending and for machines with no Gear Lever or no Flatpaks.

pacman's output is line-buffered through stdbuf. Writing to a log rather
than a terminal, libc released it in 4KB blocks - around a hundred and
sixty "upgrading foo..." lines at a time - so the bar sat still and then
leapt to the end of the step in one poll.

What is left is work whose length genuinely cannot be known: resolving a
transaction, and an AUR helper compiling for a quarter of an hour. Those
now creep along a curve that approaches the end of their step without
reaching it. The item counter stays put throughout - it is the field that
would be lying if it moved - and any real report overtakes the creep. A
bar that has not moved since it appeared is read as a hang, and somebody
who reads it that way reaches for the power button mid-update.

The bar is also monotonic now. Dropping a step rescales the run, and the
conflict-recovery loop restarts pacman and its tally from the top; both
are honest, neither is a reason to show a bar that retreats.
2026-08-20 19:45:56 +02:00

227 lines
9.7 KiB
Scdoc

cachy-auto-update(1)
# NAME
cachy-auto-update - unattended background updates for CachyOS
# SYNOPSIS
*cachy-auto-update* [_command_] [_options_]
# DESCRIPTION
*cachy-auto-update* keeps a CachyOS machine current without anybody having to
think about it: repository packages, AUR packages, Flatpaks and AppImages are
updated in the background, with no password prompt and no terminal.
Run without a command it opens a small interactive menu with the two switches
that matter - automatic updates on/off and notifications on/off - plus the
current status, and a settings screen covering every remaining option so the
configuration file never has to be edited by hand. In the settings screen the
arrow keys select, Space or Right changes a value, and _q_ goes back; every
change is written out immediately.
The actual work is done by a systemd system service. The timer ticks hourly;
whether a tick does anything is decided by _UpdateInterval_ (daily by default).
A run that is postponed - low battery, a game running, somebody else using
pacman - is simply retried at the next tick.
# COMMANDS
*enable*
Turn automatic updates on and enable the systemd timer.
*disable*
Turn automatic updates off and stop the timer.
*notifications* on|off
Turn desktop notifications on or off.
*status*
Show the current state plus an evaluation of every condition that can
postpone a run. This is the first thing to look at when the updater
appears to be doing nothing.
*run* [--force] [--dry-run]
Update now. *--force* skips the interval, battery and gaming checks;
*--dry-run* lists what would be updated and changes nothing. The check
for another running package manager is never skipped.
*log* [-n _num_] [-f] [-a]
Show the log of the last run. *-a* shows the rolling log instead, *-f*
follows it.
*-h*, *--help*
Show a short help text.
*-V*, *--version*
Show the version.
# CONDITIONS
Before anything is installed, a run is postponed when:
- the battery is below _MinBatteryPercent_ (ignored on mains power, and on
machines without a battery);
- _RequireAC_ is set and the machine is not plugged in;
- a game is running - detected via GameMode, a list of known game processes, or
an application holding a blocking idle inhibitor;
- pacman's database is locked, or pacman, yay, paru, pamac or a similar tool is
running.
The last check is what keeps *cachy-auto-update* out of the way of manual
package management. It cannot work in the other direction: if a manual pacman
run starts while an update is already in flight, that run will report the usual
locked-database error.
# HOW UPDATES ARE APPLIED
Repository packages are updated with *pacman -Syu --noconfirm*, but only after
*checkupdates*(8) has confirmed there is something to do, so on a quiet day
pacman's lock is never taken at all.
A package that has to replace another one is handled silently. If pacman would
stop to ask whether a conflicting package may be removed, the transaction is
retried once with that question answered affirmatively, unless
_AutoResolveConflicts_ is turned off. Files on disk that collide with a package
are *not* forced - that stays a human decision. A signature failure triggers one
keyring refresh and one retry.
AUR packages are built and installed as the locked *cachy-auto-update* system
account, because *makepkg*(8) refuses to run as root. That account has no
password and no shell, and is allowed - through _/etc/sudoers.d/cachy-auto-update_
- to invoke *pacman* without one. No user password is ever stored anywhere.
A failed AUR build is not reported the first time it happens; only a failure
that repeats is worth waking somebody up for.
Flatpak system installations are updated directly as root, user installations
inside each user's own account. AppImages are updated through Gear Lever, for
users with a graphical session, and AppImages whose application is currently
running are skipped.
The machine is never restarted automatically. A kernel update that makes a
restart necessary is reported by *cachy-auto-update status*, not by a
notification: the running kernel loses its module tree as soon as pacman
unpacks the new one, so a bubble would fire while the run is still working
through AUR packages and Flatpaks, and reads as an invitation to restart in the
middle of it.
# PROGRESS
For as long as a run is working, the notification area carries a live progress
entry: the step being performed, how many items it has got through, an overall
percentage, and the package currently being unpacked under "Details". This is a
job in the sense of *org.kde.JobViewServer*, the same mechanism a file manager
uses while copying, rather than a notification - which is what makes it a bar
instead of a line of text.
The desktop withdraws a job as soon as the D-Bus connection that requested it
closes, so a helper process runs inside each graphical session for the duration
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.
Working out the upgrade, fetching it and unpacking it are three steps rather
than one. Between "Starting full system upgrade" and the transaction it
eventually prepares, pacman prints nothing at all, and on a large backlog that
silence runs to minutes; that stretch therefore carries a label of its own and
deliberately no item count, because a counter frozen at "0 of 161" reads as a
stuck update. On a domestic line the download is then the longest of the three,
and a bar that called the whole thing "installing" would sit near its beginning
for minutes at a time looking stuck.
A step that turns out to have no work is dropped from the bar instead of being
handed its share for nothing. Packages already in the cache are never
announced, so a run with everything already fetched skips the download step
outright rather than jumping when unpacking starts; the same applies to AUR
with nothing pending, or a machine with no Flatpaks installed.
Neither counted 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. pacman's output is line-buffered
through *stdbuf*(1) so those lines arrive as they happen - writing to a log
rather than a terminal, libc would otherwise release them in 4KB blocks, around
a hundred and sixty packages at a time. 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
A message that reports the machine still needing a person - an update that
failed, a package database left locked, packages that had to be held back -
stays on screen until it is dismissed. A message like that is only worth
sending if it is still there when somebody comes back to the machine.
Everything else times out by itself, an update that simply worked included.
Nothing should have to be clicked away for having gone right.
This is set per message rather than left to the notification daemon. Daemons do
keep critical-urgency messages up, and the specification asks them to, but that
is a recommendation, it does not cover the normal-urgency messages here that
still need somebody to act, and urgency separately governs sound and whether
do-not-disturb is overridden.
Where nobody is logged in the message is spooled and delivered at the next
login, with the same distinction preserved.
# INTERRUPTED UPDATES
Before a transaction starts, a notification says an update is running and asks
for the machine to be left on. It is withdrawn again when the result arrives,
so one bubble is used rather than two.
While a transaction is running, *cachy-auto-update* holds a
*systemd-inhibit*(1) lock on _sleep_ and _shutdown_ in blocking mode, so a
suspend, a lid close or a normal shutdown request cannot cut it short.
What a user sees when they try anyway is worth knowing: logind refuses the
request and falls back to the polkit action
_org.freedesktop.login1.power-off-ignore-inhibit_, which is _auth_admin_keep_,
so the desktop presents an administrator password prompt reading "Power off the
system while an application is inhibiting this". That string is shipped
untranslated by systemd and does not mention updates, and no KDE dialog
explains the situation either - only *systemctl*(1) names the inhibitor and its
reason. Hence the notification.
A hard power-off - holding the power button, or losing mains power - is not
preventable. On the next run a leftover _/var/lib/pacman/db.lck_ is removed if
it is older than the current boot, since no process able to hold it can still
exist; the upgrade is then repeated and pacman reinstalls whatever was caught
half-written. A lock file that is unheld but was created during the current
boot is reported rather than removed, because there is no way to prove it is
abandoned.
Without that recovery a single power cut would leave a lock that makes every
subsequent run defer, silently stopping updates for good.
On Btrfs with *snapper*(8) and *snap-pac*, each pacman transaction is bracketed
by a pre and post snapshot, so a broken upgrade remains rollbackable.
# FILES
_/etc/cachy-auto-update/cachy-auto-update.conf_
Configuration. See the comments in the file itself for every option.
_/etc/sudoers.d/cachy-auto-update_
Lets the build account call pacman without a password.
_/var/log/cachy-auto-update/last-run.log_
Full output of the most recent run.
_/var/log/cachy-auto-update/cachy-auto-update.log_
Rolling log across runs.
_/var/lib/cachy-auto-update/_
State: timestamps, counters, queued notifications.
# SEE ALSO
*pacman*(8), *checkupdates*(8), *paru*(8), *yay*(8), *flatpak*(1),
*systemd.timer*(5)
# AUTHOR
Felitendo. Source and bug reports:
https://github.com/Felitendo/cachy-auto-update