Add cachy-auto-update: unattended background updates for CachyOS

A root systemd service applies pacman, AUR, Flatpak and AppImage updates on
its own, gated on battery state, gaming activity and whether anybody else is
using the package system. The CLI is deliberately two switches plus status.

No user password is stored anywhere: pacman runs as root directly, and the AUR
step - which makepkg forbids running as root - drops to a locked system account
that sudoers permits to call pacman without a password.
This commit is contained in:
Felitendo committed 2026-08-08 02:27:04 +02:00
1 parent 327da1dae3
commit ceb024d7be
28 files changed
+3856 -1

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
# shellcheck shell=bash
#
# Reading and writing /etc/cachy-auto-update/cachy-auto-update.conf.
#
# The file is deliberately *parsed* rather than sourced: it is edited by a
# root-run daemon, and sourcing it would turn a stray line into arbitrary code
# execution. The format is one "Key=Value" per line, '#' starts a comment.
# cau_config_get <Key> [default]
cau_config_get() {
local key="$1" default="${2:-}" val
[[ -r $CAU_CONFIG ]] || { printf '%s\n' "$default"; return; }
val="$(sed -nE "s/^[[:space:]]*${key}[[:space:]]*=[[:space:]]*(.*)$/\\1/p" \
"$CAU_CONFIG" 2>/dev/null | tail -n1)"
# strip a trailing comment and surrounding whitespace/quotes
val="${val%%#*}"
val="${val#"${val%%[![:space:]]*}"}"
val="${val%"${val##*[![:space:]]}"}"
val="${val%\"}"
val="${val#\"}"
if [[ -n $val ]]; then
printf '%s\n' "$val"
else
printf '%s\n' "$default"
fi
}
# cau_config_bool <Key> <default: yes|no>
# Succeeds when the key is truthy.
cau_config_bool() {
local val
val="$(cau_config_get "$1" "$2")"
case "${val,,}" in
yes|y|true|1|on|enabled) return 0 ;;
*) return 1 ;;
esac
}
# cau_config_int <Key> <default>
cau_config_int() {
local val
val="$(cau_config_get "$1" "$2")"
[[ $val =~ ^-?[0-9]+$ ]] && printf '%s\n' "$val" || printf '%s\n' "$2"
}
# cau_config_set <Key> <Value>
# Replaces the key in place if present (including a commented-out template
# line), otherwise appends it. Requires write access to the config file.
cau_config_set() {
local key="$1" value="$2" tmp
if [[ ! -e $CAU_CONFIG ]]; then
mkdir -p "$(dirname "$CAU_CONFIG")" || return 1
printf '# %s configuration\n' "$CAU_NAME" > "$CAU_CONFIG" || return 1
fi
[[ -w $CAU_CONFIG ]] || return 1
tmp="$(mktemp "${CAU_CONFIG}.XXXXXX")" || return 1
# keep the original permissions rather than mktemp's 0600
chmod --reference="$CAU_CONFIG" "$tmp" 2>/dev/null || chmod 0644 "$tmp"
if grep -qE "^[[:space:]]*#?[[:space:]]*${key}[[:space:]]*=" "$CAU_CONFIG"; then
awk -v key="$key" -v value="$value" '
!done && $0 ~ "^[[:space:]]*#?[[:space:]]*" key "[[:space:]]*=" {
print key "=" value; done = 1; next
}
# drop any further occurrences so the file cannot grow duplicates
$0 ~ "^[[:space:]]*" key "[[:space:]]*=" { next }
{ print }
' "$CAU_CONFIG" > "$tmp" || { rm -f "$tmp"; return 1; }
else
cat "$CAU_CONFIG" > "$tmp" || { rm -f "$tmp"; return 1; }
printf '%s=%s\n' "$key" "$value" >> "$tmp"
fi
mv -f "$tmp" "$CAU_CONFIG"
}
# ---------------------------------------------------------------------------
# Resolved settings
# ---------------------------------------------------------------------------
# Loaded once per process into plain variables so the rest of the code does not
# re-parse the file for every lookup.
cau_config_load() {
CFG_ENABLED=no; cau_config_bool Enabled no && CFG_ENABLED=yes
CFG_NOTIFICATIONS=no; cau_config_bool Notifications yes && CFG_NOTIFICATIONS=yes
CFG_REQUIRE_AC=no; cau_config_bool RequireAC no && CFG_REQUIRE_AC=yes
CFG_SKIP_GAMING=no; cau_config_bool SkipWhenGaming yes && CFG_SKIP_GAMING=yes
CFG_AUR=no; cau_config_bool UpdateAUR yes && CFG_AUR=yes
CFG_FLATPAK=no; cau_config_bool UpdateFlatpak yes && CFG_FLATPAK=yes
CFG_APPIMAGE=no; cau_config_bool UpdateAppImages yes && CFG_APPIMAGE=yes
CFG_DEVEL=no; cau_config_bool UpdateDevel no && CFG_DEVEL=yes
CFG_RESOLVE_CONFLICTS=no; cau_config_bool AutoResolveConflicts yes && CFG_RESOLVE_CONFLICTS=yes
CFG_CLEAN_CACHE=no; cau_config_bool CleanCache no && CFG_CLEAN_CACHE=yes
CFG_REMOVE_ORPHANS=no; cau_config_bool RemoveOrphans no && CFG_REMOVE_ORPHANS=yes
CFG_NOTIFY_SUCCESS=no; cau_config_bool NotifyOnSuccess yes && CFG_NOTIFY_SUCCESS=yes
CFG_NOTIFY_ERROR=no; cau_config_bool NotifyOnError yes && CFG_NOTIFY_ERROR=yes
CFG_NOTIFY_REBOOT=no; cau_config_bool NotifyReboot yes && CFG_NOTIFY_REBOOT=yes
CFG_INTERVAL="$(cau_config_get UpdateInterval 1d)"
CFG_INTERVAL_SECONDS="$(cau_duration_to_seconds "$CFG_INTERVAL" 86400)"
CFG_MIN_BATTERY="$(cau_config_int MinBatteryPercent 40)"
CFG_KEEP_OLD="$(cau_config_int KeepOldPackages 3)"
CFG_AUR_HELPER="$(cau_config_get AURHelper auto)"
CFG_IGNORE_PKG="$(cau_config_get IgnorePkg '')"
}