Compare commits

...
17 Commits
Author SHA1 Message Date
Daniele Fucini 6b26b358d7 Mention aur build in README 2026-10-14 13:06:48 +02:00
Daniele Fucini 4a9eebcad2 Warn when aur repo -u fails
Print a warning instead of silently counting 0 AUR updates, and stop
discarding aur's stderr so the cause shows up in the journal.
2026-10-14 13:06:40 +02:00
Daniele Fucini f32d2a0c87 Add README 2026-10-13 21:18:08 +02:00
Daniele Fucini 255cb6350f Update PKGBUILD 2026-10-07 19:48:31 +02:00
Daniele Fucini 32b60ef3da Clean up systemd units
Use Type=oneshot for the service and drop the default Restart=no.
Remove Persistent=true from the timer, which only applies to
OnCalendar= timers.
2026-10-07 19:45:47 +02:00
Daniele Fucini 5b877e76df Update PKGBUILD dependencies
Add libnotify (needed for notify-send), move aurutils to optdepends
since the script works without it, add fwupd as optdepend. Quote
variables in package().
2026-10-07 19:45:47 +02:00
Daniele Fucini a72fd1639e Accept zero-padded threshold, simplify fwupdmgr check 2026-10-07 19:45:47 +02:00
Daniele Fucini 9720b4d2a1 Ignore pkgrel in kernel rebuild check
A stock kernel packaging-only bump doesn't require rebuilding the
custom kernel.
2026-10-07 19:45:47 +02:00
Daniele Fucini dbb7a1766d Avoid lost and repeated notifications
Exit without touching the state when checkupdates fails, so a transient
error no longer clears it and causes the same notification on the next
run. Also save the state only after notify-send succeeds, so a failed
notification is retried.
2026-10-07 19:45:38 +02:00
Daniele Fucini e674f0311c Update PKGBUILD 2026-10-04 16:21:10 +02:00
Daniele Fucini e139acd2bd Merge all notifications
Notification for kernel update is included in the same notification as
everything else, no more a separate one
2026-10-04 16:19:16 +02:00
Daniele Fucini 0416d3e615 Fix kernel update check 2026-10-04 16:05:43 +02:00
Daniele Fucini 154fd39b20 Update PKGBUILD 2026-10-04 08:59:09 +02:00
Daniele Fucini ae33917e8a Make fwupdmgr hint conditional
Only show fwupmgr hint if firmware updates are available
Also fold that and git-monitor hint in a single notification
2026-10-04 08:56:47 +02:00
Daniele Fucini eabaa962dc Update PKGBUILD 2026-10-03 21:26:31 +02:00
Daniele Fucini 3f2702eb71 Change timer frequency 2026-10-03 21:24:57 +02:00
Daniele Fucini 9f5c3b1cb4 Update PKGBUILD 2026-10-03 20:07:10 +02:00
5 changed files with 163 additions and 37 deletions
+10 -8
View File
@@ -3,24 +3,26 @@
# Maintainer: Daniele Fucini <dfucini [at] gmail [dot] com> # Maintainer: Daniele Fucini <dfucini [at] gmail [dot] com>
pkgname=update-notify pkgname=update-notify
pkgver=3.0 pkgver=3.3
pkgrel=1 pkgrel=1
pkgdesc='Notify when updates are available' pkgdesc='Notify when updates are available'
arch=('any') arch=('any')
url="https://git.shouldnt.work/fuxino/${pkgname}" url="https://git.shouldnt.work/fuxino/${pkgname}"
license=('GPL-3.0-or-later') license=('GPL-3.0-or-later')
depends=('pacman-contrib' depends=('pacman-contrib'
'aurutils') 'libnotify')
makedepends=('git') makedepends=('git')
optdepends=('git-monitor: to check git repos for updates') optdepends=('aurutils: to check AUR packages in the local repo for updates'
'fwupd: to check for firmware updates'
'git-monitor: to check git repos for updates')
source=(git+${url}?signed#tag=${pkgver}) source=(git+${url}?signed#tag=${pkgver})
validpgpkeys=('7E12BC1FF3B6EDB2CD8053EB981A2B2A3BBF5514') validpgpkeys=('7E12BC1FF3B6EDB2CD8053EB981A2B2A3BBF5514')
sha256sums=('47f8d1aac3ef6cdb19b4f72c5b3d5cf45433810fbd166038525336c1e3cf83fc') sha256sums=('413435ce9215cf6e37f9a11e69aba7d6338489547a552a6977f4f283da9402c2')
package() package()
{ {
cd ${srcdir}/${pkgname} cd "${srcdir}/${pkgname}"
install -Dm755 ${pkgname} ${pkgdir}/usr/bin/${pkgname} install -Dm755 "${pkgname}" "${pkgdir}/usr/bin/${pkgname}"
install -Dm644 ${pkgname}.service ${pkgdir}/usr/lib/systemd/user/${pkgname}.service install -Dm644 "${pkgname}.service" "${pkgdir}/usr/lib/systemd/user/${pkgname}.service"
install -Dm644 ${pkgname}.timer ${pkgdir}/usr/lib/systemd/user/${pkgname}.timer install -Dm644 "${pkgname}.timer" "${pkgdir}/usr/lib/systemd/user/${pkgname}.timer"
} }
+80
View File
@@ -0,0 +1,80 @@
# update-notify
Desktop notifications for pending updates on Arch Linux.
update-notify runs on a systemd user timer and checks:
- **Repo updates**, using `checkupdates` from pacman-contrib.
- **AUR updates** for packages in a local aurutils repository (`aur repo -u`).
- **Custom kernel rebuilds**: when a stock kernel (e.g. `linux-lts`) has a newer version than your custom build of it (e.g. `linux-lts-custom`).
It sends one notification, and only when its content changes since the last one:
- Update counts are shown once the total number of updates reaches a threshold (default 30). Reminders for `git-monitor` and pending firmware updates (`fwupdmgr`) are added when those tools are installed.
- Kernel rebuild reminders are always shown, even below the threshold.
## Requirements
- `pacman-contrib`
- `libnotify` and a running notification daemon
Optional:
- `aurutils`: check AUR packages in the local repository
- `fwupd`: check for firmware updates
- `git-monitor`: remind to check git packages
## Installation
Build and install the package with the included PKGBUILD:
```
makepkg -si
```
Or, with aurutils, build it into your local repository with `aur build` and install it with pacman.
Then enable the timer:
```
systemctl --user enable --now update-notify.timer
```
The timer runs 5 minutes after boot, then every 6 hours.
## Usage
```
update-notify [threshold]
```
`threshold` is the minimum number of updates (repo + AUR) before update counts are shown. The default is 30.
To change the threshold used by the timer, override the service:
```
systemctl --user edit update-notify.service
```
```ini
[Service]
ExecStart=
ExecStart=/usr/bin/update-notify 50
```
## Configuration
These settings are at the top of the script:
- `AUR_REPO`: name of the local aurutils repository (default `aur-local`).
- `KERNELS`: maps each stock kernel package to its custom build. Kernels where either package isn't installed are skipped.
Changes to the installed script are overwritten on upgrade, so change them in the source and rebuild the package.
Kernel versions are compared without `pkgrel`, so a stock packaging-only update doesn't trigger a rebuild reminder. A custom build already in the local repository but not installed yet counts as up to date.
The content of the last notification is stored in `$XDG_STATE_HOME/update-notify` (default `~/.local/state/update-notify`). Delete it to get the current notification again.
## License
GPL-3.0-or-later. See [LICENSE](LICENSE).
+69 -23
View File
@@ -15,6 +15,7 @@ declare -A KERNELS=(
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/update-notify" state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/update-notify"
[[ $threshold =~ ^[0-9]+$ ]] || { echo "Threshold must be a number" >&2; exit 1; } [[ $threshold =~ ^[0-9]+$ ]] || { echo "Threshold must be a number" >&2; exit 1; }
threshold=$((10#$threshold)) # force base 10, so e.g. "08" isn't read as octal
mkdir -p "$state_dir" mkdir -p "$state_dir"
# notify-send from cron/systemd needs the session bus address # notify-send from cron/systemd needs the session bus address
@@ -31,8 +32,7 @@ notify_once() {
if [[ -f $file && $(<"$file") == "$body" ]]; then if [[ -f $file && $(<"$file") == "$body" ]]; then
return 0 return 0
fi fi
printf '%s' "$body" > "$file" notify-send "$title" "$body" && printf '%s' "$body" > "$file"
notify-send "$title" "$body"
} }
# Forget a key so the next occurrence notifies again. # Forget a key so the next occurrence notifies again.
@@ -54,6 +54,19 @@ pending_version() {
return 1 return 1
} }
# Newest known version of $1: the pending version if an update is queued,
# otherwise the installed version. Returns 1 if the package isn't installed.
latest_version() {
local pkg=$1 ver
if ver=$(pending_version "$pkg"); then
printf '%s\n' "$ver"
elif ver=$(pacman -Q "$pkg" 2>/dev/null); then
printf '%s\n' "${ver#* }"
else
return 1
fi
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Gather updates # Gather updates
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -62,7 +75,9 @@ pending_version() {
checkupdates_out=$(checkupdates 2>/dev/null) checkupdates_out=$(checkupdates 2>/dev/null)
checkupdates_rc=$? checkupdates_rc=$?
if (( checkupdates_rc == 1 )); then if (( checkupdates_rc == 1 )); then
# Bail out without touching the state, so the next good run doesn't re-notify
echo "checkupdates failed (network or sync problem?)" >&2 echo "checkupdates failed (network or sync problem?)" >&2
exit 1
fi fi
arch_list=() arch_list=()
@@ -73,45 +88,76 @@ arch_updates=${#arch_list[@]}
aur_updates=0 aur_updates=0
if command -v aur >/dev/null 2>&1; then if command -v aur >/dev/null 2>&1; then
aur_updates=$(aur repo -u -d "$AUR_REPO" 2>/dev/null | wc -l) if aur_out=$(aur repo -u -d "$AUR_REPO"); then
[[ -n $aur_out ]] && aur_updates=$(wc -l <<< "$aur_out")
else
echo "aur repo -u failed, AUR updates not counted" >&2
fi
fi fi
updates=$((arch_updates + aur_updates)) updates=$((arch_updates + aur_updates))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Kernel check: the stock kernel updated, but your custom build didn't # Kernel check: the stock kernel updated, but your custom build didn't
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
kernel_lines=()
for stock_pkg in "${!KERNELS[@]}"; do for stock_pkg in "${!KERNELS[@]}"; do
custom_pkg=${KERNELS[$stock_pkg]} custom_pkg=${KERNELS[$stock_pkg]}
# Only relevant if the custom kernel is installed and has no pending update itself # Skip kernels where either package isn't installed
if pacman -Q "$custom_pkg" >/dev/null 2>&1 && ! pending_version "$custom_pkg" >/dev/null; then if ! stock_ver=$(latest_version "$stock_pkg") || ! custom_ver=$(latest_version "$custom_pkg"); then
if stock_ver=$(pending_version "$stock_pkg"); then
notify_once "kernel-$stock_pkg" "Kernel update available: $stock_pkg" \
"Rebuild $custom_pkg ($stock_pkg $stock_ver)"
continue continue
fi fi
# Flag the kernel while the stock one is newer than the newest custom build
# (installed, or already built into your repo but not yet installed).
# pkgrel is ignored: a stock packaging-only bump doesn't need a rebuild.
if (( $(vercmp "${stock_ver%-*}" "${custom_ver%-*}") > 0 )); then
kernel_lines+=("Rebuild $custom_pkg ($stock_pkg $stock_ver)")
fi fi
clear_state "kernel-$stock_pkg"
done done
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# General updates threshold # Build a single notification
# - kernel rebuild lines are always included
# - update counts and reminders only when the threshold is reached
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
if (( updates >= threshold )); then over_threshold=0
notify_once "updates" "Updates available" "Arch: $arch_updates, AUR: $aur_updates" (( updates >= threshold )) && over_threshold=1
hints=( parts=()
"git-monitor|Check git packages|Use git-monitor for git packages updates" if (( over_threshold )); then
"fwupdmgr|Check firmware|Use fwupdmgr to check for firmware updates" parts+=("Arch: $arch_updates, AUR: $aur_updates")
)
for hint in "${hints[@]}"; do
IFS='|' read -r cmd title body <<< "$hint"
if command -v "$cmd" >/dev/null 2>&1; then
notify_once "hint-$cmd" "$title" "$body"
fi fi
done parts+=("${kernel_lines[@]}")
if (( over_threshold )); then
reminders=()
# Always remind about git packages if git-monitor is installed
if command -v git-monitor >/dev/null 2>&1; then
reminders+=("git-monitor")
fi
# Firmware: only mention it when updates actually exist.
# fwupdmgr get-updates exits 0 when updates are available, 2 when there are none.
# --no-metadata-check avoids prompting/refreshing; the fwupd-refresh timer keeps metadata fresh.
if command -v fwupdmgr >/dev/null 2>&1; then
if timeout 30 fwupdmgr get-updates --no-metadata-check </dev/null >/dev/null 2>&1; then
reminders+=("fwupdmgr (firmware updates available)")
fi
fi
if (( ${#reminders[@]} )); then
printf -v list '%s, ' "${reminders[@]}"
parts+=("Also check: ${list%, }")
fi
fi
if (( ${#parts[@]} )); then
title="Updates available"
(( over_threshold )) || title="Kernel update available"
printf -v body '%s\n' "${parts[@]}"
notify_once "updates" "$title" "${body%$'\n'}"
else else
clear_state "updates" clear_state "updates"
clear_state "hint-git-monitor"
clear_state "hint-fwupdmgr"
fi fi
+1 -2
View File
@@ -3,5 +3,4 @@ Description=Notify if updates are available
[Service] [Service]
ExecStart=/usr/bin/update-notify 30 ExecStart=/usr/bin/update-notify 30
Type=simple Type=oneshot
Restart=no
+1 -2
View File
@@ -3,8 +3,7 @@ Description=Run update-notify every 6 hours
[Timer] [Timer]
OnBootSec=5min OnBootSec=5min
OnUnitActiveSec=12h OnUnitActiveSec=6h
Persistent=true
[Install] [Install]
WantedBy=timers.target WantedBy=timers.target